Paul Hammant's Blog: Subversion Merge Limitations That Are Not In Mercurial
A couple of days ago I published an article on how Subversion merging still has upper limits, that some users may encounter. I just ported the code to Git and the bug wasn’t there. Today I’ve done so to Mercurial, and like Git it is good to go. Mercurial was annoying to relearn for this article though!
Here’s Subversion blowing up in a merge scenario after a series of cherry-picks, when it shouldn’t have done, from the first blog entry:
Attempt redundant merge of branch two into branch one (individual commits are merged already, and files at HEAD revision are idential): --- Merging r2 through r13 into 'one': C one/testfile.txt --- Recording mergeinfo for merge of r2 through r13 into 'one': U one Summary of conflicts: Text conflicts: 1 Conflict discovered in file 'one/testfile.txt'. Select: (p) postpone, (df) show diff, (e) edit file, (m) merge, (mc) my side of conflict, (tc) their side of conflict, (s) show all options:
So here are the same bash scripts, but issuing Mercurial commands rather than subversion ones (link to Github repo/branch).
And the diff between the two might prove interesting to some (as github presents that).
The same two tests again…
Test one - merging one way - all good
Merging changes out of order from branch ‘one’ to ‘two’ worked fine. Script:
$ ./test_of_out_order_merges_from_one_branch_to_another.sh marked working directory as branch one (branches are permanent and global, did you want a bookmark?) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved marked working directory as branch two grafting 1:0c041affee88 "line a changed" grafting 5:101db5a33b1e "line e changed" merging testfile.txt grafting 3:c4eeb2870d70 "line c changed" merging testfile.txt grafting 4:2072d6afc347 "line d changed" merging testfile.txt grafting 2:72b1a5431d8a "line b changed" merging testfile.txt 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
Like Subversion and Git, after a series of seemingly complete (if out of order) cherry-picks, Mercurial wants to do one more ghost merge that doesn’t have any details in the
hg log -pr (there is nothing as short as
git show in hg).
Test two - merging back - all good too
For subversion, this one had a bug in it - or rather highlighted a bug in Subversion. Mercurial Script:
This was merging another series of changes, as cherry-picks, back to ‘one’ from branch ‘two’. As Git, it worked without barfing, unlike the Subversion one (see previous blog entry). Mercurial, like Git, will also do a “one more” (ghost) merge from ‘two’ to ‘one’ even if all of the cherry picks have been done already. Again with no diffs in the “hg log -pr” for the last commit. Like Git, Mercurial doesn’t use visible annotations/properties for merge point tracking, so the bash script is slightly shorter versus the Subversion one. The result:
$ ./test_of_out_order_merges_back_the_original_branch.sh 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved grafting 12:e17af586001a "line a changed again" merging testfile.txt grafting 16:66ea4c730fec "line e changed again" merging testfile.txt grafting 14:4faf857d1781 "line c changed again" merging testfile.txt grafting 15:07d36e24e678 "line d changed again" merging testfile.txt grafting 13:223c9ff6caa7 "line b changed again" merging testfile.txt Contents of testfile.txt on branch 'one': a !! aa b !! bbb c !! cccc d !! ddddd e !! eeeeee 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Contents of testfile.txt on branch 'two' (they are the same, as a result of merges): a !! aa b !! bbb c !! cccc d !! ddddd e !! eeeeee 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Attempt redundant merge of branch two into branch one (individual commits are merged already, and files at HEAD revision are idential): 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
The ghost commits is the one that follow the complete (if out of order) series of four cherry-picks. At least in my example. It is a ghost as it is a commit without a diff. Ghost commits are a mechanical notation for “nothing more to merge to here from that other place”. I would much prefer to be able to go cherry-picks like so:
git cherry-pick --with-full-update-merge-tracking-from-implicit-branch SHA.
This way, the fourth of four (in my example) cherry pick, could also update the meta-info that the following ghost commit would have done. No need for that fifth (ghost) commit - yay!
It needs to be an optional command flag, rather than default. That is because it isn’t always going to be instantaneous looking for unmerged changes in the implicit ‘from’ branch.
Cherry pick merges are grafts or transplants. Transplant has to be enabled as Mercurial does not do it by default - that makes no sense. What’s the difference anyway?
Cherry picks can’t have csutome messages in the ‘-m’ style. Someone raised a feature request for
-graftmessage. It was declined for not particularly strong reasons. I wouldn’t mind if there was a
--nocommitequivalent, but there is not. Mercurial’s graft operation is inconsistent with its merge operation - does
--nocommitby default and you can follow it with a commit.
Creating a branch and switching to it is too clunky, with petty rules. In the Subverson and Git scripts I was able to make the “two” branch earlier in the sequence - not so for this Mercurial port of the bash script.