git push中的non-fast-forward问题 

[不指定 2012/04/16 13:50]

 大 | 中 | 小 

    今天在回滚一个git操作记录时(使用了git reset --hard)遇到了问题,在push回服务器时提示:

error: failed to push some refs to 'qiuxueda@bb-iis-dev01.vm:code/comlogsvr-proxy'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

    看了一下文档:
    原文:
NOTE ABOUT FAST-FORWARDS
       When an update changes a branch (or more in general, a ref) that used to point at commit A to point at another commit B, it is called a fast-forward update if and only if B is a descendant of A.

       In a fast-forward update from A to B, the set of commits that the original commit A built on top of is a subset of the commits the new commit B builds on top of. Hence, it does not lose any
       history.

       In contrast, a non-fast-forward update will lose history. For example, suppose you and somebody else started at the same commit X, and you built a history leading to commit B while the other
       person built a history leading to commit A. The history looks like this:

                 B
                /
            ---X---A

       Further suppose that the other person already pushed changes leading to A back to the original repository you two obtained the original commit X.

       The push done by the other person updated the branch that used to point at commit X to point at commit A. It is a fast-forward.

       But if you try to push, you will attempt to update the branch (that now points at A) with commit B. This does not fast-forward. If you did so, the changes introduced by commit A will be lost,
       because everybody will now start building on top of B.

       The command by default does not allow an update that is not a fast-forward to prevent such loss of history.

       If you do not want to lose your work (history from X to B) nor the work by the other person (history from X to A), you would need to first fetch the history from the repository, create a history
       that contains changes done by both parties, and push the result back.

       You can perform "git pull", resolve potential conflicts, and "git push" the result. A "git pull" will create a merge commit C between commits A and B.

                 B---C
                /   /
            ---X---A

       Updating A with the resulting merge commit will fast-forward and your push will be accepted.

       Alternatively, you can rebase your change between X and B on top of A, with "git pull --rebase", and push the result back. The rebase will create a new commit D that builds the change between X
       and B on top of A.

                 B   D
                /   /
            ---X---A

       Again, updating A with this commit will fast-forward and your push will be accepted.

       There is another common situation where you may encounter non-fast-forward rejection when you try to push, and it is possible even when you are pushing into a repository nobody else pushes into.
       After you push commit A yourself (in the first picture in this section), replace it with "git commit --amend" to produce commit B, and you try to push it out, because forgot that you have pushed
       A out already. In such a case, and only if you are certain that nobody in the meantime fetched your earlier commit A (and started building on top of it), you can run "git push --force" to
       overwrite it. In other words, "git push --force" is a method reserved for a case where you do mean to lose history.

      我的翻译版(不是严格依照原文,按自己理解的意思复述):
关于 FAST-FORWARDS
       当修改一个branch(或ref)时,在a是b的直接基线或祖先基线的情况下,将HEAD指针从a移动为b叫做fast-forwards

       在这种情况下,因为不会丢失任何历史数据,所以叫做fast-forward

       但是,对于non-fast-forward就会丢失历史数据,设想你和另一个人同时以x为基线开发,你开发了一个叫b的commit,而那个人开发了一个叫a的commit:

                 B
                /
            ---X---A
       如果那个人已经将a提交到了服务端,现在服务端的head指向了a,如果你试图去提交b,那么服务端会试图将head从a移动到b上,如此一来,就会丢失a的修改内容,这就是non-fast-forward。
       所以默认情况下不允许这种提交,以防止数据丢失

       如果你不想丢失你的和别人的工作成果,那么需要先从服务端获取其他人的修改,生成一个包含你的和其他人修改的commit,然后将其提交到服务器。
      你可以先运行git pull,然后解决合并冲突,然后git push。git pull会基于a和b生成一个c记录,同时包含两者的修改内容

                 B---C
                /   /
            ---X---A

       这样提交c请求就变成了fast-forward请求,从而被允许。

       同样的,你可以在a基础上添加从x到b的这些请求,使用git pull --rebase并push结果到服务器,这样会生成一个commit:d,在a的基础上添加了从x到b的修改

                 B   D
                /   /
            ---X---A

       这也是一个fast-forward请求,是被允许的。

       (这一段不是特别理解。。像是废话,因为跟上图第一种情况看起来是一回事)还有一种情况,即使没有其他人向版本库推送过数据,你也可能遇到non-fast-forward的情况:
       当你推送a到服务端后,又使用了git commit --amend 修改a为b,然后可能忘记已经推送过a,于是试图去推送b到版本库。这样的话,当你确认没有人fetch过a的话,可以使用git push --force去覆盖这个记录。
        也就是说,当你确认你确实需要丢失历史数据时,可以使用git push --force来强制推送




         在gitolite中,对于每个版本库的授权就有“RW+”字段,其中的“+”权限,就是强制推送的权限,由于可能会导致历史提交丢失,所以是比W更高级的权限,需要单独授予。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐