Reputation: 2119
So normally when we have feature1, feature2, and feature3, they branch off of devel. I accidentally just continued branching rather than switching back to devel though so feature2 branches off of feature1 and feature3 branches off of feature 2.
How can I fix this?
EDIT for pic:
I would like this:
---------------
\
A - B - C - D - E
to become this:
---------------
\ \ \
A B-C D-E
Upvotes: 1
Views: 375
Reputation: 488453
For completeness, here's how to use git rebase
(with optional --onto
) to move these commits. First, let's update the "current" drawing to add some specific labels and commits. For ease of rebasing and yet generality, I'll assume that feature1
connects behind the tip of devel
, and that you'd like to move the new branches to the tip (the commit marked with *
).
If there are no extra commits (so that *
connects directly to A
, etc) this all still works.
If there are extra commits but you want to keep the feature
s back from the tip, the --onto
destination should be devel~2
in this example.
Now:
...-o--o--* <-- devel
\
A <-- feature1
\
B--C <-- feature2
\
D--E <-- feature3
Step 1, rebase feature3
interactively onto devel
. What this will do is create a text file with a bunch of pick
commands, each of which will do a git cherry-pick
. You want to pick commits D
and E
. Tell rebase that the "upstream" (poor name) is devel
, and that you want it to start the whole operation by doing git checkout feature3
:
$ git rebase -i devel feature3
Here, rebase will find all commits selected by devel..feature3
(this is A
through E
inclusive, but not either of the two extra commits already on devel
), and will choose as its --onto
target, devel
. Then you end up in the editor, where you can delete all but the last two commits (D
and E
). Rebase will then:
devel
, on a new unnamed branch (detached HEAD mode)D
, adding it to the unnamed branchE
, adding it to the unnamed branchgit reset
to make feature3
point to the latest commit.Now the tree looks like this:
D'-E' <-- feature3
/
...-o--o--* <-- devel
\
A <-- feature1
\
B--C <-- feature2
\
D--E [abandoned, except for reflogs]
(If you add --onto devel~2
to the original git rebase -i
, it will grow the D'-E'
chain from the commit at devel~2
, which is the leftmost o
node. You still need to specify the upstream, which you can list as either devel
or devel~2
: it won't matter in this case.)
Now you can git rebase -i devel feature2
. This works in the same way: check out branch feature2
, find commits in devel..feature2
(which is A
through C
this time), and open an editor session to let you modify the set of cherry-pick
commands that git will run. This time you need only delete one line (for commit A
). Git then starts a new detached-HEAD anonymous branch at commit *
again, grows it with cherry picks of B
and C
, and then moves the feature2
branch:
D'-E' <-- feature3
/
...-o--o--* <-- devel
| \
\ B'-C' <-- feature2
|
A <-- feature1
\
B--C [abandoned]
Last, you can rebase feature1
. Here you don't need to bother with interaction, as commit A
is the only one you want to copy and the only one git rebase
will choose.
The result is the same as with git cherry-pick
. You need somewhat fewer git commands, but a bit more editing for the interactive "pick" sequences.
Which way is "better"? Neither, really. Renaming the old branches and then deleting them will toss their reflogs, which might be a feature since it enables the original A
through E
commits to get garbage-collected sooner, or might be a drawback in case you want to look at them again.
Upvotes: 2
Reputation: 145839
As there is only one or two commits per branch, using cherry-pick will be enough (no need to rebase for one or two commits).
First backup your local repository (do no trust the internet!)
Write down the hash of A, B, C, D and E. Then:
Just do:
# Switch to devel
git checkout devel
# Rename branches into temporary branch names
git branch -m feature1 tmp1 && git branch -m feature2 tmp2
&& git branch -m feature3 tmp3
# Create feature1 branch and cherry-pick the required commit
git checkout -b feature1 && git cherry-pick A
# Switch to devel
git checkout devel
# Create feature2 branch and cherry-pick the required commits
git checkout -b feature2 && git cherry-pick B C
# Switch to devel
git checkout devel
# Create feature3 branch and cherry-pick the required commits
git checkout -b feature3 && git cherry-pick D E
# Switch to devel
git checkout devel
# Delete temporary branches
git branch -D tmp1 tmp2 tmp3
Upvotes: 2