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
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.