重新定位和重新定位推送提交的含义是什么?

114

经常有人说,你不应该重置已经推送过的提交记录。这是什么意思呢?

4个回答

248
要理解这个,我们需要了解一些关于git的工作方式。一个git仓库是一个树形结构,其中树的节点是提交(commit)。下面是一个非常简单仓库的示例: When you fork 它在主分支(master branch)上有四个提交(commit),每个提交(commit)都有一个ID(在这种情况下为a、b、c和d)。你会注意到d当前是主分支(master branch)的最新提交(commit)(或HEAD)。 enter image description here 这里,我们有两个分支:master和my-branch。你可以看到master和my-branch都包含提交(commit) a和b,但是然后它们开始分叉:master包含c和d,而my-branch包含e和f。b被称为my-branch相对于master的“合并基础”(merge base)--或者更常见的是,“基础”(base)。这是有道理的:你可以看到my-branch是基于以前版本的master。
因此,假设my-branch已经过时,你想将其更新到最新的master版本。换句话说,my-branch需要包含c和d。你可以进行合并,但这会导致分支包含奇怪的合并提交(commit),使得审查拉取请求(pull request)变得更加困难。相反,你可以进行变基(rebase)。
当你进行变基(rebase)时,git会找到你分支的基础(base)(在这种情况下为b),找到该基础(base)和HEAD之间的所有提交(commit)(在这种情况下为e和f),并将这些提交(commit)重新应用于你正在变基(rebase)到的分支的HEAD(在这种情况下为master)。Git实际上创建了新的提交(commit),代表你的更改在master的顶部是什么样子:在图表中,这些提交(commit)被称为e'和f'。Git不会擦除你以前的提交(commit):e和f保持不变,如果变基(rebase)出现问题,你可以立即恢复到以前的状态。当许多不同的人同时在一个项目上工作时,拉取请求可能会很快过时。 "过时"的拉取请求是指其与主要开发线路不再保持最新状态,并且在合并到项目之前需要进行更新。 拉取请求过时的最常见原因是由于冲突:如果两个拉取请求都修改了同一文件中的类似行,并且一个拉取请求得到合并,那么未合并的拉取请求现在将有冲突。 有时,即使没有冲突,也会出现拉取请求过时的情况:也许代码库中不同文件的更改需要相应地更改您的拉取请求以符合新架构,或者分支是在某人意外地将失败的单元测试合并到主分支时创建的。 不管原因如何,如果您的拉取请求变得过时,您需要将您的分支重新基于主分支的最新版本才能合并。

82

ProGit 书籍 中有一个很好的解释

关于你的问题,可以在标题为 "The Perils of Rebasing" 的部分找到具体答案。以下是该部分的引用:

当你重置工作时,你是在放弃现有的提交记录,并创建新的类似但不同的提交记录。如果你将提交记录推送到某个地方并且其他人将其拉下来并在其基础上工作,然后你使用 git rebase 重写这些提交记录并再次将其推送上去,你的协作者将需要重新合并他们的工作,当你尝试将他们的工作拉回到你的工作中时,事情会变得混乱。

更新:
根据您下面的评论,看起来您在 Git 工作流程方面遇到了麻烦。以下是一些可能有所帮助的参考资料:


谢谢解释。为了让我更清楚地理解,我在推送某些更改后,不应该使用 git rebase(--interactive?)来重写历史记录,这是失败的必然结果。另一方面,如果我已经将某些更改重新定位到主题分支(从分支X),并将其推送,那么在另一个开发人员更改主题分支后再次重新定位就是完全正常的。基本上,我已经使用了“合并”相当长的时间,但我们和http://darwinweb.net/articles/86处于同样的境地,历史记录几乎无法使用。 - Hemant Kumar
@Hemant:在将提交推送到公共仓库后重新设置提交基本上是一个不好的想法。话虽如此,如果您的工作流程类似于他们的工作流程,那么您引用的darwinweb文章中的建议听起来是合理的。请查看我的更新响应,其中列出了其他可能有所帮助的参考资料。 - Tim Henigan
请将“ProGit”页面关于“私有托管团队”的链接更新为http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Private-Small-Team。 - Eimantas
所以从技术上讲,Git 提交保持不变,但是“放弃现有提交并创建类似但不同的新提交”只是具有不同 sha1 ID 的相同提交?好吧,这将是我能想到的唯一明显的原因,为什么协作者必须重新合并他们自己的工作! - Ciasto piekarz
@Ciastopiekarz,这是因为您正在重写上游存储库中的历史记录,其他开发人员的本地存储库可能会指向它。现在他们的指针已经过时:git客户端别无选择,只能使用旧的指针并依靠人工来解决剩下的问题。这是重新合并,可能非常混乱,需要手动排序许多令人困惑的更改!因此,建议永远不要对已经推送到上游存储库的任何内容进行变基。这是一个好建议,不应该被忽视,除非您是具有深入知识的专家。 - Forbin

72

重新定位基底会重写历史记录。如果没有人知道那段历史,那就没问题了。然而,如果那段历史是公开的,那么在Git中重写历史记录的方式就和在现实世界中一样:你需要一个阴谋。

阴谋真的很难维持下去,所以最好一开始就避免在公共分支上重新定位基底。

请注意,还有一些成功的阴谋案例:Junio C. Hamano的git存储库(Git SCM的官方存储库)中的pu分支经常被重新定位基底。这项工作的方式是,几乎每个使用pu的人都已订阅了Git开发者邮件列表,并且pu分支被重新定位基底的事实在邮件列表和Git网站上广泛宣传。


4
我认为git.git的pu分支是如何在(public)工作流中使用rebase的一个非常有用的例子。对于那些不熟悉它的人,一般的想法是重新设置主题分支(topic branches),这些分支没有任何在next(即将合并到主分支之前的不稳定分支)中的提交,然后通过重置到next,再将所有主题分支合并来重建pu分支。(来源:Documentation/howto/maintain-git.txt http://git.kernel.org/?p=git/git.git;a=blob;f=Documentation/howto/maintain-git.txt;h=d527b307707c676e82a08f18cb9fdd7d3abcb228;hb=HEAD) - Cascabel
25
“在 Git 中重写历史的方法就像现实世界一样:你需要一个阴谋。” 需要翻译成中文。 - Sleeper Smith
“不需要公开宣布,因为pu是一个一次性的分支,如上所述。” https://git-scm.com/docs/gitworkflows 我们在工作中也做类似的事情,“TEMP-something-latest”是一个一次性的分支,它是最新更改的组合,可以合并多个特性分支,随时可以被删除和重新创建,并且不应该进行开发。 - pilkch

7

Rebase(变基)会改变您的存储库历史记录。如果您将提交推送到世界上,即使它们对其他人可用,然后更改提交历史记录的视图,与拥有旧历史记录的任何人一起工作将变得困难。

Rebase considered harmful 是一个很好的概述,我认为。


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