tags:

views:

355

answers:

3

Say I have a git repository and I've been working on master, can I retroactively create a branch. For example:

A - B - C - A1 - D - A2 - E

I want to make it look like this:

A - A1 - A2   
\           \   
B - C - D - E

The specific use case is when I've cherry-picked a bunch of commits into an old version branch and it needs to go into multiple older versions and I don't want to repeat the cherry-pick on all those revision.

Essentially it's something that would have been good as a feature or topic branch in the first place but wasn't created like that.

+4  A: 

You can't do that transparently because the hashes will have to change, but you basically just need to branch HEAD and rebase -i both branches to drop the respective changes.

Dustin
+9  A: 

Of course you can. (With Git there isn’t much than you can’t do anyway. :)

git checkout -b new-branch hash-of-A
git cherry-pick hash-of-A1
git cherry-pick hash-of-A2

This will create a new branch, starting from the commit A. Afterwards you go back to the same commit again, creating another branch:

git checkout -b new-branch2 hash-of-A
git cherry-pick hash-of-B
git cherry-pick hash-of-C
git cherry-pick hash-of-D
git cherry-pick hash-of-E
git merge new-branch

Now you simply have to merge new-branch and new-branch2 to get the structure you want and drop your old branch.

Of course what Dustin said still holds: the hashes of the commits will change so you should only do that if you haven’t published your changes yet.

Bombe
So if the branch I was on was master, I should move the name master to the head of new-branch2?Makes sense.Thanks.
Otto
A: 

What you want to do is to actually rewrite history. The identifiers of commits would change, and in some cases the changeset given by commits would change. So if there is a chance that somebody could have based work on old version of the branch you want to change, better to not do this. But if you didn't publish this branch, feel free.

Let us assume that a branch we want to change is named 'master', and that the point where we want to start new branch is named 'A' (in given example one of names you can use is 'master~6').

First, lets create new branch from commit 'A', let's name it 'fixes'

$ git checkout -b fixes A

This would also make branch 'fixes' current. Because there are only a few commits that we want to un-cherry-pick, we can cherry pick them on branch 'fixes':

$ git cherry-pick A1
$ git cherry-pick A2

Then we want to remove commits 'A1' and 'A2' from branch 'master'. Because there are only a few commits we want to remove, and possibly many more we want to keep, we eould use 'git rebase --interactive' for that:

$ git rebase -i fixes master

An editor will be fired up with all the commits in 'master' after commit 'A' (which is common commit i.e. merge base of branch 'master' and branch 'fixes'). The list would look like this:

pick deadbee B
pick fa1afe1 C
pick a98d4ba A1
...

Remove lines with commits 'A1' and 'A2', save changes, close editor (or otherwise send changes to inetractive rebase) and git would reapply all commits except those that you have deleted.

Then you can finalize with

$ git merge fixes

(git-rebase left us on rewritten branch 'master').

Jakub Narębski