V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yueyoum
V2EX  ›  程序员

又是一个 GIT 问题

  •  
  •   yueyoum ·
    yueyoum · 2014-06-06 19:14:06 +08:00 · 3867 次点击
    这是一个创建于 3821 天前的主题,其中的信息可能已经有所发展或是发生改变。
    本地仓库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 中还是 文件被删除的状态……
    9 条回复    2014-06-08 01:01:59 +08:00
    delo
        1
    delo  
       2014-06-06 19:28:33 +08:00 via iPhone
    C处clone一份是怎样的?一般push到中心仓库的commit就不要再改动了吧,宁愿重新把文件手动加回来再commit一次。看看大家有啥好办法
    wwqgtxx
        2
    wwqgtxx  
       2014-06-06 19:30:55 +08:00 via Android
    强制覆盖push貌似是不能直接pull更新的吧
    你可以试试在C端重新clone一遍
    czheo
        3
    czheo  
       2014-06-06 19:56:27 +08:00 via iPhone
    push到中心的commit不应该被删除,你应该在aaaa的基础上添加回被删除的文件,然后做新的commit push上去
    xxxx <==添加回被删文件
    |
    aaaa
    |
    bbbb
    123123
        4
    123123  
       2014-06-06 20:08:23 +08:00
    如果只有两个人用仓库还好。
    B 只要 fetch 一下,然后直接 reset hard 到最新节点就好了
    xcatliu
        5
    xcatliu  
       2014-06-06 20:11:44 +08:00
    C 直接重新 clone 吧。。

    别用 force push 了,要回退就 revert 再提交
    skydiver
        6
    skydiver  
       2014-06-07 00:06:48 +08:00
    @123123 应该是 C 只要 fetch 一下,然后直接 reset hard 到最新节点就好了
    neevek
        7
    neevek  
       2014-06-07 01:18:32 +08:00   ❤️ 1
    根据你的描述不应该会出现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
        8
    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
        9
    undeadking  
       2014-06-08 01:01:59 +08:00
    在多人协作的情况下还用git push -f ,纯粹作死,会把其他人都害了的.

    让远端回滚到强制push之前吧,冲突必须要解决,别想偷懒
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1053 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 19:30 · PVG 03:30 · LAX 11:30 · JFK 14:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.