Categories
git

Avoiding Merge Commits in Git

[Update, 18/8/2008] If you’ve shared the branch with anyone else, or are pushing it to a clone of the repository, do not rebase, but use merge instead. From the man page:

When you rebase a branch, you are changing its history in a way that will cause problems for anyone who already has a copy of the branch in their repository and tries to pull updates from you. You should understand the implications of using git rebase on a repository that you share.


Do you get annoyed by seeing things like this in your git history?

commit a0b46a7c57e37f5dc43373ba9167ad2da32c1ec5
Merge: c2d8046... 73e0e15...
Author: Fred Bloggs 
Date:   Tue Jun 17 17:30:49 2008 +0100

    Merge branch 'master' into new_feature

commit c2d8046c038d47940944e5b343d281b1d0c4d2b3
Author: Fred Bloggs 
Date:   Tue Jun 17 17:30:43 2008 +0100

    Added cool new feature

This happens when you use merge instead of rebase to keep a development branch up-to-date with master. Let’s watch what happens in each case.

The wrong way (merge)

You’re working on a cool new feature in your new_feature branch. You’ve committed two changes, and in the meantime there have been three other changes on master. Here’s the branch visualisation from gitk --all:

Before merge

You use merge to pull in those other three changes to your branch:

$ git merge master
Merge made by recursive.
 file_1 |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

Now what this has actually done is added the changes from master as a new commit in new_feature. You can see this in the history:

myproj(new_feature) $ git log -1
commit cbc97a909641d3c325c6023a2459e556e62182e6
Merge: 024dc64... 0a71c4c...
Author: Kerry Buckley 
Date:   Wed Jun 18 18:52:48 2008 +0100

    Merge branch 'master' into new_feature

And also in the gitk graph:

After merge to branch

Now let’s say your new feature is all finished, so you merge it into master:

myproj(new_feature) $ git checkout master
Switched to branch "master"
myproj(master) $ git merge new_feature 
Updating 0a71c4c..cbc97a9
Fast forward
 file_3 |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

Let’s see what that’s done to the history:

myproj(master) $ git log
commit cbc97a909641d3c325c6023a2459e556e62182e6
Merge: 024dc64... 0a71c4c...
Author: Kerry Buckley 
Date:   Wed Jun 18 18:52:48 2008 +0100

    Merge branch 'master' into new_feature

commit 0a71c4c90aee5eeb60d15f199c4f8151756a8ae8
Author: Kerry Buckley 
Date:   Wed Jun 18 18:48:19 2008 +0100

    Third change in master

commit 9970c0b72e7741804fc07bba50450b3d512e5572
Author: Kerry Buckley 
Date:   Wed Jun 18 18:48:10 2008 +0100

    Second change in master

commit c44af4dee449082adf6741540d2f9e70968cf41e
Author: Kerry Buckley 
Date:   Wed Jun 18 18:46:33 2008 +0100

    First change in master

commit 024dc64022932a5a7b56c4fd7c7cf4a59d72e825
Author: Kerry Buckley 
Date:   Wed Jun 18 18:45:15 2008 +0100

    New feature finished

commit eb4f05fb8ef8b93cf639b1e06528ef075f19f323
Author: Kerry Buckley 
Date:   Wed Jun 18 18:45:01 2008 +0100

    New feature partly done

commit b4ffa1d35f808cc38e5f74fb2592224dd6f0e027
Author: Kerry Buckley 
Date:   Wed Jun 18 18:44:02 2008 +0100

    Last commit before creating branch

Or graphically:

After merge to master

The problem here is that the merge has applied all of the commits which were on new_feature but not on master, including the merge commit. That’s just ugly.

The right way (rebase)

Now, from exactly the same point, we’ll use rebase instead:

myproj(new_feature) $ git rebase master
First, rewinding head to replay your work on top of it...
HEAD is now at a644c41 Third change in master
Applying New feature partly done
Applying New feature finished

Basically, as it says, this has rewound all your changes since the new_feature branch diverged from master, moved the branch point up to the tip of master, then replayed your changes on top. The effect of this is as if you had created the branch from the current latest master, so no separate merge commit is required. Again, gitk --all can confirm this visually:

After rebase

Now merge the changes into master exactly as before:

myproj(new_feature) $ git checkout master
Switched to branch "master"
myproj(master) $ git merge new_feature 
Updating a644c41..d7d2233
Fast forward
 file_3 |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

No merge commit in the history this time, and a nice simple graph:

After rebase and merge

So there you have it. No excuse for polluting your history with merges any more!

[tags]git, merge, rebase, branch[/tags]

Categories
Enterprise Rails Ruby

Defending Ruby and Rails in the Enterprise

I consider myself fortunate that the previous two projects I worked on (the BT Web21C Portal and Mojo) were Rails-based (actually it wan’t just luck in the former case, as I had a part in selecting the framework). I love the expressiveness and flexibility of the Ruby language, the power and relative simplicity of the Rails framework, and the all-round awesomeness of tools like RSpec and Capistrano, and I don’t particularly relish the thought of going back to Java (although I’m told that Spring is much nicer than last time I used it).

At our recent release planning session, I was assigned to a new project, which involves (among other things) exposing a CLI-based configuration interface as a web service. In our initial discussions, the four of us more-or-less agreed on a few initial decisions:

  • The exposure should be REST. Fortunately the people developing the upstream system shared this opinion.
  • Rails is an ideal framework for RESTful web services.
  • Ruby seemed like a good fit for parsing the command responses too.
  • Asynchronous behaviour would be handled using queues (probably ActiveMQ).

Since we’d all been working on Rails projects when the new team was formed, we assumed that this wouldn’t be a particularly contentious route to go down, but unfortunately our director/architect/boss didn’t see things quite the same way. He had two main objections:

  • Rails may make sense for GUI applications, but why on earth would you use it for a service? All our other [SOAP] services are written in Java.
  • At some point the application will need to go into support, and we don’t have support/operations people with Ruby or Rails experience

I think the first point’s easier to address, as I’d argue it’s based on a misunderstanding: Rails isn’t really anything to do with GUIs, but is a framework for creating MVC web applications. Virtually all the heavy lifting Rails takes care of is in the controller and model areas, with the creation of the actual visible GUI being left to the developer to take care of with the usual mix of HTML, CSS and Javascript. The only thing Rails adds is the ability to insert dynamic content using ERB – similar to the role of JSP in Java EE.

A RESTful web service is, to all intents and purposes, the same as a normal web application, but (potentially) without the HTML. All the power that Rails brings to web application development is also harnessed when creating RESTful services.

The second point represents a much more fundamental strategy choice. If the company makes the decision that all development is going to use Java (the language as well as the platform), then we inevitably lose the flexibility to choose what may appear (in a local context) to be the right tool for the job. Personally I think that would be a shortsighted and ill-informed decision: if that were the strategy, we’d presumably all still be developing in C, or COBOL, or Assembler. Or we’d have gone bust. But then I’m not an architect (incidentally, according to Peter Gillard-Moss, that’s reason number 10 why I don’t deserve to be fired), so what do I know?

However, if Ruby is considered an acceptable technology choice for “normal” web applications, we’ll still need people with appropriate skills to support those, so the problem doesn’t go away. I suspect even for a Java specialist, supporting a well-written Rails application with good test coverage is probably easier than supporting some of the spaghetti-coded Java I’ve seen.

Anyway, our arguments obviously weren’t totally unconvincing, because we were given a couple of weeks to show what we could produce before getting a final decision. That time runs out on Monday, so if I’m unnaturally grumpy after that it’ll be because we’ve been told to chuck all our work so far away and start from scratch in Java. Or possibly FORTRAN.

Update, 16 June Well we made our case, and we get to stick with Rails. Celebration all round!