1. 程式人生 > >git push中的non-fast-forward問題

git push中的non-fast-forward問題

git push中的non-fast-forward問題 

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

  |  |  

    今天在回滾一個git操作記錄時(使用了git reset --hard)遇到了問題,在push回伺服器時提示:

error: failed to push some refs to '[email protected]: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更高階的許可權,需要單獨授予。