如何在GIT中将一个分支合并到另一个分支?

6
让我详细解释一下问题。
我在主git分支上创建了一个名为bug10101010的新侧分支,现在我想将bug10101010合并到主分支中。到目前为止一切都很好。 现在我有同一产品的另一个不同分支,名为legacy。 我想将bug10101010合并到GIT的legacy分支中。
有什么想法吗?
我不能直接合并它,因为bug10101010分支是从主分支中衍生出来的,在legacy中我只需要该分支与其父分支之间的差异。

@Priyank:我同意,打补丁更简单。但是我所描述的方法并不复杂。它还说明了您可以轻松克隆存储库并进行任何Git操作,只要您没有从那里推送即可。 - VonC
3个回答

6
你应该在这里使用 git rebase --onto,并指定一个范围。(请参阅git rebase man page: 将基于一个分支的主题分支移植到另一个分支上,以假装你从后者分支分叉了主题分支,使用rebase --onto.)。
当然,这会将你的bug10分支移动到legacy分支的顶部,这不是你想要/需要的。
因此,一个解决方法是在克隆库中执行那个 rebase,然后将 'enhanced' legacy 分支(克隆库中具有 bug10 修改的分支)与本地和当前的 legacy 分支(你想要修改的分支,同时保留 bug10)合并。
现在:
  • 这涉及到额外的仓库(可能会导致磁盘空间限制)
  • 总体而言,这与定义补丁并将其应用于“legacy”分支相当,因此其他答案(补丁)是有效的(并且更简单)。
  • 我在这种方法中唯一看到的优点是有机会定义一个遗留环境,在该环境中您可以对想要的内容(如“bug10”提交)进行变基,然后只将该分支“legacy”推送到您的原始 repo(您不会推送“bug10”,因为它的历史记录已被完全重写!)

我只是想看看它是否可行,所以...让我们测试一下这种方法。
(Git1.6.5.1,在旧XP SP2上,使用PowerShell 1.0会话,因为Start-Transcript命令)


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

我很喜欢现在不需要再先创建git仓库目录,然后在其中输入git init了!自从1.6.5版本:

"git init"学会了在给定额外参数时(即"git init this"),进入一个目录并创建它。

太好了!

让我们为三个不同的目的创建3个文件。
(为了举例说明,我将保持每个分支的文件修改分开:这里不会有合并或变基冲突。)

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

此时,运行git log --graph --oneline --branches命令会返回:

* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit

让我们建立一个遗留分支legacy

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"

我们回到主分支,进行另一次提交,我们将其标记为“2.0”(这是一个需要修复一些错误的版本!)

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

我们有:
* 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

现在我们创建一个bug10的错误修复分支:

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"

让我们在主分支上添加最终提交

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"

我们主要仓库的最终状态:
* 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

在这个阶段,我不会在主存储库中进行任何进一步的操作。我只会克隆它来进行一些测试。如果这些测试失败了,我可以随时回到这个存储库并重新克隆它。
第一个克隆实际上是必须的,以便执行我们的git rebase --onto
PS D:\git\tests\mainRepo> cd ..
PS D:\git\tests> git clone mainRepo rebaseRepo
PS D:\git\tests> cd rebaseRepo

我们需要在克隆的仓库中获取两个主要仓库分支:
PS D:\git\tests\rebaseRepo> git co -b bug10 origin/bug10
PS D:\git\tests\rebaseRepo> git co -b legacy origin/legacy

让我们只将 bug10 分支上 2.0 标签之后所有的提交变基到 bug10 分支的 HEAD(包括 bug10 分支的 HEAD):

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

此时,bug10 已经在 legacy 的顶部重新播放,没有所有其他中间提交
现在我们可以将 legacyHEAD 快进到重新播放的 bug10 分支的顶部。

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(-)

以下是我们所需的内容:

  • 我们拥有所有传统内容:

 

PS D:\git\tests\rebaseRepo> type legacy.txt
legacyContent
aFirstLegacyEvol

 

  • main分支的内容仅限于1.0标签(legacy分支的根),没有更多了

 

PS D:\git\tests\rebaseRepo> type mainFile.txt
mainFile
firstMainEvol

 

  • 并且这里是bug10的修复:

 

PS D:\git\tests\rebaseRepo> type toBeFixedFile.txt
contentToBeFixed
aFirstBug10Fix
aSecondBug10Fix

 

就是这样。
想法是从原始仓库中拉取那个“增强”的legacy分支,该分支仍将其bug10保持不变(即仍从2.0标签开始,并且没有像我们在rebaseRepo上重播到任何位置。
在这个克隆的仓库中,我跟踪origin/legacy分支,以便将另一个远程源的legacy分支合并到它上面:即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

在这个原始仓库中(我只是克隆它,以免干扰主仓库的状态,以防我有其他实验要做),我将声明rebaseRepo为远程仓库,并获取它的分支。
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

我们现在可以更新 legacy 而不用修改 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(-)

您可以重复此过程任意次数,每当需要将新的bug10提交重放到旧的legacy分支之上时,而不必包含所有中间提交。


我必须得说“哇”! 但我对这些步骤几乎感到困惑,对于像我这样的新手来说,看起来非常复杂。 我只是按照手动修补程序的方式进行。 git diff 标签名 标签名~ 然后 git apply,这对我来说似乎非常简单。 - Priyank Bolia
这个过程结束后,主分支没有bug10的更改,对吗?我认为原帖作者(以及我)想要的是在遗留分支和主分支都有bug10的更改? - huggie
@huggie,OP确实提到了“我想要”,但是四年前我可能有不同的理解。 - VonC

2
这很难做。Git保存合并历史,如果你使用“cherrypick”并将bug10101010中的一个提交作为父提交(表示你已经进行了合并),Git将假定在该提交之前(回到它们分开的点)的所有提交都已经合并。这会在你想要进行“真正”的合并时给你带来问题。
另一方面,你可以手动从那个特定的提交生成补丁。但当你稍后进行“真正”的合并时,这也会给你带来问题,因为它会尝试两次应用你手动处理的提交。
但是,既然一个分支被命名为“Legacy”,我怀疑你不打算进行真正的合并,那么你几乎可以自由地按照自己的方式进行操作。
这里有一篇关于此主题的有趣博客文章

感谢提供如此精彩的链接。那么,您有什么建议,以及如何实现上述功能呢?或者让我重新表达一下问题:我需要创建特定于错误的事务的能力,这些事务始终合并到主分支,但需要随时在其他分支上重放这些事务。 - Priyank Bolia
“always merged to main”是什么意思?一个特定的错误分支只需要合并一次回主分支。如果我的一般性答案,加上博客链接和一般的git文档不能帮助你,你可以考虑更新原始问题,详细说明你所期望的具体工作流程。那时我可能能够提供帮助。 - Mizipzor
问题是相同的,分支bug10101010已合并到主分支中,因为所有的bug修复必须出现在主分支中。但是,如何将分支bug10101010中所做的特定更改与其父分支一起合并到遗留分支中呢? 正如您所建议的那样,手动生成补丁并应用,但这样做会失去跟踪这些更改来自哪个分支的能力。 - Priyank Bolia
那么错误分支是从遗留分支分支出来的,而遗留分支本身是从主分支分支出来的?现在错误分支应该合并回主分支,然后删除(因为错误分支的目的,修复错误,已经实现了)? - Mizipzor
错误分支是从主分支而不是遗留版本分支创建的,因为可能存在遗留版本1.0.3、遗留版本2.0.3等。 错误分支的想法是创建一个事务,就像您手动创建补丁一样。 - Priyank Bolia
那么,您不直接使用补丁的特殊原因是什么呢?是因为缺乏跟踪能力吗?分支的想法通常是根据特定的想法创建的,然后实施该想法,之后将分支合并回主分支,然后删除。我仍然认为您试图以链接中所述的方式使用分支,这不是它们的本意。 - Mizipzor

1

你如何追踪合并的内容是来自bug10101010分支呢,这只是一个事务。此外,这并不是一件简单的事情。我需要能够指定分支名称并合并更改的能力。 - Priyank Bolia
这个想法是创建一个错误事务(使用小分支),并轻松地重放这些事务到任何地方(任何分支)。在CVS / SVN中也可以创建补丁并合并相同的内容,那么就没有必要转移到Git了。 - Priyank Bolia
如果分支是一个事务,那么在其目的完成后,该分支应该被有效地删除,因此仅记录补丁是修复bugXXXXXXXXX的内容是否足够?由于事务的目的是封装更改,因此更改的总和可以作为聚合进行跟踪。 - Amber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接