当我学习使��Git与中央仓库(Gitorious上的项目)时,我被告知始终使用rebase而不是merge,因为我们希望有线性历史记录。所以,我一直在尝试这种方式。
现在当我想到它时,它真的那么有益吗?将具有许多提交的分支进行变基比简单合并要耗费更多时间。
目前我能想到的有两个优点:
git bisect
- 可以将提交历史记录与其他版本控制系统(如SVN)一起提交。
还有其他好处吗?
当我学习使��Git与中央仓库(Gitorious上的项目)时,我被告知始终使用rebase而不是merge,因为我们希望有线性历史记录。所以,我一直在尝试这种方式。
现在当我想到它时,它真的那么有益吗?将具有许多提交的分支进行变基比简单合并要耗费更多时间。
目前我能想到的有两个优点:
git bisect
还有其他好处吗?
据我所知,保持线性历史记录大多是出于美学考虑。对于一位非常有技能、守纪律的开发人员来说,这是整理历史记录并使其易于快速浏览的非常好的方式。在完美的世界里,我们应该拥有完全线性的历史记录,让一切变得清晰明了。
然而,在一个大团队的企业环境中,我通常不建议人为地保持线性历史记录。在一支充满聪明、经验丰富和守纪律的开发人员队伍中保持历史记录的线性是一个好主意,但我经常看到这被描述为“最佳实践”或某种“必须做”的方法。我不赞成这样的想法,因为世界不完美,保持线性历史记录会带来很多成本,而人们并没有做好披露的工作。以下是简要概述:
现在,让我们深入研究一下。 警告:篇幅较长,大部分内容是口胡
重写历史可能会导致历史被抹去
将所有提交都重新设置基础以使一切保持线性的问题在于:重新设置基础通常是不可逆的。当您重置基础时,有些信息-实际开发人员完成的真实任务-可能被压缩掉。有时这是好事。如果开发人员发现自己犯了错误,做一个交互式的重新设置基础来整理一下是很好的。已经处理过的捕获并修复错误:我们不需要它们的历史记录。但是有些人与总是搞砸合并冲突的特定个人一起工作。我个人不认识名叫Neal的任何开发人员,所以我们假设这是一个名叫Neal的家伙。Neil正在功能分支上编写一些非常棘手的应收账款代码。Neil在分支上编写的代码是100%正确的,并且按照我们的要求正常工作。Neal已经准备好将他的代码合并到主分支中,但发现现在有合并冲突。如果Neal将主分支合并到他的特性分支中,我们就有了他的代码原始版本以及解决合并冲突后的代码版本历史记录。如果Neal重新设置基础,那么我们只能看到他在重新设置基础后的代码版本。如果Neal在解决合并冲突时犯了错误,那么处理以前的版本比处理以后的版本要容易得多。更糟糕的是,如果Neal以足够不幸的方式搞砸了他的重新设置基础(也许他执行了 git checkout --ours 命令,但忘记了该文件中有重要的更改),我们可能会永久丢失部分代码。我明白了,我知道了。他的单元测试本应该捕捉到这个错误。代码审核人员本应该捕捉到这个错误。QA 本应该捕捉到这个错误。他不应该在解决合并冲突时搞砸了。唠叨几句,无关紧要。Neal 已经退休了,首席财务官非常生气,因为我们的分类账出了大问题,告诉首席财务官 “根据我们的开发理念,这种情况不应该发生” 可能会让我挨打。
并非每个人都会rebase,兄弟。是的,我听说过:你在一家太空时代的创业公司工作,你的物联网咖啡桌只使用最酷、最现代化、反应迅速、基于区块链的递归神经网络,技术栈也很强大!你们的首席开发人员亲临Go语言发明的现场,而且每个人从11岁起就开始成为Linux内核贡献者。我很想听更多,但是由于经常被问到“如何退出git diff?”等问题,我实在没有时间。每当有人试图通过rebase解决与主分支的冲突时,我就会被问到“为什么它说我的文件是他们的文件”或“为什么我只看到了一部分的改动”,然而大多数开发人员可以处理将主分支合并到他们的分支中而不会出现问题。也许这不应该是这种情况,但事实上确实如此。当你的团队中有初级开发人员和实习生、忙碌的人以及直到已经成为程序员35年才知道什么是源代码控制的人时,要保持历史记录的完整性需要付出很多努力。
好处常常被夸大了。
我们都遇到过这样的项目,在使用git log --graph --pretty
命令时,突然终端被彩虹色的意大利面条占据了。但是历史并不难读,因为它是非线性的...它难以阅读的原因是它很杂乱。一个每个提交信息都是“。”的杂乱线性历史并不比思考周全的提交信息相对清晰的非线性历史更容易阅读。拥有非线性历史并不意味着你必须让分支在到达主分支之前来回合并多次。也不意味着你的分支必须存活6个月。偶尔在历史图表上出现的分支并不是世界末日。
我也不认为使用非线性历史进行git bisect是更困难的。一个熟练的开发人员应该能够想到很多方法来完成工作。这里有一篇我喜欢的文章,其中有一个像样的示例。 https://blog.smart.ly/2015/02/03/git-bisect-debugging-with-feature-branches/
tldr; 我不是说变基和线性历史不好。我只是想说您需要了解您正在签署的内容,并根据您的团队是否适合做出知情决策。完全线性的历史并非必需,而且肯定不是免费的。在适当的情况下,它确实可以使生活更美好,但并不适用于所有人。
master
上有新的更改并将你的主题分支rebase
到master
时,你的版本控制系统会忘记批准,因为历史已经被重写。最终,如果“批准”和“合并”之间有延迟,开发人员将不得不再次打扰审批者,这是不必要的。 - Anuj Kumar如果您经常将自己的工作与代码库中的最新版本合并,并且没有其他人在该部分进行工作,那么通常情况下这不会引起什么问题。
以下是大致的命令(来源于 这里):
git checkout -b my-new-feature
git push -u origin my-new-feature
# Changes and commits
git rebase origin/master
git push origin my-new-feature --force-with-lease
git merge --no-ff my-new-feature
这里的一些人似乎将合并和合并提交搞混了。我倾向于采用线性历史和合并提交,就像这样。这样你可以在需要时查看单个提交,但也可以从合并到合并跳转。
git log --follow
轻松跟踪单个文件在重命名过程中的历史记录。引用关于log.follow
配置选项的文档:true
,则git log
将像使用--follow
选项一样工作,当给定单个<path>时。这具有与--follow
相同的限制,即无法用于跟踪多个文件,并且在非线性历史记录上效果不佳。线性/半线性历史的另外两个优点,其他答案中没有提到:
让我通过一个例子来解释。假设以下非线性历史:
* f17ba26 (HEAD -> master) Merge branch 'topic/feature'
|\
| * 1234bbb (topic/feature) adds feature foo
* | 1234aaa blah
|/
* 03f4f8d previous commit
假设某些代码行的演变被提交1234aaa
和1234bbb
所更改,由合并提交解决了产生的冲突:
Commit Line contents
03f4f8d print("Nothing is supported")
1234aaa print("We now support blah!")
1234bbb print("We now support foo!")
f17ba26 print("We support plenty of futures now!")
* f17ba26 (HEAD -> master) Merge branch 'topic/feature'
|\
| * 1234bbb (topic/feature) adds feature foo
|/
* 1234aaa blah
* 03f4f8d previous commit
冲突本应在变基的过程中得到解决,这是在应用1234bbb
提交时发生的。解决冲突的开发人员将有机会修订特定更改,考虑到已经存在的来自1234aaa
的更改,并在提交消息中记录其关于最终代码的决策理由。