Reputation: 2026
I have a linear history like so
----a--b--c--d--e--f---- main
I want to move a series of commits into a new branch and create a merge commit into the old branch, creating a non-linear history like so
----a-------x--d'--e'--f'-- main
\ /
b--c
where x
is the new merge commit.
How do I do this?
Note, the moved commits aren't the most recent ones (see here).
Upvotes: 1
Views: 554
Reputation: 2026
Let's start by creating a few pointers to make things easier.
git branch base <a>
git branch feat <c>
The diagram looks like this.
----a--b--c--d--e--f---- main
^ ^
base feat
Now we merge feat
into base
, creating the merge commit.
git switch base
git merge --no-ff feat
The diagram now looks like this.
----a------x base
\ /
b--c--d--e--f-- main
Now we rebase main
onto base
, moving it onto the new merge commit.
git switch main
git rebase --onto base feat
Finally, we can clean up the pointers, which we don't need anymore.
git branch -d base
git branch -d feat
And we end up with the diagram that looks like this.
----a------x--d'--e'--f'-- main
\ /
b--c
Upvotes: 1
Reputation: 534893
You can do it easily enough, but you will be rewriting the history of main
which is not recommended and might have to be force-pushed to the remote.
Anyway, here's your initial situation, more or less (I'm using master
instead of main
and I only went up to e
):
$ git log --oneline --graph --all
* d5de97b (HEAD -> master) e
* 97f47d1 d
* 7c33da9 c
* 295b3c7 b
* 18622aa a
OK, I propose to turn b
and c
into commits on a branch feature
that will be merged into master
before d
. Ready? (Some of these steps, such as some of the checkout
moves, may be unnecessary, but I didn't pause to worry about that.)
$ git branch holdmyplace # prevent losing any commits
$ git branch feature 7c33da9 # call `c` by a branch name `feature`
$ git reset --hard 18622 # slide `master` all the way back to `a`...
$ git merge --no-ff feature # ...and merge `feature` with a merge commit!
$ git checkout holdmyplace # okay, now return to the placeholder branch...
$ git rebase --onto master feature holdmyplace
# ...and append it to `master`
$ git checkout master # finally, return to master...
$ git reset --hard holdmyplace # ...and slide it back up to the end!
$ git branch -d holdmyplace # we no longer need the placeholder branch
And now we have:
$ git log --oneline --graph --all
* 0d58dd1 (HEAD -> master) e
* 7831073 d
* 2bd7688 Merge branch 'feature'
|\
| * 7c33da9 (feature) c
| * 295b3c7 b
|/
* 18622aa a
Which is what you said you wanted.
Upvotes: 2
Reputation: 30156
This is what I would do:
git checkout a
git merge --no-ff c
git cherry-pick c..main
# at this point you are on a revision that has a history like what you asked for
# now it's time to move main
git branch -f main
git checkout main
And if you have pushed that branch into other repos, you will have to force-push new because you rewrote its history. (there are other considerations out of you rewriting history, just in case).
Upvotes: 2