How do I get the interdiff between these two git commits?
Asked Answered
L

5

12

I'm trying to view the difference between two revisions of the same commit in git. A diff of the diffs, basically. From what I've read so far, this is known as the "interdiff". I've read several tutorials on how to create interdiffs of git patches, but I haven't been able to get these methods to work in my particular case.

So here's the setup. I have two diverging branches, each with slightly different commits:

* 29e734f - (origin/feature_branch, new_commits) New commit 3 (69 minutes ago) <Ajedi32>
* b22ebea - New commit 2 (89 minutes ago) <Ajedi32>
* 09d42c2 - New commit 1 (2 hours ago) <Ajedi32>
| * 467e08f - (old_commits) Old commit 3 (4 weeks ago) <Ajedi32>
| * f2bf1cb - Old commit 2 (4 weeks ago) <Ajedi32>
| * 34a2187 - Old commit 1 (4 weeks ago) <Ajedi32>
|/  
*   1b05a4a - (origin/base, base) Base commit (5 weeks ago) <Ajedi32>

In this example, I want to find the interdiff between "Old commit 3" and "New commit 3". I've tried creating a patch file from these two commits and running them through the interdiff utility, but all I got was this:

1 out of 2 hunks FAILED -- saving rejects to file /tmp/interdiff-1.Kgwx8u.rej
interdiff: Error applying patch1 to reconstructed file

I'm not really sure what that means, so I'm kind of stuck now. Where do I go from here?

Note: I'm am not looking for git diff old_commits new_commits here. I don't want the revisions to commits 1 and 2 included in the output.

Litt answered 22/7, 2013 at 16:30 Comment(6)
Oh, and depending on what the OP was trying to ask this might be related to this question.Litt
Out of curiosity, what would the diff of the diffs serve you?Absolution
@Absolution You mean what is it useful for? I'm using it to make it easier for the people reviewing my code to see what I changed in response to a previous review. This page is one of the tutorials I followed. It has a pretty good explanation near the top of the page of why interdiffs are useful.Litt
@Absolution Or were you asking what would happen if I ran the content of the diffs through the diff command? In that case, I'm not sure but I doubt it would be pretty.Litt
how is this different from diff "old_commit_3".."new_commit_3"?Tindal
@Tindal From the original question: "Note: I'm am not looking for git diff old_commits new_commits here. I don't want the revisions to commits 1 and 2 included in the output." So if there were some minor differences between "Old commit 1" and "New commit 1", I wouldn't want those changes to be included. I'm diffing the patches, not the state of the repo.Litt
L
12

Git 2.19 introduces a new command, git range-diff which does this:

git-range-diff - Compare two commit ranges (e.g. two versions of a branch)

git range-diff [--color=[<when>]] [--no-color] [<diff-options>] [--no-dual-color]
               [--creation-factor=<factor>]\
               ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )

Description

This command shows the differences between two versions of a patch series, or more generally, two commit ranges (ignoring merge commits).

To that end, it first finds pairs of commits from both commit ranges that correspond with each other. Two commits are said to correspond when the diff between their patches (i.e. the author information, the commit message and the commit diff) is reasonably small compared to the patches' size. See Algorithm below for details.

Finally, the list of matching commits is shown in the order of the second commit range, with unmatched commits being inserted just after all of their ancestors have been shown.

So, in your case:

git range-diff base old_commits new_commits

Would automatically match up the commits made in the old_commits branch with the commits in the new_commits branch, and display a summary of the differences between each commit.

Or, if you just want the changes from the last commit in each of those branches, you'd run:

git range-diff old_commits~..old_commits new_commits~..new_commits

Since Git 2.31 you can use a more concise form for single commits:

git range-diff old_commits^! new_commits^!

For more on range-diff, see the official documentation.

Litt answered 11/9, 2018 at 14:49 Comment(0)
L
5

Perhaps something like this:

git log -p -1 new_commits > patch.new
git log -p -1 old_commits > patch.old
diff patch.old patch.new

Or for a terse one-liner (in bash):

diff <(git log -p -1 old_commits) <(git log -p -1 new_commits)
Lauritz answered 22/7, 2013 at 17:41 Comment(5)
The bash syntax used for the one-liner is a process substitutionWanhsien
Is threre a reson to use git log instead of git show, i.e. something like diff <(git show old_commits) <(git show new_commits)?Norry
@Norry Good point - it seems that the output is identical, at least on the version of git I'm currently using... That may or may not always be the case, though...Lauritz
@talberg, I am pretty sure that this true since at least 10 years and at least for the next two decades as well ;) From man git show: "For commits it shows the log message and textual diff."Norry
that one-liner seems posix-compliant actuallyAmersham
H
1

If there are few differences between the two commits, comparing lines removed and lines added (i.e. only those starting with + or -) is effective. It gets rid of the noise introduced by the hunks boundaries starting with @@ or the commit message body that are not significant:

diff -u --ignore-matching-lines '^[^+-]' \
    <(git show 1d9e4ac) <(git show 7b8e5c9)

a sample outout is:

--- /dev/fd/63  2015-02-13 13:27:08.612683558 +0100
+++ /dev/fd/62  2015-02-13 13:27:08.616683527 +0100
@@ -62,13 +57,24 @@
  }

 diff --git a/src/crush/CrushWrapper.h b/src/crush/CrushWrapper.h
-index 0113662..282cbeb 100644
+index 3b2e6e6..0a633a5 100644
 --- a/src/crush/CrushWrapper.h
 +++ b/src/crush/CrushWrapper.h
-@@ -874,6 +874,25 @@ public:
-     return false;
+@@ -863,6 +863,36 @@ public:
+     if (!crush) return -1;
+     return crush_find_rule(crush, ruleset, type, size);
    }
- 
++
++  bool ruleset_exists(int const ruleset) const {
++    for (size_t i = 0; i < crush->max_rules; ++i) {
++     if (crush->rules[i]->mask.ruleset == ruleset) {
++       return true;
++     }
++    }
++
++    return false;
++  }
++
 +  /**
 +   * Return the lowest numbered ruleset of type `type`
 +   *
Hemihydrate answered 13/2, 2015 at 12:40 Comment(0)
P
1

The GNU interdiff on your 2 patches fails because they have no direct common base / parent.

While a git range-diff produces an outer diff of diffs (which is often hard to read; unaware / inconsistent regarding already consumed / merged changes in previous commits; not an applicable patch format), you can get an effective well readable interdiff by merge-rebasing a copy of the one commit onto the parent of the other (preferably newer) commit. There are several ways.

Trivial would be to compare <New commit 1> with <Old commit 1> in your example because they already have an immediate common parent:

diff NC1 OC1

Further away from the common base, according to your actual question, we get it for example like this:

# move to the parent of NC3 - entering detached HEAD state
git checkout NC3~1
# merge-rebase the single patch <Old-commit-3> onto here
git cherry-pick OC3
# show the diff or reverse diff (or diff in gitk GUI)
git diff -R NC3
# back to feature_branch
git checkout feature_branch

Of course merge conflicts may appear here - which then (necessarily) expose changes that are not compatible to a sort of interdiff with 'virtual common base' due to the lack of a real direct common parent.

A (temporary) tag or branch could be created to conserve this rebased comparison commit (or range of commits) - e.g. for discussion and work with other users. Otherwise the temporary unreferenced dangling commit(s) would be auto-recycled soon.

Primo answered 27/10, 2020 at 0:25 Comment(0)
B
0

Maybe this would give you something like what you want. But it will fail if commits 1-2-3 are very dependent.

$ git checkout 'old commit 2'
$ git cherry-pick -n 'new commit 3'
$ git diff 'old commit 3'

$ git checkout 'new commit 2'
$ git cherry-pick -n 'old commit 3'
$ git diff 'new commit 3'
Bibliopole answered 22/7, 2013 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.