0

Given a git situation like this:

A---B---X---C'---Y (main)
     \
      C---D---E (my-branch)

I want

A---B---X---C'---Y---D'---E' (my-branch)

Note that main has C' which is the same commit as C, but rebased onto X (possibly with some fixups)

99% of the time I can just type git rebase main from my-branch and it works great, telling me it noticed that C has already been applied and that it skipped the commit.

But sometimes it has conflicts reapplying C so instead of fixing them I proceed with:

git rebase --abort
git checkout main
git branch -D my-branch
git branch my-branch
git cherry-pick C..E
git push -u origin my-branch --force-with-lease

And it works with fewer conflicts. However, it's 5 commands instead of 1, requires deleting a branch, requires hunting down git SHA's and requires a force push. It has a significant potential for screwing up my branch.

Is there a better way to do this that is less error prone?

Bryan Larsen
  • 9,468
  • 8
  • 56
  • 46
  • 2
    `git rebase` is just automating a series of cherry-picks under the hood. Both courses of action should give similar results. Could you double-check your tests and give an example of a conflict happening in only one case? – Romain Valeri Apr 11 '23 at 13:37
  • Is the `C..E` intended, or did you mean `B..E` or `main..E`? The difference is that `C..E` would not cherry-pick commit `C`. If it is intentional, then it could explain your observation, but it would also mean that your cherry-pick procedure produces an incorrect result. – j6t Apr 11 '23 at 13:47
  • C..E is deliberate. main already has C', and somebody else has already fixed up some conflicts I can piggy back on. – Bryan Larsen Apr 11 '23 at 13:56
  • I edited the question to make this clear. Thanks @j6t. – Bryan Larsen Apr 11 '23 at 14:17
  • 5
    Use the `-i` option to rebase and delete C from the list. Or if you want it all on the command line `git rebase --onto main C` – Raymond Chen Apr 11 '23 at 14:29
  • I tried rebase --onto a previous time I had the problem and it didn't work, probably PBKAC. But "-i" is obvious in hindsight. Turn it into an answer I'll upvote/accept it. – Bryan Larsen Apr 11 '23 at 14:47

2 Answers2

1

Being on my-branch you can do it like that:

git reset --hard main
git cherry-pick D^..E
Orace
  • 7,822
  • 30
  • 45
  • I verified this answer, it works. Would be accepted but I prefer the comment from @Raymond Chen. Too bad I can't accept more than one. – Bryan Larsen Apr 11 '23 at 14:48
0

Is there a better way to do this that is less error prone?

The most straight forward way to do this is to use the three argument --onto version of rebase which will do exactly what you ask for with one command:

# == Before ==
# A---B---X---C'---Y (main)
#      \
#       C---D---E (my-branch)

git rebase --onto main C my-branch

# == After ==
# A---B---X---C'---Y (main)---D'---E' (my-branch)

This will rebase my-branch from but not including commit C and put on top of main.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • If you run `git rebase main` (which you should not do, you should [exclusively use the explicit 2 (or 3) argument form](https://stackoverflow.com/a/75113010/23118) e.g. `git rebase main my-branch`), that is basically the same as `git rebase --onto main B my-branch`. – hlovdal Apr 11 '23 at 21:18