又是一个 GIT 问题

2014-06-06 19:14:06 +08:00
 yueyoum
本地仓库A 中心仓库B 远端仓库C

流程就是 A 开发好, push 到 B, C 再 从B pull 下来。

但 A 不小心删了个文件,然后就 commit push 到B了,
此时 A B 的 commit log 大概是这样的,

aaaaaaa
|
bbbbbbb


aaaaaa 就是这个最新的commit

发现不对, 在A执行了 git reset HEAD^ , 然后把误删除的文件checkout 回来,再commit

此时push 就提示和B冲突了, 这个很好理解

但我 git push -f ,强制推送了

A B 的 commit log 成了这样

ccccccc
|
bbbbbbb

因为 aaaa 的commit 被reset 并且 强制推送覆盖了。


但现在到 C来操作, 从 B pull 下来的 commit log 是

aaaaaaa
|
bbbbbbb

并且 提示 此时的状态和B 是 already up-to-date

但 C 中还是 文件被删除的状态……
3867 次点击
所在节点    程序员
9 条回复
delo
2014-06-06 19:28:33 +08:00
C处clone一份是怎样的?一般push到中心仓库的commit就不要再改动了吧,宁愿重新把文件手动加回来再commit一次。看看大家有啥好办法
wwqgtxx
2014-06-06 19:30:55 +08:00
强制覆盖push貌似是不能直接pull更新的吧
你可以试试在C端重新clone一遍
czheo
2014-06-06 19:56:27 +08:00
push到中心的commit不应该被删除,你应该在aaaa的基础上添加回被删除的文件,然后做新的commit push上去
xxxx <==添加回被删文件
|
aaaa
|
bbbb
123123
2014-06-06 20:08:23 +08:00
如果只有两个人用仓库还好。
B 只要 fetch 一下,然后直接 reset hard 到最新节点就好了
xcatliu
2014-06-06 20:11:44 +08:00
C 直接重新 clone 吧。。

别用 force push 了,要回退就 revert 再提交
skydiver
2014-06-07 00:06:48 +08:00
@123123 应该是 C 只要 fetch 一下,然后直接 reset hard 到最新节点就好了
neevek
2014-06-07 01:18:32 +08:00
根据你的描述不应该会出现aleady up-to-date。

如果B的log是:
ccccccc
|
bbbbbbb

C的log是:
aaaaaaa
|
bbbbbbb

意味着这时候B和C的common ancestor是bbbbbbb,也就是当C从B pull的时候不是fast-forward,原来A提交的aaaaaaa会被当成是C提交的,换言之,文件(假设文件名为file.txt)是在C中被删除的,A没有对file.txt做任何修改。这时候merge B和C最终会导致file.txt文件被删除,并且会产生一个merge commit,这个时候C的log应该是这样的:
Merge commit
|
ccccccc
|
aaaaaaa
|
bbbbbbb

楼主没有直接问问题,我假设楼主希望C跟B合并之后,C中的file.txt没被删除。假设当前在C,这时候需要执行以下命令(执行命令之前,如果你已经执行过git merge或者不带任何参数的git pull,那请回退到合并之前的历史,如:git reset --hard HEAD@{1}):

注:假设upstream是origin,分支名是master

git pull --rebase origin master
或者
git fetch origin master
git rebase --onto origin/master HEAD~0 master

上面这两组命令在这个场景下是等价的,但是用下面这一组命令来解释会更加容易理解。
这里第一个参数origin/master表示我想基于origin/master来replay当前分支的commit,这个时候origin/master其实就是B。HEAD~0实际上等价于HEAD,写成HEAD~0只是为了强调我只有当前分支最新1个commit(这个commit就是aaaaaaa)需要replay到origin/master上。最后的master表示rebase的结果是应用到master分支上的,最终的结果就是file.txt回来了,commit历史会变成:
ccccccc
|
bbbbbbb

没错,aaaaaaa不见了,因为rebase origin/master(等价于based on B中的ccccccc)实际上就是拿C中的aaaaaaa和B中的ccccccc做比较,最终发现ccccccc中增加了file.txt,所以保留了file.txt,所以相当于aaaaaaa这个提交神马都没做,所以这个commit就被去除了。

另外,假设在rebase B之前,在C上面做过一次提交,那上面的HEAD~0改成HEAD~1就可以了。


oh, my god....好长
neevek
2014-06-07 09:44:47 +08:00
发现不能编辑回复。。。只能再写一个回复更正上一个回复了。

前面关于HEAD~0的描述是错误的(最后3段),HEAD~0在这里实际上exclusive的,也就是HEAD~N往回数N个commit(这里HEAD~0就是0个commit,即完全忽略),然后对这些commit生成patch,replay到origin/master上,所以下面这个命令的意思是:完全忽略在C上面的从HEAD数到与B的common ancestor的位置的所有commit,楼主的情况就只有那个删除file.txt的commit,然后replay到origin/master上面,最终结果就是把B那个checkout回file.txt的commit合并回来了,丢掉已在C上面的删除file.txt的commit。

git rebase --onto origin/master HEAD~0 master
undeadking
2014-06-08 01:01:59 +08:00
在多人协作的情况下还用git push -f ,纯粹作死,会把其他人都害了的.

让远端回滚到强制push之前吧,冲突必须要解决,别想偷懒

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/116146

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX