如果你将一个提交推送到服务器,然后在本地重写该提交(使用
git reset
,
git rebase
,
git filter-branch
或任何其他历史操作),然后将重写的提交推回服务器,那么任何已经拉取了该提交的人都会出现问题。这里有一个例子:假设你已经提交了A,并将其推送到服务器。
-*-*-A <-- 主分支
-*-*-A <-- 远程主分支
现在你决定按照上述方式重写A,重置并重新提交。请注意,这会留下一个悬空的提交A,因为它是不可达的,最终将被垃圾回收。
-*-*-A
\
A' <-- 主分支
-*-*-A <-- 远程主分支
如果有其他人(比如Fred)在你这样做时从服务器上拉取了主分支,他们将拥有对A的引用,可能会从A开始工作:
-*-*-A' <-- 主分支
-*-*-A <-- 远程主分支
-*-*-A-B <-- Fred/主分支
现在,如果你能够将A'推送到远程主分支,这将创建一个非快进式的提交,它的历史记录中没有A。因此,如果Fred再次尝试拉取,他将突然不得不合并,并重新引入A提交:
-*-*-A' <-- master
-*-*-A <-- origin/master
-*-*-A-B-\
\ * <-- fred/master
A'--/
如果Fred注意到这个问题,他可以进行一次变基(rebase),这将防止提交A再次出现。但他必须注意到这个问题,并记得这样做;如果有不止一个人拉取了提交A,那么他们都必须变基才能避免在树中获得额外的A提交。
因此,在拉取其他人的仓库时,通常不建议更改历史记录。但是,如果您知道没有其他人从该仓库拉取(例如,它是私有的仓库或者只有一个开发人员正在协调易于协调的项目),那么您可以通过运行以下命令强制更新:
git push -f
或者
git push origin +master
这两种方法都会忽略非快进式推送的检查,并将服务器上的内容更新为新的A'修订版,放弃A修订版,因此最终将被垃圾回收。
可能情况是,使用
receive.denyNonFastForwards
配置选项完全禁用了强制推送。该选项在共享存储库上默认启用。在这种情况下,如果你真的想强制推送,最好的选择是删除分支并重新创建它,使用
git push origin :master; git push origin master:master
。但是,
denyNonFastForwards
选项有其原因,如上所述;在共享存储库上,这意味着现在每个使用它的人都需要确保他们重新基于新历史记录。
在共享存储库上,通常最好只推送新提交来解决你遇到的任何问题;你可以使用
git revert
生成撤消先前提交更改的提交。