Matthieu Moy <Matthieu.Moy@xxxxxxx> writes: > 'git push' failing because of non-fast forward is a very common situation, > and a beginner does not necessarily understand "fast forward" immediately. > > Signed-off-by: Matthieu Moy <Matthieu.Moy@xxxxxxx> > --- > That may be a bit verbose, but I think it's worth it. > ... > + if (nonfastforward) { > + printf("Some branch push were rejected due to non-fast forward:\n"); > + printf("Merge the remote changes (git pull) before pushing your's\n"); > + printf("or use git push --force to discard the remote changes.\n"); > + } Although I think the patch identified the right place to make changes, I am not sure about what the help message should say. If the user lacks understanding of what a fast-forward is, I do not think giving two choices that would produce vastly different results (because they are applicable in vastly different situations) would help such a user very much, as the understanding of the concept of fast-forward is a must to correctly decide which one to use. Unfortunately, I do not think we have a good description of fast-forward in our documentation set. The glossary defines what it is, git-push manual page says by default only fast-forwards are accepted, and user-manual says that we do not create a merge commit in a fast-forward situation. But nobody talks about _why_ a non-fast-forward update (either push or fetch) is a bad idea clearly. I wrote that in my upcoming book so we could refer to it in this error message, but I suspect it may not help most people since it is in Japanese ;-) Jokes aside, perhaps we could add "see git-push documentation for details" to the above message of yours, and add something like this to the documentation. Documentation/git-push.txt | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 75 insertions(+), 0 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 2653388..c1ae82d 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -195,6 +195,81 @@ reason:: refs, no explanation is needed. For a failed ref, the reason for failure is described. +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. + + Examples -------- -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html