2

I have two branches, B1 and B2, in the following setup

          [B1]
-o-o-d1-d2-d3 
      \
       c1-c2-c3  
            [B2]

I would like to switch commit c1 to B1 in order to achieve the following setup

-o-o-d1-c1-d2-d3
         \
          c2-c3

I know that I can cherry-pick c1 onto the first branch. But my history is cleaner if I had the changes of c1 only on branch B1. (I admit, the problem can be seen rather as an aesthetic one... but I hope that git has a nice solution for it.)

Arne
  • 23
  • 2
  • See here: https://stackoverflow.com/questions/32315156/how-to-inject-a-commit-between-some-two-arbitrary-commits-in-the-past – Tim Biegeleisen Nov 20 '19 at 10:39
  • Does this answer your question? [Git: How to rebase to a specific commit?](https://stackoverflow.com/questions/7744049/git-how-to-rebase-to-a-specific-commit) – Canh Nov 20 '19 at 10:54
  • @TimBiegeleisen: Thank you. So in step 2 I would still write `pick` but with the hash of c2? – Arne Nov 20 '19 at 10:57
  • 1
    Note that the connection going from commit `d2` back to commit `d1` is actually *part of* `d2` itself. No part of any existing commit can ever be changed, so to achieve your desired result, you must copy existing `d2` to a new commit (with a different hash ID) whose parent is `c1`. The existing `c1` is OK to be re-used, but `d2` must be copied. We can call the new-and-improved copy `d2'`. Once `d2` is copied, you must swap out `d3` for a new-and-improved copy as well, so that `d3'` refers to `d2'`. This finishes all the copying, after which the branch name label `B1` needs adjustment. – torek Nov 20 '19 at 17:38
  • 1
    That whole process—copy some set of commits to new and improved versions, then adjust the branch label to point to the final copied commit—is exactly what `git rebase` does. You can split it up manually into a series of `git cherry-pick` commands, each of which copies one commit, followed by `git branch -f`, which is what is in [RomainValeri's answer](https://stackoverflow.com/a/58952723/1256452), or use `git rebase` as in [Phlippe's answer](https://stackoverflow.com/a/58953751/1256452). – torek Nov 20 '19 at 17:38

2 Answers2

2

You just have to do:

git checkout B1
git rebase c1

The rebase will re-apply onto c1 all the commits that from the last common ancestor between the commit c1 and the branch B1 i.e. all the commits in B1 from d1 (the last common ancestor).

In your case, d2 and d3.

Philippe
  • 28,207
  • 6
  • 54
  • 78
1

Short answer

git checkout c1
git cherry-pick d2 d3
git branch -f B1 [HEAD]

Step by step process (with a temp branch for clarity)

Initial situation

-o-o-d1-d2-d3 <<< B1
      \
       c1-c2-c3 <<< B2

First point a temp branch to c1

git checkout -b B3 c1

Phase 2

-o-o-d1-d2-d3 <<< B1
      \
       c1 <<< B3
         \
          c2-c3 <<< B2

Then you recover d2 and d3

git cherry-pick d2 d3

Phase 3

-o-o-d1-d2-d3 <<< B1
      \
       c1-d2'-d3' <<< B3
         \
          c2-c3 <<< B2

and you'll finally have to move B1 to point to our new construct (B3)

git checkout -B B1 B3
# and destroy the temp branch
git branch -d B3

(Of course this means a rewitten history of B1.)

Romain Valeri
  • 19,645
  • 3
  • 36
  • 61