22

I have two commits, once of which is the ancestor of another. (They happen to be the start and end points of a branch. I don't think that matters, but I'll include it if it does).

I want to see the diff between the two commits, but excluding changes made during merge commits (that is, all commits with more than one parent) that were made between the two commits. (Basically, I want any "real" commit that was made to the branch, excluding the merges.)

Is this possible? If so, how do you accomplish this?

If necessary, assume that there are no conflicts resolved during the merge commits... but bonus points for a solution that can handle them elegantly.

Craig Walker
  • 49,871
  • 54
  • 152
  • 212
  • Please clarify: do you want an individual diff for each commit that is not a merge commit, or do you expect something else? – Jeff Ferland Dec 28 '10 at 20:30
  • @Autocracy: I've edited it and hopefully clarified what I'm looking for. Let me know if it still doesn't make sense. – Craig Walker Dec 28 '10 at 21:41

5 Answers5

19

Your question is slightly ambiguous but I think you want this.

git log --no-merges -p branch-start..branch-end
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
5

If your merges all came from the same branch (say master) or are all contained in another branch, you can use the solution from this question.

Assuming you have a tree like the following:

         x---y-+-z-+-branch
        /     /   /
---a---b---c-+-d-+-e---master

and the two commits you would like to compare are b and branch. Then instead of comparing the two commits directly, running

git diff master...branch

will show all changes done on the branch (x,y,z) excluding everything that is also on master (c,d,e, the merges). Note that this also ignores any changes done on master that are not yet in the branch.

From the docs:

git diff [--options] commit...commit [--] […​]

This form is to view the changes on the branch containing and up to the second commit, starting at a common ancestor of both commits. "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B".

Community
  • 1
  • 1
jmiserez
  • 2,991
  • 1
  • 23
  • 34
3

I didn't know the --no-merges -o options but here another solution (I suppose that merges have been done from master) :

git checkout -b temp
git rebase --onto master branch-start branch-end
git diff master
mgautierfr
  • 729
  • 3
  • 8
  • Upvoting this solution as it seems to be less prone to shoot your feet. While `--first-parent` helps to exclude all commits pulled from a target branch into a feature branch when there are some merges in between and `--no-merges` allows to exclude the merge commits themselves, these flags are applied to `git log`, not `git diff`, so we have to review each commit step-by-step. Moreover, what is more crucial to me is that it can be dangerous to exclude merge commits from reviewing as they can contain important updates as well. This solution allows to see the exact diff without losing anything. – Andrei K. Feb 23 '23 at 15:21
2

Here's how I compare my checked out branch to master, excluding changes made during merge commits. I prefer using Meld's directory mode.

# if you've never setup Meld as your difftool
sudo apt install meld
git config --global diff.tool meld

# the command to see the differences
git difftool -d --no-merges master
1

I wanted a way to show a single diff view (all non-merge changes across two commits in one diff), rather than the patch-per-commit view from git log. I came up with a solution based on the accepted answer to this question.

It's wrapped as a shell function in order to use the start/end commit arguments multiple times:

function review() {
  start_commit="$1"
  end_commit="$2"
  git log --pretty=format: --no-merges --first-parent --name-only $start_commit...$end_commit | grep -v '^$' | sort | uniq | xargs git difftool -d $start_commit...$end_commit --
}

This essentially uses git log to get a list of changed files between the two commits excluding merges, dedupes and cleans up the list, then feeds that list to a git diff between the same two commits.

This solution has an undesirable side effect of including changes in the diff from upstream that also happened to be in the set of files only changed between the two commits in the branch, but this "single diff" view is still useful to me in many situations.

It also turns out that GitHub supports this if your branch has an associated pull request (it will automatically exclude merges from upstream in its diff view):

https://github.com/[user]/[repo]/pull/[number]/files/[commit1]..[commit2]

segfault
  • 572
  • 1
  • 7
  • 20