Robin Green Robin Green - 28 days ago 15
Git Question

Compare old and new versions of force-pushed pull request

Frequently, my colleagues will make some changes to an open pull request, rebase their local branch against the base branch - often squashing their changes into previous commits as well - and force-push.

How can I see what changed between the old version of the PR and the new version of the PR?

I guess I could do a

git pull
and
git checkout $BRANCH_NAME
when the PR was first raised, then
git fetch
and then
git diff $BRANCH_NAME..origin/$BRANCH_NAME
after the PR was updated - but that will also show changes that have been introduced into the base branch (typically master) and brought into the PR via a rebase. Is it possible to remove that noise and just show what has changed in the PR itself?

Answer

Checkout this answer to another question which want to do something very similar to what you are trying to achieve.

It describe your situation like this:

newcommit -> the new pull request commit
oldcommit -> the old pull request commit
upstream -> the base branch commit the new pull request is based on

Now do this:

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message"
git show <commit id returned by previous command>

The idea is that commit-tree will fake a merge between oldcommit and upstream producing newcommit tree and thus containing exactly the code of newcommit. It does not modify your current branch at all, it create a new headless commit and give you its ID. This means git show will list every modification as a conflict resolution, which is the exact difference between the new PR and the old one.

To be able to do that you need to have the previous PR in your git repository somewhere (if a force push has been performed the git history has been rewritten and can't be recovered unless you have it on your pc or you have access to the server reflog). Check VonC answer for details about this.

Assuming:

  • base branch: master
  • you have locally the old PR branch: $BRANCH_NAME
  • the new PR in a remote branch: origin/$BRANCH_NAME

You can do like this:

# fetch locally upstream changes (origin/$BRANCH_NAME)
git fetch
# produce the fake merge commit
git commit-tree origin/$BRANCH_NAME^{tree} \
         -p $BRANCH_NAME \
         -p `git merge-base master origin/$BRANCH_NAME` \
         -m "message"
# see "fake" conflict resolution = difference between the two PR
git show <commit id returned by previous command>

the git merge-base is used to find the common ancestor between two branches, in this case to find the commit on which the new PR is based on in the base branch, if you prefer you can write the commit ID directly.