Comparing differences across a rebase in Git

Kevin Reid picture Kevin Reid · Sep 17, 2012 · Viewed 13.2k times · Source

Suppose that I just rebased the branch foo on master, with conflicts. I want to make sure that I did not accidentally damage the content of foo during conflict resolution by introducing extra changes or losing changes (other than that which is appropriate for the conflict resolution). I have done this via:

diff -u <(git diff `git merge-base master foo@{1}` foo@{1}) \
        <(git diff `git merge-base master foo    ` foo    )

(update: or the equivalent ... syntax for git-diff which I have just been reminded of:)

diff -u <(git diff master...foo@{1}) <(git diff master...foo) | mate

This shows me all the changes that have occurred to master..foo considered as a patch, which is exactly what I want to check for being minimal. However, the invocation is complex and the output is not entirely straightforward to interpret.

Is there a better way to accomplish this task — to provide the same information, but with a better method or format — or should I just take the above and wrap it up in a script?

Answer

Jake picture Jake · Oct 21, 2016

What we really want to show is the conflict combined diff that is generated as if we had done a merge to get from (a) to (b) where (a) was previously based on (upstream-old) and (b) is now based on (upstream-new).

We don't want just a straight diff.

Instead, we can essentially do the merge but force the resulting tree to be $b^{tree} which we already know is the correct "end" point of what we want.

More or less, let's assume that we have

newcommit -> the new version of the series
oldcommit -> the old version of the series
upstream -> the (new) version of the base of the series

We can generate the merge via

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message"

and then show the result with "git show" and this will generate a combined diff format that shows all the necessary bits as a conflict resolution which automatically ignores changes that weren't actually part of resolving conflicts.

This even works for simply ammending a change as well, and you can do a bit more work to ensure that the generated merge commit has exact author and commit timestamps so that it's consistent over multiple calls (since we are storing a loose ref into the object database).

Unfortunately I have not been able to figure out how to just get "git diff" to diff the same way without actually generating a tree yet. I'm not sure what arguments we'd have to pass to get to that point.