我经常不得不使用git reset
并git rebase
更改一些最近的提交(重新排列,将多个提交合并为一个,...)。这需要在推送时使用 force 选项。不幸的是,这也可能会覆盖远程存储库中其他用户的更改。
为什么我需要重写历史?因为我们在功能分支中做了大量工作(具体而言,从使用 Swing 转换为 SWT)并且必须使其尽可能短。为了实现这一点,我们经常发现,一些提交应该在功能分支被分叉之前提交给主节点。
git push
如果我没有从远程存储库中获取最新更改,如何使强制失败?
如果其他人在存储库中看到了历史提交,而您稍后又去重写它们,那么您将遇到问题。这只是 git 设计的一部分。
git 无法真正区分您已重写提交但拥有最新的存储库和您没有重写提交但必须在推送之前合并提交。检测最新推送的方法是简单地检查目标 ref 中的每个提交是否在您正在推送的 ref 的历史记录中;这被称为快进。当你变基时,你从本地分支中删除提交,并创建新的、类似的提交。git 无法确定这些新提交是否等于历史记录中的旧提交,将其视为未决合并,并且需要强制覆盖它并覆盖目标存储库中已有的任何内容。
所以如果你强制,你会覆盖目的地中的任何内容。这是设计使然。此外,从您的存储库中提取的其他人也会因此而看到问题。假设您翻转了存储库中的提交顺序:
A-------B-------C oldmaster
\
------C'------B' master
如果其他用户在本地拥有 oldmaster 的副本,并从 master 中提取,git 将尝试合并这两个分支。C'
这将在和中双重应用补丁B'
。不好。Git有时可以在合并时检测到这些问题,但即使它正确合并,您也会留下:
A-------B-------C------D master
\ /
------C'------B'----
现在你的重新定位的提交已经从死里复活了。
在那里使用 rebase 也无济于事;你也可以得到:
A------B------C------C'------B'
在像这样的简单情况下,git 可能能够检测到双重应用的补丁并删除它们,但不能保证。
所以总而言之:不要对其他人可见的提交进行变基。当它仍然是您在本地保留的私有分支时,或者当它是带有严重警告的公共分支时,请执行所有您想要的变基,“开发分支 - 可能会变基 - 不要在此分支上建立工作”。但是,当其他人在此基础上进行建设时,不要再胡闹历史了。
如果您绝对必须一直与多个用户混淆历史,您可能会发现使用StGIT、guilt、topgit或类似方法将功能分支作为一包补丁使用会更好。然后,您可以将它们作为补丁使用,对补丁重新排序(但不要更改提交本身!),然后当需要将更改推送到上游时,将补丁线性化到原始上游分支之上。
我不知道对此有一个简单的答案,尽管您可以使用 repo 上的钩子来发挥一些魔力。然而,在多用户设置中,强制更新通常不是一个好主意。我怀疑更好的答案是改变你使用 git 的方式。
首先,你真的需要改写历史吗?大多数时候,正确的答案是保持历史不变,只添加新的提交。
如果你真的认为你必须重新安排提交(比如说为了满足编码标准?)并且你不能教育做出这些提交的人第一次就正确,你是否考虑过不同的设置?拥有一个中央“祝福”存储库,但只允许发布工程师(或集成工程师或团队或任何你想称呼他们的人)推送到它。普通用户在其他地方(通常在他们自己的工作站上)发布 repos,而不是推送,他们要求发布工程师从中提取。然后,发布工程师审查更改,根据需要重新排列和合并它们,运行任何测试或其他必需的步骤,并将结果推送到祝福的存储库。