在公共特性分支中使用git rebase

36

在网络上,人们总是建议不要在公共分支中使用 git rebase,但我看不出有什么问题,如果你总是在一个特性分支上进行变基。

我的团队总是为特性(哇)创建分支,我们习惯于只在本地使用它,因此变基不是问题,但有时我们想向另一个开发人员展示部分完成的特性代码,所以我们只需将其公开化,但然后我们就失去了使用 git rebase 的所有优势,或者至少在网上可以读到这样的说法。

我不明白问题在哪里,如果正在同一公共分支上工作的开发人员从未将其合并到任何分支中(当该分支仍在开发时),并且当他们拉取它时,他们会执行一个变基操作。对分支所做的任何更改将始终在远程分支的顶部进行变基,从而永远不会丢失,并且您不会遇到相同提交重复的问题。

附录1:

至今为止,没有一个答案显示将发生什么问题以及如何发生问题,因此我将尝试更清楚地解释。

我将举例说明使用变基的工作流程(在以前的段落中描述不当,抱歉),我认为没有任何问题。

初始状态:

master         ==========A
origin/feature           +=====AB
feature user A           +=====AB
feature user B           +=====AB

主分支(master)新增了一些提交记录,用户A也进行了几次提交:

master         ==========A=====C
origin/feature           +=====AB
feature user A           +=====AB====D
feature user B           +=====AB

用户A经常使用git pull --rebase命令更新分支,在没有新内容的情况下,他将分支变基到主分支并推送:

master         ==========A=====C
origin/feature                 +=====ACB'=====ACB'D
feature user A                 +=====ACB'=====ACB'D
feature user B           +=====AB

(请注意,B'是仍然代表更改B的新提交)
然后用户B进行了几次提交:
master         ==========A=====C
origin/feature                 +=====ACB'=====ACB'D
feature user A                 +=====ACB'=====ACB'D
feature user B           +=====AB======E

作为惯例,用户B最终执行了git pull --rebase操作,因为没有必要在主分支上进行变基,所以他只需进行推送:

master         ==========A=====C
origin/feature                 +=====ACB'=====ACB'D======E'
feature user A                 +=====ACB'=====ACB'D
feature user B                 +=====ACB'=====ACB'D======E'

4
“附录1”中的问题在于,一旦用户B执行了git pull --rebase,他的B'将与用户A的B'不同,因为每个提交(commit)B'的变体都包含一个提交时间戳,这将改变每个提交的SHA-1哈希。如果既没有用户A也没有用户B更改任何补丁,并且每次重新基础操作都没有冲突,那么只要始终在主分支上进行重新基础而不是合并到主分支,你就可以基本解决SHA-1的差异。如果您所有的git用户都意识到了这种情况,请继续使用。 - Mikko Rantalainen
2
@MikkoRantalainen,那不是真的。时间戳是被保留的。但是当存在冲突时,用户A可能会以不同于用户B的方式解决冲突,这将产生不同的提交(不同的SHAs),从而破坏一切。那就是我4年前期望得到的答案。如果你能写成答案,我会很高兴接受它。 - Filipe Giusti
5
一个Git提交始终包含两个时间戳,它们都会影响提交的SHA-1值:"作者"时间戳和"提交者"时间戳(都精确到秒;请尝试使用git cat-file -p HEAD查看示例)。在创建提交时,可以覆盖这两个时间戳,但对于新提交,默认情况下两个时间戳都为当前时间。对于已经变基的提交,“作者”时间戳保持不变,而“提交者”时间戳设置为变基时间。除非您在变基时使用特殊标志,否则您的变基后的SHA-1取决于您运行变基命令的时间。 - Mikko Rantalainen
3个回答

41

如果进行rebase操作,就相当于改写历史。就像现实生活中一样,如果你想改写历史,你需要密谋:每个知道历史的人都必须参与其中(至少是那些曾经从分支上拉取过代码的人)。

只要拉取代码的人数目得到严格控制,发动密谋就比较容易。但是,一旦你公开了这段历史,事情就会变得困难 许多。然而,并非不可能做到:例如,在Junio C Hamano的Git存储库中,pu分支在每次发布后都会被rebase,而该存储库是广泛发布的。实现这一点的方式是将分支频繁地rebase以及这将发生的时间,在Git网站、Git wiki和Git邮件列表上广泛记录,并且每次rebase都会提前在邮件列表中宣布,以便人们能够做好准备。


5
没错,重点是任何使用(或可能使用)分支的人都必须被告知它的历史记录是否最终可能被重写。只要你得到所有(潜在)用户的同意,重写已发布的历史记录就没有问题。用户的同意表示当你重写该分支的历史记录部分时,他们没有抱怨的权利。你甚至可以使用特殊前缀来指示可重写的分支:tmp/、unstable/、rewritable/或use-at-own-risk/。 - Chris Johnsen
我不认为这需要共谋,我改进了我的问题,提供了一个不需要共谋的工作流示例。 - Filipe Giusti
8
只要你能让每个人参与这个阴谋,那么确保他们与重新编写的历史同步的步骤是什么? - kenwarner
未来参考:如何从上游变基中恢复,例如在 https://git-scm.com/docs/git-rebase#_recovering_from_upstream_rebase(部分内容也在 https://git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-When-You-Rebase 中)有所涵盖。 - cr7pt0gr4ph7
你读过奥威尔的《1984》吗?那里有一个很好的反基础重构的例子。 - kmiklas

7

当你对公共分支进行变基时,这是完全可以的。

但是,当你对公共分支本身进行变基时,对于也在使用它的人来说并不好。

补充:

当变基导致单元测试失败时,你将没有机会git bisect找到错误版本。

更详细地说:

  • 你已经准备好要添加到分支中的一些代码。
  • 你调试了它,使其通过了所有单元测试
  • 你已经从(远程)分支中获取了新更改
  • 现在你正在对你的代码进行变基,以适应重新变基的远程分支
  • 在这里,单元测试被破坏了
  • 你运行git bisect,它指向了远程变基。
  • 你会采取什么行动?

难道这不是因为你的历史记录与其他人不同,从而混淆了同步你的存储库的尝试吗? - Andrew Matthews
1
我的行动:检查为什么它会中断并修复提交,这太难了吗?如果您合并并且测试对于合并的2个父项都没有问题,但对于合并提交有问题,则会发生相同的情况。 - Filipe Giusti

5

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