Elazar Leibovich
Elazar Leibovich

Reputation: 33593

reviewing diff caused by a set of commits

I want to view the diff of only two commits.

For instance

$ git log --oneline
0ff4567 fix bug #1, now really really really
1ff4567 fix bug #1 really
2ff4567 fix bug #2
3ff4567 fix bug #3
4234567 refactor code
5ff4567 fix bug #1
6234567 fix bug #4

I want to view only the commits relevant to bug #1, that is, commits 0ff4567,1ff4567,5ff4567.

I don't care about the diffs of the rest of the commits.

Is there an easy way to do that?

Update: I know the list of relevant commits. Given this list, I want to get a single diff, which is easier to review.

Upvotes: 3

Views: 170

Answers (2)

Mark Longair
Mark Longair

Reputation: 467181

You can use the --grep option to git log, which only outputs commits with log messages that match a particular regular expression. In your case you could do:

git log --oneline --grep='bug #1'

... if you want to see the patches introduced by each of those commits, of course, you should do:

git log -p --grep='bug #1'

In the comments below, you explain that you really want one patch as output, which represents the cumulative effect of the patches introduce by those three commits. In that case, you could try one of the following:

  • Using the combinediff tool from patchutils to combine the diffs. (This may not work, depending on what the intermediate commits have changed.)
  • Create a temporary new branch, and use interactive rebase (possibly with a cunningly constructed command in the GIT_EDITOR environment variable) to reorder and squash the commits.

To expand a bit on the latter option, this script is based on a ("super-kludgy") example by Jefromi:

#!/bin/sh

set -e

if [ $# -ne 2 ]
then
    echo "Usage: $0 REGEX TEMPORARY_BRANCH_NAME"
    exit 1
fi

REGEX="$1"
BRANCH_NAME="$2"

git checkout -b "$BRANCH_NAME"

FIRST_COMMIT=$(git log --grep="$REGEX" --pretty=format:%H | tail -1)
if [ -z "$FIRST_COMMIT" ]
then
    echo "No commits matched '$REGEX'"
    exit 2
fi

export GIT_EDITOR="f() { if [ \"\$(basename \$1)\" = \"git-rebase-todo\" ]; then sed -i -n '/${REGEX}/p' \$1 && sed -i '2,\$s/pick/squash/' \$1; else vim $1; fi }; f"
git rebase -i ${FIRST_COMMIT}^

... which you might invoke as:

squash-matching-commits 'bug #1' tmp-branch

... which will then create the branch tmp-branch, rebase back to the parent of the first commit that matches bug #1, only picking commits that match bug #1 and squash all but the first one. (You may have to fix some conflicts, and provide a commit message for the squashed commits.) If that succeeds, then you can just do:

git show

... to see the combined patch. I don't seriously recommend anyone use this script, but it's a fun and hacky way of doing what you want, I think :)

Upvotes: 2

VonC
VonC

Reputation: 1324178

You could use git log --grep=fix1 (as show in Git reference) in order to isolate the relevant commit, and then perform a git show <commit> for each commit.

See "Shorthand for diff of git commit with its parent?".

Combining those patches as a single diff isn't trivial, as Jefromi explains in "git diff with author filter".

The problem here is that you can't do this in the general case.
Suppose Alice changes a particular file, then Bob changes it - including parts that Alice changed - and finally Alice changes it again.
How do you combine Alice's two diffs into a single diff?
If you take them as two patches, the second simply won't apply without Bob's patch being applied first!
But you also can't simply diff the final state against the original, because that will include Bob's changes.

In your case, a possible (quite cumbersome) solution should be similar to "Extract relevant changes for code review":

Check out a working copy at a revision just before the first changes. Then merge all related commits into your working copy.
Now you have a working copy which differs from its base just by the relevant changes. You can review this directly, or create a patch from it for review.

So: a dedicated fix1_review branch is possible, but that remains a semi-automated setup (as you have to solve possible conflicts).

Upvotes: 1

Related Questions