1

GitHub pull requests are all about merging two branches together:

  • The "remote" branch, containing new changes you want to merge.
  • The "local" branch you want to merge into, which GitHub calls the "base" branch.

Is there a way to create a branch that shows the same merge conflicts as a given cherry-pick, and that, when merged, is equivalent to performing that cherry-pick? In this instance we know the specific branch the cherry-pick will be applied to, if that makes any difference?

For example, is it sufficient to create a branch whose HEAD is the commit I want to cherry-pick, and open a PR for that branch?

Sam Salisbury
  • 1,066
  • 11
  • 19

1 Answers1

1

A cherry-pick tries indeed to merge two patches together, but you will not be able to create a merge request on your work branch that does the same action as the cherry-pick.


Detailed view of a cherry-pick :

Here is my go at illustrating the merge operation performed by git cherry-pick :

Suppose you run git cherry-pick eacf32 from branch my/work :

# 'p' marks the parent commit of eacf32 :
---*-----*-----*------*-----* <- my/work
          \                   
           \               
            \-*---*----*----*-----*-----* <- some/other/branch
                       ^    ^
                       p   eacf32

# 'git cherry-pick eacf32' will try to resolve this merge :

                         my/work
                          v
---*-----* . . * . .* . . *----* <- would-be-cherry-pick
          \              /    /
           \            /    /
            \-*---*----*----* . . * . . *
                       ^    ^
                       p   eacf32

Turning it into an equivalent merge :

Following this diagram :

  • you would want a branch cherrypick/target, with the actual content of my/work, grouped as one commit on top of p -- the parent commit of eacf32 :

    # start from your branch (you will have the expected content) :
    git checkout my/work
    # create a new branch and switch to it :
    git checkout -b cherrypick/target
    # use 'reset --soft' to move to the parent of eacf32
    # ('--soft' will keep all the differences as staged changes) :
    git reset --soft eacf32^
    # commit that :
    git commit
    
  • you would want a branch cherrypick/source at eacf32 :

    git branch cherrypick/source eacf32
    
  • you would merge cherrypick/source in cherrypick/target


What to do with the result of this merge ?

The possible merge conflicts and the content of the end result will be the same as the cherry-pick, you would obviously have to pick the content of that commit on top of your actual my/work branch.

As you can see from the diagram : the merge base for the cherry-pick must be p, so you won't be able to create a PR against your initial my/work branch, which gives the same result as the cherry-pick.

LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • That's great, thanks for explaining @LeGEC. Because I have to reset the target branch, does that mean I couldn't fast-forward merge the result back into the original target branch? Or is there a way to use the conflict resolution I apply to cherrypick/target to update the original target branch as expected? – Sam Salisbury May 26 '20 at 13:38
  • From the resulting commit : re-apply the `reset --soft / commit` dance on top of `my/work` – LeGEC May 26 '20 at 13:50
  • Hm, I was unable to make that work, but maybe I'm misunderstanding. I have managed to get the desired effect using `git diff-tree --no-commit-id --name-only -r merged^..merged` to get a list of files modified by the manual merge, and then to copy the contents of those files across to the original target branch, and make a commit using the original commit message. This seems to have the same overall effect as a cherry-pick. However, this all seems over the top, I wonder if I would be better off just directly trying to apply the patch using the `--3way` flag... – Sam Salisbury May 26 '20 at 16:00
  • I can't mark your answer as correct yet, because I don't think it allows me to open a PR in GitHub that looks just like a proposed cherry-pick, conflicts and all, and merges back to the original target branch without rewriting history. I'm still not sure if that is even possible... – Sam Salisbury May 26 '20 at 16:03
  • Ah, this is not possible. As you can see in the diagram : one of the diff (the diff between `p` and `my/work`) cannot be represented by a merge of existing branches. – LeGEC May 26 '20 at 16:16
  • added some more verbosity to the answer ... hope it is clear enough – LeGEC May 26 '20 at 16:25