mergediff
Author: wolever
Repository: https://bitbucket.org/wolever/hg-mergediff/
(work in progress - see status)
Show only the changes introduced by a merge.
Use case: you are reviewing code, and you would like to see only the changes that were introduced by a merge, but *not* the changes leading up to the merge.
This extension does not currently work with Mercurial >=1.9
Example
$ hg mdiff --- a/changed_in_merge # changed to "in a" in branch 'a' --- b/changed_in_merge # changed to "in b" in branch 'b' +++ c/changed_in_merge # changed to "merged" in the merge - in b -in a ++merged --- a/resolved # same as above, except changed to "in a" +++ c/resolved # to resolve the merge conflict - in b + in a --- /dev/null --- /dev/null +++ c/added_in_merge # file added in the merge ++added in merge
Algorithm
Imagine a DAG that looks like this (arrows point from child to parent):
Note that:
AP1, AP2, P1M and P2M are sets of diff hunks
Hunks in AP1 and AP2 should //not// be included in the merge diff
P1M contains AP2 and P2M contains AP1 (ie, AP2 ⊆ P1M and AP1 ⊆ P2M)
Therefore, the merge diff should contain exactly: AP2-P1M ∪ AP1-P2M (where "-" is the "set difference" operation).
Status
The mergediff algorithm has been implemented and appears to function correctly.
The implementation is currently suboptimal (it uses more time and memory than is strictly necessary)… But this should only be a problem for "really big" merges.
Caveats
The algorithm which compares hunks (ie, to perform AP2-P1M) only considers the source file, destination file and context diff. Notably, it does //not// consider the diff's location in the destination file.
I've done this to help cope with situations where, for example, changes are made above a hunk in one branch, which causes their offset to be different in the "parent to merge" diff than it is in the "base to parent" diff.
Here's an example, from the testing repository:
$ hg diff -r a:merge diff --git a/top_bottom b/top_bottom --- a/top_bottom +++ b/top_bottom @@ -10,3 +10,4 @@ <<< note: line 10 unchanged line 7 unchanged line 8 unchanged line 9 +bottom change $ hg diff -r 0:b diff --git a/top_bottom b/top_bottom --- a/top_bottom +++ b/top_bottom @@ -9,3 +9,4 @@ <<< note: line 9 unchanged line 7 unchanged line 8 unchanged line 9 +bottom change
This could potentially lead to changes being incorrectly ignored (eg, if a change which should not be ignored has the same context diff as a change which should be ignored)… But I'm not sure what the best way to fix it will be.
Code
The code is available at: http://bitbucket.org/wolever/hg-mergediff/
I've tried to roughly emulate git's combined diff format. There are, however, some notable differences:
Format notes
- mdiff may include three files in the header, instead of only two
- mdiff does not include any extended headers
- mdiff only includes the target file's range (this is only because I'm lazy - it should technically be possible to implement)
FAQ
Q: What's wrong with hg diff -r <merge>?
A: In short, diff -r <merge> will show changes from one of the branches. For example, taking the diff of a clean merge will show changes, but showing the mergediff of a clean merge will not.
Q: Isn't this the same as git's combined diff?
A: No. Among other things, git's combined diff doesn't show new changes (ie, changes that weren't conflict resolutions) and it only shows resolutions to one of the merge parents. For more examples, compare the output of hg mergediff -r merge to the output of git diff merge^ in the repository created by mktestrepo.