Search code examples
gitmergefast-forward

Merge first n commits of branch to master


I have a branch from the most recent commit to master. the branch has two commits on it. I'd like to fast-forward merge the first of those commits onto master.

I know I could do a cherry pick, but I don't really want to diverge the master and branch if I can help it.

Current state:

Branch:       +--->B--->C
             /
Master: --->A

Cherry-pick option:

Branch:       +--->B--->C
             /
Master: --->A--->B'

Ideal option:

Branch:            +--->C
                  /
Master: --->A--->B

Is this possible?

Note that the commits are all pushed to the remote already; but no-one else currently has access to this particular repo so a push -f would be OK.

I seem to remember there might be some way to merge onto master while a branch is checked out... if that is true, I could simply check out commit B, then merge in that manner?


Solution

  • More specifically, you might want:

    git merge --ff-only <commit-specifier>
    

    which tells Git to do a fast-forward not-really-a-merge of the current branch, whatever that is, to the specified commit. Such an operation:

    1. tests whether HEAD is an ancestor of <commit-specifier>: if not, fail, if so, continue on to...
    2. advance the current branch so that it points to <commit-specifier> and read that commit into the index and work-tree (git read-tree, the "two tree merge" case—note that this too can fail, if you have uncommitted changes, or untracked files that are in the target commit).

    Now, finding a specific hash that's N commits "ahead of" the current commit in the direction of some target commit can be kind of tricky. Consider, for instance:

    ...--o--o   <-- master
             \
              \      C--D
               \    /    \
                A--B      G--H   <-- branch
                    \    /
                     E--F
    

    The tip commit of branch is eight commits ahead of the tip commit of master—or is it? I think everyone agrees that A is one commit ahead of master, and B is two ahead. But what about C and E, and D and F? C is three ahead, but so is E; and D and F are both four ahead. But G is not five ahead of master: it seems to be seven ahead of master. Likewise, H aka branch seems to be eight ahead of master.

    So if I told you to move three commits ahead, would you pick commit C, or E? Would you give me an error, or a choice? Why?

    If you constrain things to a linear chain, we can enumerate the commits "between" master and branch, excluding master and including branch, with:

    git rev-list --reverse master..branch
    

    and then the N-th commit in the list is the commit hash to give to git merge --ff-only. But if there are branch-and-merge chains "between" these two points, the whole question becomes tricky.