Search code examples
gitgit-diff

Find files that changed specifically in a branch


I have a git repository in which I usually work on two branches, a public branch, and a private branch, the latter following closely the public branch, but with a few extra additions.

Is there a way to find the changes since a given commit that are specific to the private branch ?

The closest I can think of is to do something on the lines of:

git diff base..public > pub.diff
git diff base..private > private.diff
interdiff pub.diff private.diff

This works, but it's not that robust, and I'm sure it's possible to do that with git in a better way.

This is essentially how the branches look (with many more merges):

...A---B---C---D---E---F---G---H public
    \           \           \
     A'--I---J---D'--K---L---G'--M private

For instance, let's assume that base above is D in this scheme. Just running

git diff D'..M

will also give me the results of the merge commit G'. I'd like to avoid that, and have something robust across any numbers of merges.


Solution

  • Because git diff

    is about comparing two endpoints, not ranges

    This might not be possible (git diff will not detect were those changes came from).

    You could try using


    Option A

    $ git log -p --no-merges --first-parent D'..M
    
    • -p gives you a diff (patch) for the commits listed
    • --no-merges will exclude merges from the list
    • --first-parent will only follow the first parent (the one you merged into e.g. private)
    • D'..M as explained in git revisision will

      Include commits that are reachable from <rev2> but exclude those that are reachable from <rev1>


    Option B

    A different approach would be to follow @torek's suggestion and create a temporary branch, cherry-pick all the commits onto it and then look at the diffs from this branch.

    Because AFAIK git cherry-pick is not able to skip commits when specifying commit ranges you would first need to get a list of commits for cherry-picking (Here Option A is useful)

    $ git log --no-merges --first-parent D'..M --pretty=format:%H
    
    • --pretty=format:%H will print all the commit hashes in long form

    Create the temporal branch (which needs to have the correct starting point)

    $ git checkout -b temp D'
    

    And then cherry-pick those commits manually or try using this command (exchanged for a better command suggested by @torek)

    $ git cherry-pick $(git rev-list --reverse --topo-order --no-merges --first-parent D'..M)
    
    • --reverse is used to reverse the order of commits printed (oldest to newest instead of the other way around)
    • --topo-order this will avoid problems with commits that have weird or wrong timestamps
    • --no-merges skip merge commits
    • --first-parent follow the mainline branch (e.g. the branch the merges were merged into)

    This approach might fail if commits are based on changes which were merged in