Search code examples
gitgit-rebasegit-cherry-pick

Rebase/copy all missing commits from one branch on top of current branch


ok, this looks so simple but I can't find an easy solution. Let's say there are two branches:

a - b - c - d - e - f <current_branch>
         \
          g - h - i <temp_branch>

I simply want all missing commits in <temp_branch> to be on top of <current_branch> without touching <temp_branch> and without merge commit:

a - b - c - d - e - f - g' - h' - i' <current_branch>
         \
          g - h - i <temp_branch>

What didn't work:

  • git rebase : I could first rebase <temp_branch> on top of <current_branch> and merge back fast-forward. But that's not what I want and it is too complicated.
  • git cherry-pick : if there is no automatic way, manually looking up the commit-range is required

A simple one-liner would be great, like

git rebase --reverse-order temp_branch HEAD

Or some other command I don't know of?


Solution

  • The easy way:

    $ git status  # just to make sure you're on the current branch :-)
    

    Now that you know for sure you're on current_branch:

    $ git cherry-pick HEAD..temp_branch
    

    which relies on cherry-pick's internal sequencing to do them in the right order, but won't skip patch-equivalent commits.

    The slightly harder way that skips patch-identical commits:

    $ git cherry-pick $(git rev-list --reverse --topo-order \
        --right-only --cherry-pick HEAD...temp_branch)
    

    or the same with --left-only and temp_branch...HEAD (these should be precisely the same).

    (I have not tested either one, but git rebase --interactive uses:

    git rev-list $merges_option --format="%m%H ${format:-%s}" \
        --reverse --left-right --topo-order \
        $revisions ${restrict_revision+^$restrict_revision} | \
        sed -n "s/^>//p" | ...
    

    where $merges_option is --no-merges --cherry-pick, $revisions is $upstream...$orig_head where those are the obvious, and $restrict_revision is via --fork-point if specified). The sed command deletes the right-side commits. You may want an explicit --no-merges as well, though this is not needed for the illustrated case.)