How to merge a branch to another branch in GIT?
Let me explain the problem in detail.
I have a main git branch on which I created a new side branch b开发者_开发问答ug10101010, now I wan't to merge the bug10101010 to main. So far everything is good. Now I have a different branch of the same product, named legacy. I wan't to merge the bug10101010 to the legacy branch in GIT.
Any ideas?
I can't just merge it directly, as the branch bug10101010 is spin off from the main branch and in the legacy I need only the diff between the branch bug10101010 and its parent branch.
You should use git rebase --onto
here, and specify a range.
(see git rebase
man page:
transplant a topic branch based on one branch to another, to pretend that you forked the topic branch from the latter branch, using
rebase --onto
.
).
Of course, this would move your bug10
branch on top of the legacy
branch, which is not what you want/need.
So, one workaround would be to do that rebase in a cloned repo, then merge that 'enhanced' legacy
branch (the one in the clone repo, with the bug10
modifications on top of it) to the local and current legacy
branch (the one you want to modify, while leaving bug10
alone).
Now:
- this involves an extra repo (which can lead to disk space limitation)
- all in all, this is fairly equivalent to define a patch and apply it to
legacy
branch, so the other answers (patch) are valid (and simpler). - the only advantage I see in this method is the opportunity to define a legacy environment in which you rebase what you want (like the
bug10
commits), before pushing only that branchlegacy
to your original repo (you would not pushbug10
, since its history would have been entirely rewritten!)
I just wanted to see if it works, so... Let's test that approach.
(Git1.6.5.1, on a old XP SP2, with a Powershell 1.0 session because of the Start-Transcript
command)
PS D:\> mkdir git
PS D:\> cd git
PS D:\git> mkdir tests
PS D:\git> cd tests
PS D:\git\tests> git init mainRepo
I like how I do not have anymore to make the git repo directory first, then type in it git init
! Since 1.6.5:
"
git init
" learned tomkdir
/chdir
into a directory when given an extra argument (i.e. "git init this
").
This is GREAT!
Let's create 3 files, for 3 different purposes.
(For the sake of example, I will keep the file modifications separate per branch: no conflict during merge or rebase here.)
PS D:\git\tests> cd mainRepo
PS D:\git\tests\mainRepo> echo mainFile > mainFile.txt
PS D:\git\tests\mainRepo> echo contentToBeFixed > toBeFixedFile.txt
PS D:\git\tests\mainRepo> echo legacyContent > legacy.txt
PS D:\git\tests\mainRepo> git add -A
PS D:\git\tests\mainRepo> git ci -m "first commit"
PS D:\git\tests\mainRepo> echo firstMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "first evol, for making 1.0"
PS D:\git\tests\mainRepo> git tag -m "1.0 legacy content" 1.0
At this point, a git log --graph --oneline --branches
returns:
* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit
Let's build a legacy
branch
PS D:\git\tests\mainRepo> git co -b legacy
PS D:\git\tests\mainRepo> echo aFirstLegacyEvol >> legacy.txt
PS D:\git\tests\mainRepo> git ci -a -m "a first legacy evolution"
We return to master, make another commit, which we will tag "2.0" (a release which will need some bug-fixing!)
PS D:\git\tests\mainRepo> git co -b master
PS D:\git\tests\mainRepo> git co master
PS D:\git\tests\mainRepo> echo aMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a main evol"
PS D:\git\tests\mainRepo> echo aSecondMainEvolFor2.0 >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a second evol for 2.0"
PS D:\git\tests\mainRepo> git tag -m "main 2.0 before bugfix" 2.0
We have:
* e727105 a second evol for 2.0
* 473d44e a main evol
| * dbcc7aa a first legacy evolution
|/
* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit
Now we do a bug10
bug-fixing branch:
PS D:\git\tests\mainRepo> git co -b bug10
PS D:\git\tests\mainRepo> echo aFirstBug10Fix >> toBeFixedFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a first bug10 fix"
PS D:\git\tests\mainRepo> echo aSecondBug10Fix >> toBeFixedFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a second bug10 fix"
Let's add a final commit on the main branch
PS D:\git\tests\mainRepo> git co master
PS D:\git\tests\mainRepo> echo anotherMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "another main evol"
Final state of our main repo:
* 55aac85 another main evol
| * 47e6ee1 a second bug10 fix
| * 8183707 a first bug10 fix
|/
* e727105 a second evol for 2.0
* 473d44e a main evol
| * dbcc7aa a first legacy evolution
|/
* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit
At this stage, I will not make any further manipulation in mainRepo. I will only clone it to make some tests. If those fails, I can always get back to this repo and clone it again.
The first clone is actually mandatory, in order to perform our git rebase --onto
PS D:\git\tests\mainRepo> cd ..
PS D:\git\tests> git clone mainRepo rebaseRepo
PS D:\git\tests> cd rebaseRepo
We need two of the mainRepo branches in our cloned repo:
PS D:\git\tests\rebaseRepo> git co -b bug10 origin/bug10
PS D:\git\tests\rebaseRepo> git co -b legacy origin/legacy
Let's rebase only bug10 (that is all commits after 2.0
tag up to HEAD
of bug10
branch):
PS D:\git\tests\rebaseRepo> git co bug10
PS D:\git\tests\rebaseRepo> git rebase --onto legacy 2.0
First, rewinding head to replay your work on top of it...
Applying: a first bug10 fix
Applying: a second bug10 fix
At this point bug10
has been replayed on top of legacy
without all the other intermediate commits.
We can now fast-forward HEAD
of legacy
to the top of the replayed bug10
branch.
PS D:\git\tests\rebaseRepo> git co legacy
Switched to branch 'legacy'
PS D:\git\tests\rebaseRepo> git merge bug10
Updating dbcc7aa..cf02bfc
Fast forward
toBeFixedFile.txt | Bin 38 -> 104 bytes
1 files changed, 0 insertions(+), 0 deletions(-)
The content follow what we need:
- We do have all the legacy content:
PS D:\git\tests\rebaseRepo> type legacy.txt
legacyContent
aFirstLegacyEvol
- the content for the
main
branch is there only up to1.0
tag (root forlegacy
branch), and not any further.
PS D:\git\tests\rebaseRepo> type mainFile.txt
mainFile
firstMainEvol
- and the
bug10
fixes are here:
PS D:\git\tests\rebaseRepo> type toBeFixedFile.txt
contentToBeFixed
aFirstBug10Fix
aSecondBug10Fix
That's it.
The idea is to to pull that 'enhanced' legacy
branch in your original repo, which will still have its bug10
unchanged (i.e. still starting from the 2.0
tag, and not replayed anywhere like we did on the rebaseRepo
.
In this cloned repo, I track the origin/legacy
branch, in order to merge on it the legacy
branch of another remote source: the rebaseRepo
.
PS D:\git\tests\rebaseRepo> cd ..
PS D:\git\tests> git clone mainRepo finalRepo
PS D:\git\tests> cd finalRepo
PS D:\git\tests\finalRepo> git co -b legacy origin/legacy
In this original repo (I only cloned it to not mess with the state of the mainRepo, in case I had some other experiments to do), I will declare rebaseRepo
as a remote, and fetch its branches.
PS D:\git\tests\finalRepo> git remote add rebasedRepo D:/git/tests/rebaseRepo
PS D:\git\tests\finalRepo> type D:\git\tests\finalRepo\.git\config
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = D:/git/tests/mainRepo
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "legacy"]
remote = origin
merge = refs/heads/legacy
[remote "rebasedRepo"]
url = D:/git/tests/rebaseRepo
fetch = +refs/heads/*:refs/remotes/rebasedRepo/*
PS D:\git\tests\finalRepo> git fetch rebasedRepo
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 6 (delta 3), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From D:/git/tests/rebaseRepo
* [new branch] bug10 -> rebasedRepo/bug10
* [new branch] legacy -> rebasedRepo/legacy
* [new branch] master -> rebasedRepo/master
We can now update legacy
without touching to bug10
:
PS D:\git\tests\finalRepo> git merge rebasedRepo/legacy
Updating dbcc7aa..4919b68
Fast forward
toBeFixedFile.txt | Bin 38 -> 104 bytes
1 files changed, 0 insertions(+), 0 deletions(-)
You can repeat the process as many time as you want, whenever new bug10
commits need to be replayed on top of an old legacy
branch, without including all the intermediate commits.
This is hard to do. Git saves merge history, and if you "cherrypick" and point at a commit in bug10101010 as a parent (indicating you have done a merge) Git will assume that all commits before that (back to the point where they split) as been merged as well. Giving you problems when you want to do a "real" merge.
On the other hand you can just manually generate a patch from that (and only that) specific commit. But that will also give you problems when you later do the "real" merge, since it tries to apply your manually handled commit twice.
But then again, since one branch is named "Legacy" I suspect that you dont plan to do that real merge anyway, in which case youre pretty much free to do it anyway you please.
Heres an interesting blog post on the topic.
Use git-diff and then git-apply?
精彩评论