我有一个裸git存储库B,我从中克隆了两个存储库C和D.然后我添加了一些更改为C,后来被推送到B,最后由D拉。所以一切都是同步的。
现在我想从C中删除最后一次推送的提交,所以我这样做:
$ git reset HEAD^ --hard
$ git push -f
(来自http://christoph.ruegg.name/blog/git-howto-revert-a-commit-already-pushed-to-a-remote-reposit.html)
在D我做:
$ git pull
我输出如下:
[mkm@horklum git1]$ git pull
From /home/mkm/projects/git_tests/git1
+ a5d681f...c481973 master -> origin/master (forced update)
Already up-to-date.
和git log
给了我与之前完全相同的输出。我希望D上的历史与C上的历史相同。我从What and where does one potentially lose stuff when git says "forced update"?知道最后git pull
将我的历史与服务器上已更改的历史合并,但它真的会发生什么?我希望历史在任何地方都能保持同步。
我知道我可以做git revert提交,这是在这样的场景中推荐的方法,但我想理解为什么在强制推送的情况下,历史不会在任何地方保持同步。
答案 0 :(得分:3)
让我们尝试绘制存储库的历史记录(更高版本更旧):
commit 1 --> o <-- the initial commit
|
...
|
commit N-1 --> o <-- B and C are here now
|
commit N --> o <-- D is here
您在N
上创建了一些提交(最多提交C
),从B
推送到D
。所有三个repos都与指向commit master
的{{1}}分支同步。 (如果您的分支未命名为N
,则只需将其名称改为名称并继续阅读。
然后你强迫master
的{{1}}分支返回提交master
并检查出来(这是C
的作用。)
此外,您强制N-1
的{{1}}分支返回提交git reset --hard
。这就是master
在这种情况下的作用。
现在,提交B
在N-1
和git push -f
存储库中不再存在。 1 就像您创建了提交一样N
上的B
.. C
,将其推送到1
,将其从N-1
(C
,B
和D
同步)然后您在B
上创建了提交C
。从未在D
和N
上创建提交D
。
然后您在N
上运行B
并且没有任何变化。这是因为在后台C
运行git pull
后跟D
。
git pull
从远程存储库(git fetch
)检索本地存储库中尚未存在的所有可执行提交。在这种情况下没有; git merge
在git fetch
上有一部分提交。它还了解B
上分支的当前位置。
B
发现本地仓库(D
)是远程仓库(B
)之前的一次提交,它无关。
为了使git merge
看起来像D
,您可以在B
上运行D
然后B
。它将强制移动git fetch
的{{1}}分支git reset --hard B/master
D
分支master
然后将检查出来。它基本上执行了D
对master
所做的事情。
1 这不完全正确。提交仍然存在,但无法使用分支访问。这使得它成为将在下一个垃圾收集中删除的孤立提交。虽然它仍然存在于存储库中,但可以使用其哈希来访问它,并且可以通过创建指向它的分支来恢复它。