DevOps Zone is brought to you in partnership with:

Paul is a principal consultant at ThoughtWorks. He is enthusiastic about open source in particular. He is known for Dependency Injection (one of its pioneers with PicoContainer), Selenium browser automation (co-founder), Branch by Abstraction and most recently Client-Side MVC frameworks. Paul is a DZone MVB and is not an employee of DZone and has posted 73 posts at DZone. You can read more from them at their website. View Full User Profile

Git's Lightweight Merging

01.09.2014
| 5693 views |
  • submit to reddit

One of Git’s selling points, some years ago, was that it has lightweight branching compared tools that came before it. As it happens Subversion was also launched with claimed lightweight branching compared to CVS. It’s just that Git’s branches take that to another level.

Anyway, it’s Git’s merges that are the truly lightweight thing in my opinion. This article tries to show how that is.

Merging back from a task branch with Git

Let’s make a single source file on master, make a branch for a task we’ve been assigned, change the file there, also change it on the master, then merge the task branch back to the master and delete the task branch:

#!/bin/sh
mkdir foo
cd foo
git init
echo "** Create trunk version of file"
echo "Efficiently unleash cross-media information without\nimmediate value. Dramatically\nmaintain clicks-and-mortar solutions without\nfunctional aspects." > file.txt
git add file.txt
git commit -m "initial version on master"
sleep 2 
echo "** Create Temp Branch .."
git branch task001 
git checkout task001 
echo "** A change on task001"
perl -p -i -e "s/immediate/any/g" file.txt
git commit -am "A change on the task001 branch"
sleep 2 
git checkout master 
echo "** Modify master version of file"
perl -p -i -e "s/cross-media/socially-awkward/g" file.txt
git commit -am "Different change on master made after the change on branch"
sleep 2 
echo "** Merge temp task001 into master"
git merge -s ours task001 -m "merge task001 branch back into master"
git branch -D task001

After the event, the history looks like this:

Each of those commits in Git could have a separate code review. That is apart from the last one – the auto-merge that was merge task001 branch back into master and it has no diff associated with it. We have nothing review at the moment of merge in our test case. That’s the key take-away – there’s often no ceremony with the merge, and it’s truly lightweight.

The task branch made task001 is long gone (and was only a ‘local’ branch anyway), but the change made on it is retained. It doesn’t matter whether we keep the task branch or not in this case it does’t affect the merge history of the file. This is cool as it means your code review (if you’re reviewing commit by commit) is very efficient.

Incidentally, the multiple sleep 2 statements in the script above allow us to compare the actual order of commits via timestamps, with the visualization of the history (GitX in this case). 19:58:53 followed by 58 then 56 and the merge at 57. Interesting (but not wrong in this representation).

Trying the same thing with Subversion

Now we can’t play with local branches for a pure-subversion setup, so both trunk and the task branch task001 exist on the server. Here’s the script that’s a Subversion version of the Git script above:

# $1 is the name of the repo served locally.
#!/bin/sh
svn --username harry --password harry checkout svn://127.0.0.1/$1
echo "** Create trunk version of file"
echo "Efficiently unleash cross-media information without\nimmediate value. Dramatically\nmaintain clicks-and-mortar solutions without\nfunctional aspects." > $1/trunk/file.txt
svn add $1/trunk/file.txt
svn commit $1 -m "initial version on trunk"
sleep 2 
echo "** Create Temp Branch .."
svn copy svn://127.0.0.1/$1/trunk svn://127.0.0.1/$1/branches/task001 -m "Create Branch"
svn up $1
echo "** A change on task001"
perl -p -i -e "s/immediate/any/g" $1/branches/task001/file.txt
svn commit $1 -m "A change on the task001 branch"
sleep 2 
echo "** Modify trunk version of file"
perl -p -i -e "s/cross-media/socially-awkward/g" $1/trunk/file.txt
svn commit $1 -m "Different change on trunk made after the change on branch"
svn up $1
sleep 2 
echo "** Merge temp task001 into trunk"
svn merge svn://127.0.0.1/$1/branches/task001 $1/trunk
svn ci $1 -m "merge task001 branch back into trunk"

The crucial commit is the merge back from task001 to trunk. The last commit – #7. Here’s what that looks like in Crucible:

But here’s the change that happened on the branch (#5):

They look the same. If we’d done a code review for #5 (the change on the branch), and some time later a code review for #7 (the merge back to trunk) we’d be duplicating work previously done :-(

It’s fair to say that the merge back to trunk for the Subversion and Perforce from a task branch, isn’t lightweight. Perforce is exactly the same. Mercurial and PlasticSCM are as lightweight as Git.




Published at DZone with permission of Paul Hammant, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)