`git push --force`的其他后果是什么?

23
关于强制执行git push的问题和答案并不难找到,(这里有两个)。(链接1, 链接2)。标准答案大概是这样的:
引用:

如果你必须要强制执行git push,技术上你可以使用--force选项,但在程序上你不应该这么做,因为有人可能已经拉取了代码,并且某个地方会有一只小猫死去。

我认为这是一个明智的建议-更安全的方法是重新提交,修复你犯的错误。但是,比如说我知道(神奇地)没有人拉取过这些提交。或者更好的是,这是一个私有仓库,所以没有危及到其他人克隆的风险。
那么--force推送是否还有其他[负面]影响,特别是技术影响?也许它会在远程产生一些额外的垃圾,或者它会破坏X公司分析工具的1.2.3版本,或者它会使后续的rebase更加混乱,等等。有吗?
修改1:我有自己的经验证明,在私有仓库上强制执行--force推送似乎并没有引起任何问题。但我不关注感知,我在寻找相关证据和/或证明。
3个回答

18
补充 torek 的优秀答案: 强制推送可能会导致以后的合并出现问题。
问题在于:
如果你强制推送分支 A,那么你将从该分支中删除一些现有提交(否则你就不需要强制推送了)。如果(如 torek 的答案所述)这些提交也被另一个分支 B 引用,那么它们仍然存在于那个分支中。如果之后合并两个分支 A 和 B,A 将包含“新”的(强制)提交,而 B 包含“旧”的提交。
在这种情况下,git merge 可能不会按你想要的方式执行操作。最好的情况是会产生合并冲突。最坏的情况是即使没有合并冲突,结果仍然会是错误的。例如,如果你使用 git rebase -i 从 A 中删除了提交 c1,如果 c1 也在 B 中,那么在合并 A 和 B 时它将被重新引入。这可能会悄悄地重新引入一个错误 :-(。
因此,如果你要强制推送,请确保你删除的提交没有被其他分支/标签引用(或者也要强制推送这些分支/标签)。

已删除提交“返回”的示例

运行下面的 bash 脚本。该脚本创建一个 git 仓库,并分别在 master 和 branch1 上创建以下提交:
  • 好的提交(branch1)
  • 坏的提交(master)
  • 初始提交
然后它:
  • 使用 git reset --hard 从主分支中丢弃“坏的提交”
  • 将 branch1 合并到主分支中
脚本输出:
Initialized empty Git repository in ....
[... creating commits ...]
### Bad commit in history:
* 7549dcc (HEAD, master) bad commit
* 31afec8 Initial
### Removing bad commit:
HEAD is now at 31afec8 Initial
### Bad commit no longer in history:
31afec8 (HEAD, master) Initial
### Merging branch1:
Updating 31afec8..be801e5
Fast-forward
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b
 create mode 100644 c
### Bad commit is back!
be801e5 (HEAD, master, branch1) good commit
7549dcc bad commit
31afec8 Initial

如您所见,使用git reset --hard从主分支中删除了“坏提交”。

然而,“坏提交”仍包含在branch1中,因此当branch1合并到主分支时,“坏提交”会重新出现。

注意:这个例子实际上没有强制推送,但如果您使用的是上游代码库,您需要在删除错误提交后执行强制推送 - 结果将是相同的。

脚本:

#!/bin/bash
set -e
if [ -e gitrepo ]; then echo "Please remove gitrepo"; exit 1; fi

git init gitrepo; cd gitrepo; git config core.pager cat
touch a; git add a; git commit -m Initial
touch b; git add b; git commit -m "bad commit"
git checkout -b branch1; touch c; git add c; git commit -m "good commit"
git checkout master

echo "### Bad commit in history: "
git log --oneline --decorate

echo "### Removing bad commit: "
git reset --hard HEAD~1

echo "### Bad commit no longer in history: "
git log --oneline --decorate

echo "### Merging branch1: "
git merge branch1

echo "### Bad commit is back!"
git log --oneline --decorate

合并通常一次只查看三个文件:来自合并基础的版本、来自HEAD的版本和正在合并的版本。因此,如果合并分支的末端仍然可以干净地合并,则是“安全”的。基本上,为了使合并工作,您需要修剪掉任何不需要的“尾部”提交。对于大而复杂的合并,您可能需要使用像git-imerge这样的工具,然后您将希望整个链都是“干净”的(在这里,git 1.9的新代码可能会有所帮助)。 - torek
2
@torek:正如我所解释的,如果分支合并干净,你并不安全。如果你使用 rebase -i / reset 删除了一个提交,如果它也在分支中,它将会回来,这可能不是你想要的。 - sleske
啊,我明白你的意思了:提示提交可能仍然包含一些或全部不需要的早期提交。是的,同意。 - torek
很遗憾,我只能接受一个答案 - 非常感谢您提供的额外细节! - Chris Tonkinson

16

force-push指的是即使移动不是快进操作,也会告诉远程仓库移动给定的标签。 1非快进可能(但不一定)会导致“废弃”提交:这些提交不再可以从某个引用访问。

(当标签运动发生在其他地方引用的图的一部分时,它不会这样做。例如,如果分支xyzzy指向序列中的提交D

A-B-C-E  <-- plugh
   \ /
    D    <-- xyzzy
如果标签 xyzzy 发生非快速转发式的移动,如指向提交 C,那么与标签 plugh 关联的提交 D 不会受到影响。同样,完全删除标签 xyzzy 也是无害的,至少就提交图结构而言。(关于远程裸仓库(一般情况下是推送到该仓库),它们通常不记录所有的引用更新信息,因此这往往会引发废弃提交的快速垃圾回收。如果你在自己的仓库中保留了这些提交,你可以在以后恢复gc的提交,但这需要再次通过网络发送数据,并且如果你自己的仓库出现问题,例如由于停电或计算机着火导致损坏,则存在数据丢失的风险。)如果某个第三方软件假定(某些或全部)分支标签只以快速转发方式移动,则该软件可能会出现意想不到的错误。我不知道是否有这样的软件,如果有我会称之为“损坏的”,但人们似乎经常编写有缺陷的代码,然后依赖它。(注:旧版本的 Git 允许标签进行快速转发的移动而不需要强制推送,就像分支标签预期的移动方式一样。新版本(我认为是在1.8.2中引入的,但这只是从记忆中得出的)拒绝任何标签的移动,除非使用 force。当然,您始终可以删除,然后重新创建(可能在不同的点),一个标签,并且内置挂钩对此没有问题。因此,即使没有强制推送,也有其他方法以任意方式移动标签或分支。只需确保您希望保留的提交图部分具有每个操作的某些标签即可。)
至于让自己以后更难进行变基:是的,它可能会产生这种影响,因为您可能会从“台式机”进行强制推送,因此实际上会重新排列“共享裸仓库”。稍后,在“笔记本电脑”上,您可能无法记住要变基的确切提交,并且找到它可能有点棘手,特别是在大型、高活跃度的存储库中。
幸运的是,新发布的 git 版本具有一种新功能,它使用您的 reflog 来确定是否已发生“上游变基”的情况。也就是说,在上述情况下,您将能够(在“笔记本电脑”上)要求 git 自动发现在共享裸仓库中已经变基的提交,并在此基础上“重新变基”您笔记本电脑工作。 (这是对今天在 git 1.8.x 中使用的 git pull --rebase 方法的概括。)

2
+1. 关于“新即将发布的Git版本有一个新功能,可以使用您的reflogs来确定何时以及是否发生了“上游rebase”,它是fork_point=$(git merge-base --fork-point origin/master topic); git rebase --onto origin/master $fork_point topic。请参见https://dev59.com/D2855IYBdhLWcg3w6IzV#20423029。我迫不及待地等待这个功能的推出 :) - VonC
1
谢谢!这正是我在寻找的详细信息。 - Chris Tonkinson

1
我经常在我的私有仓库上使用--force,没有出现任何负面影响,因为我知道什么时候强制更改以及对我的代码的影响。唯一的问题是--force会更改上游仓库的历史记录,而在历史记录被更改之前,可能已经有人对仓库的状态进行了更改。这会导致问题-这就是为什么它不是推荐的解决方案的原因。但即使是这样-这些问题也可以解决,只需要花费一些工作量。因此,回答这个问题-如果你只想在私有仓库中与自己共享代码,并且没有其他人会受到这些更改的影响,那么就没有其他副作用。

我在想,也许我的原始问题不够清晰,所以我进行了编辑。 - Chris Tonkinson

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