将两个分支合并到另一个分支上

4
给定以下 Git 历史记录:

enter image description here

如何最简单地将分支feature/1feature/2变基到master上,以实现以下历史记录:

enter image description here

到目前为止,我想到了以下步骤:

  1. 检出 feature/2 分支(我想要变基的提交链的头部)
  2. 将该分支变基到主分支上:git rebase master
  3. 更改 feature/1 分支的头部:git branch -f feature/1 <hash>

其中 <hash> 是由 rebase 命令创建的重复的 Feature 1 提交的哈希值。

这个方法可以正常工作,但对我来说似乎太过复杂。

2个回答

6

最简单和最快速的解决方案就是你已经想出来的那个。你可以将步骤1和2结合起来。

git rebase master feature/2

如果可以轻松计算目标提交的表达式,您可能会发现避免处理提交ID更容易(例如您的示例情况)。

git branch -f feature/1 feature/2^

您可以像其他人提到的一样使用两个rebase。但是,这并不更简单。也就是说,命令的复杂程度是否更简单是一个主观的问题;但您要告诉git做的事情是客观上更加复杂的。因此,出现问题的机会更多。例如,首先对feature/1进行变基,然后在feature/1上变基feature/2的特定技术依赖于第二个变基期间重复补丁ID的检测,如果在第一个变基期间发生任何冲突解决,则会以不幸的方式失败。如果您真的想使用两个rebase方法,我建议第二个rebase应该像这样完成。
git rebase --onto feature/1 feature/1@{1} feature/2

为了避免重新编写feature/1提交记录,建议使用这种方法。(feature/1@{1}是指在重置之前指向feature/1的reflog条目。)但是,这样做需要使用较不常见的rebase语法,据我所知,它至少与您最初执行的操作一样“不简单”。

或者,您可以使用--fork-point选项,让git自动解释reflog并尝试找出哪些提交记录是重复的。

git rebase --fork-point feature/1 feature/2

只要你在同一个克隆上几乎同时执行两个变基操作,这将在大多数情况下起作用。但是,这会增加更多的复杂性(以git在幕后执行的“魔法”形式),因此您需要了解它可能出错,如果出错,要么(a)足够了解它尝试做什么来诊断问题,要么(b)撤销并使用其他方法。

是的,看起来在第一次变基时存在冲突时,2-rebase方法会失败。你的第二个建议可以正常工作,但远非我所说的“简单”。 然而,你的第一个建议并不可行。它会出现以下错误: zsh: no matches found: feature/2^ - Kuba Szymanowski
啊,我以为是git错误,但实际上是shell错误。将feature/2^用引号括起来就可以了。 - Kuba Szymanowski
另外,您能解释一下 feature/1@{1} 的语法或者指向相关资源吗? - Kuba Szymanowski
1
@KubaSzymanowski - feature/1@{1} 是一个 reflog 表达式。它只在第一次变基发生的特定存储库中起作用(reflogs 是本地的,因此其他克隆看不到它),而且您还必须知道 reflogs 过期了(但默认是保留它们一段相当长的时间)。但基本上它表示“查看 feature/1 指向的位置的历史记录,在过去往后退一步”。请参见https://git-scm.com/docs/git-reflog。 - Mark Adelsberger

-3
告诉 Git 你正在做什么的命令序列是:
git rebase master feature/1
git rebase feature/1 feature/2

就像这样,你可以复制并粘贴:

git init test; cd $_
doit() { eval echo \>$*; git add $1; git commit -m "$2"; }
doit master "Initial commit"
git checkout -tb feature/1
doit file1 "Feature 1"
git checkout -tb feature/2
doit file2 "Feature 2"
git checkout master
doit master "New commit on master"

精确地绘制您的图表:

$ git log --graph --decorate --oneline --all
* 35cfad5 (feature/2) Feature 2
* 46b79ae (feature/1) Feature 1
| * ae89e31 (HEAD -> master) New commit on master
|/  
* f1138eb Initial commit
$

现在,既然你已经告诉 Git 你的分支结构了,

$ git rebase master feature/1
First, rewinding head to replay your work on top of it...
Applying: Feature 1
$ git rebase feature/1 feature/2
First, rewinding head to replay your work on top of it...
Applying: Feature 2
$ 

1
像这样的模式,告诉git多次变基相同的提交(并依赖于它来通过补丁ID检查“修复”错误)是不可靠的。很遗憾,人们继续投票支持这样的答案,因为他们教导人们在简单情况下使用变基的方法,然后在最不方便的时候以看似神秘的方式失败。 - Mark Adelsberger
@MarkAdelsberger 在另一个被删除的答案中,您提到这里的语法是 rebase <upstream> <source>。您所说的 upstream 是什么意思?我们在这里没有涉及远程存储库,除非我理解错了,否则 upstream 指的是该远程存储库(如 origin)。 - Kuba Szymanowski
1
@KubaSzymanowski:git 有一个不幸的习惯,在不同的上下文中使用相似但不同的意思来表示同一个词。我认为在 rebase 中使用 upstream 可能源于这样一种用例,即您正在将本地更改移动到远程(如使用 pull --rebase)以进行追赶;更普遍地说,它通常意味着“我的”更改(我正在重写的更改)分叉的分支。从技术层面上讲,它的意思是“在选择要重写的提交时,排除此提交及其可达的任何提交,因为它们在我正在重写的提交的上游(拓扑)”。 - Mark Adelsberger
1
@KubaSzymanowski:话虽如此,这个主题太复杂了,在评论中无法充分说明。我建议查看https://git-scm.com/docs/git-rebase上的“git rebase”文档,并注意当您只给出两个引用时,第一个引用是“<upstream>”,并且有一个不同的“--onto”选项-这就是为什么在这种情况下说“onto”时实际上并不意味着“upstream”的原因。 - Mark Adelsberger
@jthill - 不,它不会。第二个命令说“将从feature/2可达但不再可达于feature/1的所有内容重新定位。但是不要相信我的话-我已经多次描述了失败条件。运行测试。 - Mark Adelsberger
显示剩余3条评论

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