Search code examples
gitgit-rebase

git | move old commit to the past of another branch


I incorrectly branched in the past and one commit was left in the start of another branch:

* 03431cb (HEAD -> bar) a2
| * d332e4d (foo) b2
| * 9b29ae3 b1
| * 4656a98 a1
|/  
* 6ebca20 (master) root

How can I move a1 out of foo into bar, so that bar's history is root -> a1 -> a2 and a1 is not in foo? Is it possible to do it with one single git commit?
This is not pushed so no need to worry about breaking others' local repos.

I first thought about doing a cherry pick of a1 and then correcting the order between a2 and a1. The issue with this is that those two commits conflicts in my real case scenario and I would have to correct the conflict both when doing the cherry pick and when switching their order.


mwe in bash:

#!/bin/bash
set -e

rm -rf .git

git init -b master
echo content > my-file
git add my-file
git commit -m root

git checkout -B foo
echo asd >> my-file
git add my-file
git commit -m a1
echo qwe >> my-file
git add my-file
git commit -m b1
echo zxc >> my-file
git add my-file
git commit -m b2

git checkout master
git checkout -B bar
echo jkl >> my-file
git add my-file
git commit -m a2

Solution

  • I'm a big fan of the syntax git rebase --onto x y z, which means:

    Starting at z, look back thru the parent chain until you are about to come to y and stop. Now rebase those commits, ie everything after y up to and including z, onto x.

    In other words, using this syntax, you get to say clearly where to snip the chain. Plus you don't have to switch branches before rebasing. The syntax takes some getting used to, but once you get fluent at it, you'll find yourself using it all the time.

    So:

    1. Create a temporary branch at a1 just to give it a name: git branch temp 4656a98
    2. Now rebase b1 and b2 onto root: git rebase --onto master temp foo
    3. Finally rebase a2 onto a1: git rebase --onto temp master bar
    4. Now you can delete temp if you like: git branch -D temp

    Sure, we could save two steps and just do 2 and 3 using SHA number 4656a98 instead of the name temp, but names are nicer.


    Proof.

    Starting position:

    * 9a97622 (HEAD -> bar) a2
    | * 83638ec (foo) b2
    | * 7e7cbd0 b1
    | * 931632a a1
    |/  
    * 6976e30 (master) root
    

    Now:

    % git branch temp 931632a
    % git rebase --onto master temp foo
    % git rebase --onto temp master bar
    % git branch -D temp
    

    Result:

    * 3a87b61 (HEAD -> bar) a2
    * 931632a a1
    | * bbb83d0 (foo) b2
    | * 5fa70af b1
    |/  
    * 6976e30 (master) root
    

    I believe that's what you said you wanted.