何时应该使用git pull --rebase命令?

1135

我知道一些人默认使用git pull --rebase,而另一些人坚持永远不要使用它。我相信我理解合并和变基的区别,但我正在尝试将其放入git pull的上下文中。这只是因为不想看到许多合并提交信息,还是存在其他问题?


9
反对使用git pull --rebase的建议来源是什么?Rebase或者说git rebase是与git pull --rebase不同的独立操作。 - przemo_li
11个回答

955

我希望提供关于git pull --rebase的不同观点,因为有时候它可能会被忽略。

如果你曾经使用过Subversion(或者CVS),那么你可能已经习惯了svn update的行为。如果你有改动要提交,但是提交失败了因为上游代码已经发生了变化,那么你就需要执行svn update。Subversion会将上游代码与你的本地代码进行合并,可能会导致冲突。

Subversion实际执行的操作就是git pull --rebase。将你的本地代码相对于新版本进行重新制定的操作就是“rebasing”。 如果在提交失败之前先执行了svn diff,然后将结果与提交失败后的svn diff输出进行比较,那么两个差异之间的区别就是rebasing操作所做的事情。

Git和Subversion在这种情况下的主要区别在于,在Subversion中,“你”的更改仅存在于工作副本中而未提交,而在Git中,你有实际的本地提交。换句话说,在Git中,你已经分叉了历史记录;你的历史记录与上游历史记录已经分离,但是你们有共同的祖先。

在我看来,如果你的本地分支只是简单地反映了上游分支并持续进行开发,则始终应该使用--rebase,因为这才是你实际上正在做的。你和其他人都在打破一个分支的有意线性历史。有人恰巧在你尝试推送之前稍微提交了一点更改,这实际上是无关紧要的,而且对于每个这样的时间差事故都导致历史记录中的合并似乎是不利的。

如果你确实需要某些东西成为一个分支,那么在我看来,这是一个不同的问题。但是,除非你有明确且积极的愿望以合并的形式表示你的更改,否则默认行为应该是git pull --rebase

请考虑其他需要观察和了解项目历史的人。您希望在历史记录中散布着成百上千个合并记录,还是只有一些代表着真正有意义的分支开发努力的少数精选合并记录?请注意保留原文中的HTML标签。

30
请注意,我并不是说 Git(这个工具)应该默认使用变基(rebase)。 这样做很危险,因为变基实际上会破坏历史记录。 我的意思是,在工作流程方面,当你不打算分支,而只是在一些其他人也在工作的分支上进行hack时,“git pull --rebase” 应该是用户的默认行为。 - scode
12
@MarceloCantos 我不是 ;) 就我个人而言,如果我在除了本地<->远程之外的分支之间合并时,我会将autosetupmerge保留为默认值true(我喜欢明确地指定)。但我想表达的是,作为一个人,我通常会在我的正常“获取主分支中最新内容”的工作流程中使用"git pull --rebase",因为我永远不想创建合并提交,除非我明确进行分支操作。 - scode
19
+1 @scode。在经历了许多痛苦的时间与合并问题搏斗后,终于有了一个准确解答。 - Thinkisto
15
这个答案只是试图让非分布式版本控制系统的用户适应Git,而不是给他们一个机会来正确理解拥有正确分支的好处。 - Alexey Zimarev
3
太棒了!我在一家公司工作,该公司的策略是挑选而不是合并或拉请求,因此本地分支必须反映远程分支,我们应该始终在推送之前“更新”本地分支以避免代码审查工具中的冲突。我总是明确地使用“git fetch && git rebase”,这和“git pull --rebase”做的事情一样吗? - Thomas H.
显示剩余6条评论

734

当您的更改不需要单独的分支时,应使用git pull --rebase

  • 这样更清晰,不会对您的提交强加逻辑分组。

好吧,我想它需要一些澄清。在Git中,您被鼓励分支和合并。您的本地分支用于拉取更改,而远程分支实际上是不同的分支,并且git pull是关于合并它们的。这是合理的,因为您不经常推送,并且通常在构成完成功能之前积累了许多更改。

但是,有时,出于某种原因,您认为如果这两个分支(远程和本地)实际上是一个分支,那么实际上会更好。就像在SVN中一样。这就是git pull --rebase发挥作用的地方。您不再进行合并-实际上您是在远程分支之上提交。这就是它实际上所关注的内容。

是否危险取决于您是否将本地和远程分支视为一个不可分割的整体。有时是合理的(当您的更改很小或者如果您处于健壮的开发初期,当重要更改通过小提交带来时)。有时不是(当您通常会创建另一个分支,但您太懒了以至于没有这样做)。但这是一个不同的问题。


25
我认为当你在同一个分支上工作时这是很有用的,但是你可能会更换工作站。我倾向于从一个工作站提交和推送我的更改,然后在另一个工作站上拉取并变基,继续在同一个分支上工作。 - Pablo Pazos
14
使用 git config --global pull.rebase preserve 将 Git 配置为在拉取时自动进行变基(rebase)是一种有用的最佳实践。(preserve 表示除了启用重新变基外,还尝试保留本地进行的任何合并操作。) - Colin D Bennett
4
我不同意只在单个分支上工作时才使用"pull --rebase"。除非由于其他情况不可能,否则应该始终使用它。 - Tomáš Fejfar
1
@P Shved:“但是,有时候——出于任何原因——你会认为如果远程和本地合并为一个分支,那实际上会更好”……这可行吗?我的理解是,在本地环境中,我可以将我的分支和远程分支设置成 origin/master 的镜像。您能提供相关建议吗? - Mandroid
2
我认为值得注意的是,冲突最终仍然会导致一次合并。 - ka3ak
1
@ColinDBennett 显然,git 已经不再支持 pull.rebase preserve 选项。它似乎被同样具有相同效果的 merge 选项所取代。或者你最初可能打错字了,输入了 preserve 而不是 merge。:) https://git-scm.com/docs/git-config#Documentation/git-config.txt-pullrebase - https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---rebasefalsetruemergesinteractive - mr.b

417
也许用一个例子来解释最好不过了:
  1. Alice创建了一个名为A的主题分支,并在上面工作。
  2. Bob创建了一个无关的主题分支B,并在上面工作。
  3. Alice执行git checkout master && git pull。Master已经是最新的了。
  4. Bob执行git checkout master && git pull。Master已经是最新的了。
  5. Alice执行git merge topic-branch-A
  6. Bob执行git merge topic-branch-B
  7. Bob在Alice之前执行git push origin master
  8. Alice执行git push origin master,但由于不是快进式合并而被拒绝。
  9. Alice查看origin/master的日志,发现提交与她的提交无关。
  10. Alice进行git pull --rebase origin master
  11. Alice的合并提交被取消,Bob的提交被拉取,然后Alice的提交被应用到Bob的提交后面。
  12. Alice执行git push origin master,这样每个人在查看未来的日志时都不需要阅读无用的合并提交了。

请注意,合并到特定的分支对于本例来说是无关紧要的。在本例中,Master也可以是一个发布分支或开发分支。关键点在于Alice和Bob同时将他们的本地分支合并到共享的远程分支中。


6
好的。我倾向于详细说明:git co master && git pull; git checkout topic-branch-A; git rebase master; git checkout master; git merge topic-branch-A; git push origin master,如果有其他人在我之前推送到主干分支,则重复该过程。尽管我可以看到你的方法更为简洁有效。 - HankCa
6
"Alice的提交在Bob的提交之后被应用了",值得注意的是,在这种情况下,Alice的提交哈希值会发生变化。 - jiggunjer
1
以下是有关编程的内容,请将其翻译成中文。请仅返回翻译后的文本:对以下主题的最佳解释 - Niko
2
我不明白。如果两个人修改的内容没有关联,那么修改后的内容之间就不应该有冲突。为什么在这种情况下不允许使用push? - 时间只会一直走
2
@alalalala 当这些分支创建时,它们是基于主分支的HEAD。当合并分支B时,主分支的HEAD发生了变化。如果B的分支有5个提交,那么分支A在合并B后所知道的上一个HEAD变成了HEAD-5。所以现在,分支A不知道它应该去哪里。它只知道肯定是在HEAD-5之后,但现在又有了5个其他的提交!它是在HEAD之后还是在HEAD-5之后?通过rebase可以回答这个问题,它告诉你:“你在HEAD之后”。它不再是基于(旧的)HEAD-5,而是基于(新的)HEAD。因此:rebase(d)! - undefined

189

我认为在与他人协作开发同一分支时,应该使用git pull --rebase。您处于工作 → 提交 → 工作 → 提交的循环中,当您决定推送工作时,由于同一分支上有并行工作,您的推送将被拒绝。此时,我总是执行pull --rebase。我不使用Squash(将提交压缩为一个),而是使用变基来避免额外的合并提交。

随着您对Git的了解增加,您会发现自己更多地关注历史记录,而不是其他任何版本控制系统。如果您有大量的小型合并提交,很容易失去正在发生的更大背景。

这实际上是我唯一做变基的时间,我的其余工作流都是基于合并的。但只要您的最频繁的贡献者这样做,最终历史记录看起来会好得多。

(*) 在教授Git课程时,我曾被学生拘捕过,因为我还倡导根据某些情况变基功能分支。他读过这个答案 ;) 这样的变基也是可能的,但必须按照预先安排/约定的系统进行,并且因此不应“总是”应用。那时,我通常也不执行pull --rebase,这就是问题所在;)


4
当然可以编写脚本来隐藏合并提交记录,从而在日志中不显示它们。 - hasen
4
合并(Merges)也可能包含差异(diffs),这意味着它并非完全琐碎。 - krosenvold
11
@hasen j 是的,但是这些合并的内容可能很重要。 - krosenvold
与所选答案相比,此回答含糊且带有个人观点:“同一分支”是什么意思?然而,此回答中也有一些好的观点,这些观点在所选答案中并未提及。 - iwein
2
“分支”一词的含义是有意模糊的,因为使用引用的方式有很多种;“工作线路”只是其中之一。 - krosenvold
@hasen 看起来 git log --no-merges 只是一个可以轻松避免问题的解决方法。 - Victor Yarema

71

记住:

  • pull = 拉取(fetch) + 合并(merge)
  • pull --rebase = 拉取(fetch) + 变基(rebase)

因此,选择你想要处理分支的方式。

最好了解合并(merge)和变基(rebase)之间的区别 :)


虽然这个助记符很有帮助,但这并不完全正确。 - CervEd
13
这并没有回答何时使用rebase的问题。 - Dylan Kinnett

59

我认为没有不使用 pull --rebase的理由 - 我专门在 Git 中添加代码,以便我的git pull命令始终重新对齐上游提交。

当查看历史记录时,知道工作人员何时停止同步其功能是从未有趣的。这可能对正在执行此操作的人很有用,但这就是reflog的用途。它只会给其他人增加噪音。


3
浏览历史记录时,了解那个正在开发特性的人何时停下来进行同步,这似乎并不是很有趣。但这是否意味着那些中间提交很可能是错误的版本? - eglasius
15
是的,而且他们也不是一个“整个州”。这就是为什么我们不想要他们。我想知道他想要什么,而不是他是怎么到那里的。 - Dustin
8
如果应该始终使用 pull --rebase,为什么 pull 默认不这样做? - straya
3
很抱歉,你必须自己形成自己的观点。我在我的.gitconfig中设置了一些选项以使某些操作能够正确执行。我认为默认情况下git rebase和git tag等操作都有问题...如果你持不同意见,无需解释。 - Dustin
13
如果你从“上游”(例如master)拉取,并且你要拉取的分支还没有发布(即公开),那么这似乎是个不错的建议。然而,如果相反地,你从一个特性分支拉取到master中,情况就有点相反了:永远没有理由使用--rebase,对吗?这也许是为什么没有将其设为默认选项的原因。我发现,变更从层次结构顶部向下传递时应该使用Rebase,而当它们向上流回时则使用合并(merge)。原文链接:https://www.derekgourlay.com/blog/git-when-to-merge-vs-when-to-rebase/ - jolvi
显示剩余3条评论

22

编辑:不要像其他回答所建议的那样将--rebase作为默认选项,否则可能会丢失部分工作。例如:

  • 合作者推送提交1
  • 在同一分支上,您推送提交2
  • 合作者在未拉取您的工作的情况下修改了提交1并git push --force它(这不是一个好习惯,但您无法控制此情况)
  • 如果您执行git pull --rebase,则将悄悄丢失提交2中包含的所有工作。

我建议仅在您知道自己忘记在其他人之前提交自己的提交时使用git pull --rebase只有.

如果您没有提交任何内容,但您的工作空间不干净,请在执行git pull之前执行git stash,然后执行git stash pop。这样,您就不会悄悄地重写历史记录,从而可能悄悄地删除您的一些工作。


如何使“下拉菜单”工作? - user18099
5
假设你(A)和另一个人(B)正在同一分支上工作。 B推送了他们的提交1,你推送了你的提交2,然后B在不拉取你的工作的情况下修改了他们的提交1并使用强制推送。如果你拉取并变基,将会丢失包含在提交2中的所有工作。 - Habax
2
这是一个很好的经验法则 - 在拉取之前先存储 - Dani Amsalem
2
在您所描述的情况下,您不会“悄悄地丢失提交2”。在重新设置步骤期间,提交2和来自原始的修改后提交1之间的差异仍然在本地应用。您不会失去您的工作。 - Tony B

15

如何使用 git pull

  • git pull: 更新本地工作分支以包含远程提交,并更新所有远程跟踪分支。

  • git pull --rebase: 更新本地工作分支以包含远程提交,但重写历史记录,使得任何本地提交都出现在来自远程的所有新提交之后,避免了合并提交。

  • git pull --force: 此选项允许您在使用该选项时由于冲突而不会被获取的情况下强制获取特定的远程跟踪分支。如果要强制 Git 覆盖您当前的分支以匹配远程跟踪分支,请阅读下面关于使用 git reset 的内容。

  • git pull --all: 获取所有远程 - 如果您正在一个 fork 或其他使用多个远程的用例中工作,则这很方便。


14

我认为这归结于个人喜好。

你想在推送更改之前隐藏愚蠢的错误吗?如果是这样,git pull --rebase 是完美的选择。它允许您稍后将提交压缩成几个(或一个)提交。如果您的(未推送的)历史记录中有合并操作,则稍后执行 git rebase 不那么容易。

就我个人而言,我不介意公开所有愚蠢的错误,所以我倾向于合并而不是变基。


16
请将此翻译成中文。仅返回翻译后的文本:注意,任何查看此问题的人:这个答案与“何时使用git pull --rebase?”的问题完全无关。 - therealklanni
4
@therealklanni 我编辑了答案,以便更清晰地表明它与问题的相关性(希望没有改变原意)。 - Paŭlo Ebermann
2
分享一个混乱和不结构化的工作日志并不是一项诚实的努力,这只是懒惰。通过让人们在你的开发和调试的兔子洞中追逐你,你浪费了他们的时间;给他们结果,而不是胡言乱语。 - krystah

10
这个场景描述了当你不能推送你的提交时,因为远程端的代码已经发生改变。这个Cody的解释很容易理解。我画了一张图来描述这个场景,并希望它有所帮助。如果我错了,请纠正我。

enter image description here


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