How can I move old commits in a branch to a new branches?

37 views Asked by At

I have a branch called staging that contains old commits that I would like to move to two separate branches.

A - B - C - D - E - F - G - H - I   staging

I would like to move commits B and C to a new branch: feature-a. Then move commits D, E, and F to a new branch: feature-b.

In the end my branches would look like this:

A - G - H - I   staging
A - B - C       feature-a
A - D - E - F   feature-b

The staging branch is public and other developers may have local versions of it. How can I accomplish this?

2

There are 2 answers

1
larsks On BEST ANSWER

NB: There is probably a better way, but here's one way to do it.

Creating feature-a is easy:

git branch feature-a C

We can create feature-b by creating a new branch based on A and then using cherry-pick:

git checkout -b feature-b A
git cherry-pick feature-a..F

That r1..r2 expression is documented in the git-rev-parse man page:

The .. (two-dot) Range Notation

The ^r1 r2 set operation appears so often that there is a shorthand for it. When you have two commits r1 and r2 (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable from r1 by ^r1 r2 and it can be written as r1..r2.

Lastly, we can use git rebase -i --root on the staging branch to remove commits B-F:

git checkout staging
git rebase -i --root

In my test repository, this results in:

$ git log --oneline staging
15326ce (HEAD -> staging) This is commit I
3862020 This is commit H
a26f0bb This is commit G
9bc662c This is commit A

$ git log --oneline feature-a
b390f07 (feature-a) This is commit C
1489cd1 This is commit B
9bc662c This is commit A

$ git log --oneline feature-b
f8f3c18 (feature-b) This is commit F
0eb0b08 This is commit E
4e2ed05 This is commit D
9bc662c This is commit A
0
eftshift0 On

@larsks answer is fine (upvoted), but it can also be done without any interactive work from rebase or cherry-picking. Starting from the original setup

git branch feature-a C # branch needs no more work

Right now you have:

* I (staging)
* H
* G
* F
* E
* D
* C (feature-a)
* B
* A

Then:

git branch feature-b F

Now you have:

* I (staging)
* H
* G
* F (feature-b)
* E
* D
* C (feature-a)
* B
* A

Then:

git rebase --onto A feature-a feature-b # place on top of commit A all commits in feature-b that are _past_ feature-a (D, E and F)

Now you have:

* I (staging)
* H
* G
* F
* E
* D
* C (feature-a)
* B
| * F' (feature-b)
| * E'
| * D'
|/
* A
git rebase --onto A F staging # place on top of A all commits that make up staging past F (G, H, I)

And finally you have:

* C (feature-a)
* B
| * F' (feature-b)
| * E'
| * D'
|/
| * I' (staging)
| * H'
| * G'
|/
* A