<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Snooda]]></title> 
<link>http://www.snooda.com/index</link> 
<description><![CDATA[Snooda's Blog]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Snooda]]></copyright>
<item>
<link>http://www.snooda.com/read/</link>
<title><![CDATA[git push中的non-fast-forward问题]]></title> 
<author>snooda &lt;admin@snooda.com&gt;</author>
<category><![CDATA[linux操作系统]]></category>
<pubDate>Mon, 16 Apr 2012 05:50:13 +0000</pubDate> 
<guid>http://www.snooda.com/read/</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;今天在回滚一个git操作记录时（使用了git reset --hard)遇到了问题，在push回服务器时提示：<br/><br/>error: failed to push some refs to 'qiuxueda@bb-iis-dev01.vm:code/comlogsvr-proxy'<br/>To prevent you from losing history, non-fast-forward updates were rejected<br/>Merge the remote changes (e.g. 'git pull') before pushing again.&nbsp;&nbsp;See the<br/>'Note about fast-forwards' section of 'git push --help' for details.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;看了一下文档：<br/>&nbsp;&nbsp;&nbsp;&nbsp;原文：<br/>NOTE ABOUT FAST-FORWARDS<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; history.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; person built a history leading to commit A. The history looks like this:<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Further suppose that the other person already pushed changes leading to A back to the original repository you two obtained the original commit X.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; because everybody will now start building on top of B.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The command by default does not allow an update that is not a fast-forward to prevent such loss of history.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; that contains changes done by both parties, and push the result back.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B---C<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp; /<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Updating A with the resulting merge commit will fast-forward and your push will be accepted.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and B on top of A.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B&nbsp;&nbsp; D<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp; /<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Again, updating A with this commit will fast-forward and your push will be accepted.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; overwrite it. In other words, "git push --force" is a method reserved for a case where you do mean to lose history.<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我的翻译版（不是严格依照原文，按自己理解的意思复述）：<br/>关于 FAST-FORWARDS<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当修改一个branch（或ref）时，在a是b的直接基线或祖先基线的情况下，将HEAD指针从a移动为b叫做fast-forwards<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这种情况下，因为不会丢失任何历史数据，所以叫做fast-forward<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是，对于non-fast-forward就会丢失历史数据，设想你和另一个人同时以x为基线开发，你开发了一个叫b的commit，而那个人开发了一个叫a的commit：<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果那个人已经将a提交到了服务端，现在服务端的head指向了a，如果你试图去提交b，那么服务端会试图将head从a移动到b上，如此一来，就会丢失a的修改内容，这就是non-fast-forward。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所以默认情况下不允许这种提交，以防止数据丢失<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你不想丢失你的和别人的工作成果，那么需要先从服务端获取其他人的修改，生成一个包含你的和其他人修改的commit，然后将其提交到服务器。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你可以先运行git pull，然后解决合并冲突，然后git push。git pull会基于a和b生成一个c记录，同时包含两者的修改内容<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B---C<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp; /<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样提交c请求就变成了fast-forward请求，从而被允许。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同样的，你可以在a基础上添加从x到b的这些请求，使用git pull --rebase并push结果到服务器，这样会生成一个commit：d，在a的基础上添加了从x到b的修改<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B&nbsp;&nbsp; D<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/&nbsp;&nbsp; /<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---X---A<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这也是一个fast-forward请求，是被允许的。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; （这一段不是特别理解。。像是废话，因为跟上图第一种情况看起来是一回事）还有一种情况，即使没有其他人向版本库推送过数据，你也可能遇到non-fast-forward的情况：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当你推送a到服务端后，又使用了git commit --amend 修改a为b，然后可能忘记已经推送过a，于是试图去推送b到版本库。这样的话，当你确认没有人fetch过a的话，可以使用git push --force去覆盖这个记录。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;也就是说，当你确认你确实需要丢失历史数据时，可以使用git push --force来强制推送<br/><br/><br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在gitolite中，对于每个版本库的授权就有“RW+”字段，其中的“+”权限，就是强制推送的权限，由于可能会导致历史提交丢失，所以是比W更高级的权限，需要单独授予。<br/><br/><br/><br/><br/><br/>Tags - <a href="http://www.snooda.com/tags/git/" rel="tag">git</a> , <a href="http://www.snooda.com/tags/fast-forward/" rel="tag">fast-forward</a> , <a href="http://www.snooda.com/tags/gitolite/" rel="tag">gitolite</a>
]]>
</description>
</item><item>
<link>http://www.snooda.com/read/#blogcomment</link>
<title><![CDATA[[评论] git push中的non-fast-forward问题]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.snooda.com/read/#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>