补充 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 ...]
* 7549dcc (HEAD, master) bad commit
* 31afec8 Initial
HEAD is now at 31afec8 Initial
31afec8 (HEAD, master) Initial
Updating 31afec8..be801e5
Fast-forward
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 b
create mode 100644 c
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的新代码可能会有所帮助)。 - torekrebase -i
/reset
删除了一个提交,如果它也在分支中,它将会回来,这可能不是你想要的。 - sleske