What is the "git push problem: non fast forward"
This mini-note is primarily the answer to the question . Since my account is read-only, this is the answer method. “But life is getting better!” ©
The first conclusion after reading the question and answers - do not do as defuz suggested . He does not understand the essence of the problem, and if you do as suggested by him - most likely, you will lose data.
Second: alekciy is also not quite right, but here the chances of losing data are much less. Almost none.
And the third one: damn it, when will people understand that it’s really necessary to own the used tool? Read the documentation!
I will say right away that many of the details and “insides” of git’s work are omitted or simplified, but not to the detriment of understanding. It is the appearance of understanding and the purpose of the article, and if you already have it, and you are sure that it is true :) - then there will be nothing new for you here. If there is no understanding yet, then let's go!
First, find out what fast-forward is. This is just one of the options (methods, strategies) for performing the merge operation. It is possible only if the current commit (the one to which the merge will take place, denote it by A) is the ancestor of the merged commit (the one with which the merge will take place, we denote it by B). Graphically, it looks like this (x is just other commits that are not important to us):
If the commit history looks like this:
then you won’t be able to merge from A to B using the fast-forward method - A is not an ancestor of B. Similarly, in this situation, you cannot merge using the fast-forward method from B to A. But, any of the merges “from B” to A ”and“ from B to B ”- it’s possible, and when performing one of these merges, git (by default) will use the fast-forward method.
Now the next thing to understand is what push is. There are two key points in this operation. First: it’s not just the transfer of data about your commits “to the other side”, but also the obligatory subsequent operation merge “on the other side”. And the second: “that side” for the merge operation will use only fast-forward. Why is everything so?
Well, the first is obvious. To put something in the story, you need to create a new commit object in the story (which will be the descendant of the current one) and change the pointer to the last commit in the branch (called HEAD), to this new commit. By and large, there are only two operations for this: commit and merge (merge, which in most cases will lead to the appearance of a new commit). Obviously, no one commits manually on the server. And if you simply transfer your history (new commits) to the server, this will not change HEAD in any way. So the server is simply forced to do merge with your new commits - it just doesn’t have another option to change HEAD.
The second point is also not difficult, but not immediately obvious. Why is the server using merge using only fast-forward? After all, there isa bunch of other great methods, the same octopus , but their server does not use. Why? Everything is very simple: fast-forward is the only option that does not generate new commits (in addition to those that you have already added). After all, the server cannot generate new commits itself, since git requires the author to specify a commit - the server can only store your commits. And what is even more important - this method (fast-forward) during merging never gives conflicts: simply by the nature of the fast-forward method itself, conflicts during merging are not possible. This is important for the server, since in the event of a conflict there will be no one to solve it. That's why it is a conflict, so that it can be solved by a person, because the computer could not figure it out.
Understanding these basic things should be enough to “clarify” the situation. But how can it arise?
Everything is very simple: the repository is used by more than one person (or one, but on several machines), or a rebase was made in the local depository. In any of these options, the appearance of what is displayed in the second picture may occur. And since this situation has arisen for you (that is, you did a “git fetch”, and saw that everything is so), then let's agree that on the HEAD server it points to A (remotes / origin / master), to you locally - on B (master).
How to solve the problem? There are two options, and both of them lead to the fact that A will merge with such a commit (let's call it X), for which A will be the ancestor. How to achieve this?
Option One: Merge. You need to locally merge your code (B) and the one that is on the server (A). The merge will result in a new commit in your local history, the same X - and the push server will not refuse to make a push for it. For persuasiveness - picture:
Option Two: rebase. Rebase is the operation of "transferring" part of the story in such a way as to change the "root" of the branch, but not to change the branch itself. For clarity, the picture again. The initial state is the same as Figure 2, and the state after rebase will be like this:
In the result, the role of the commit X is played by B - but this is another B, let's say B '. This can be easily verified by looking at commit-id before rebase and after. But as already mentioned, the branch itself (that is, the contents of the commits that make it up) will not change - only commit-id will change. But the goal is achieved - you can do push.
Which option to choose is up to you. On the net, more than one pack of dogs was eaten about this. My recommendation for beginners is the first option, as it is simpler and less error prone.
There is a third solution, and it was defuz who proposed forced push, for example: It will cause the server to accept your commits, and of course
change its HEAD so that it points to the last of your commits - to B. Thus, the server will “forget” all “your” commits that you don’t have (from B to A). This is the very data loss that I talked about at the beginning. However, sometimes there are times when this is exactly the operation that you need. But if you have just such a case, and you understand this, then probably this article has not been needed for a long time.
Well, to put all the points above and - regarding the answer alekciy . Since its version does not use forced push, there will be no data loss. But it’s quite obvious that after merge, firstly, there’s nothing to put in stash (except for new files that are not yet monitored by git), and secondly, rebase is no longer needed (and if there are new files, it will break off) .
I hope this material clarified what is happening and will help solve the problem.
PS1: It’s very convenient to use “gitk -a” (linux) to view the history.
UPD: Terentich Windows also works fine.
UPD: eyeofhell And this also works on OSX :)
PS2: Stash (as well as many other operations) is a topic for another discussion.
The first conclusion after reading the question and answers - do not do as defuz suggested . He does not understand the essence of the problem, and if you do as suggested by him - most likely, you will lose data.
Second: alekciy is also not quite right, but here the chances of losing data are much less. Almost none.
And the third one: damn it, when will people understand that it’s really necessary to own the used tool? Read the documentation!
I will say right away that many of the details and “insides” of git’s work are omitted or simplified, but not to the detriment of understanding. It is the appearance of understanding and the purpose of the article, and if you already have it, and you are sure that it is true :) - then there will be nothing new for you here. If there is no understanding yet, then let's go!
First, find out what fast-forward is. This is just one of the options (methods, strategies) for performing the merge operation. It is possible only if the current commit (the one to which the merge will take place, denote it by A) is the ancestor of the merged commit (the one with which the merge will take place, we denote it by B). Graphically, it looks like this (x is just other commits that are not important to us):
......- xxxxAxxx-B
If the commit history looks like this:
......- B-xxxA
\
\ xxx-B
then you won’t be able to merge from A to B using the fast-forward method - A is not an ancestor of B. Similarly, in this situation, you cannot merge using the fast-forward method from B to A. But, any of the merges “from B” to A ”and“ from B to B ”- it’s possible, and when performing one of these merges, git (by default) will use the fast-forward method.
Now the next thing to understand is what push is. There are two key points in this operation. First: it’s not just the transfer of data about your commits “to the other side”, but also the obligatory subsequent operation merge “on the other side”. And the second: “that side” for the merge operation will use only fast-forward. Why is everything so?
Well, the first is obvious. To put something in the story, you need to create a new commit object in the story (which will be the descendant of the current one) and change the pointer to the last commit in the branch (called HEAD), to this new commit. By and large, there are only two operations for this: commit and merge (merge, which in most cases will lead to the appearance of a new commit). Obviously, no one commits manually on the server. And if you simply transfer your history (new commits) to the server, this will not change HEAD in any way. So the server is simply forced to do merge with your new commits - it just doesn’t have another option to change HEAD.
The second point is also not difficult, but not immediately obvious. Why is the server using merge using only fast-forward? After all, there isa bunch of other great methods, the same octopus , but their server does not use. Why? Everything is very simple: fast-forward is the only option that does not generate new commits (in addition to those that you have already added). After all, the server cannot generate new commits itself, since git requires the author to specify a commit - the server can only store your commits. And what is even more important - this method (fast-forward) during merging never gives conflicts: simply by the nature of the fast-forward method itself, conflicts during merging are not possible. This is important for the server, since in the event of a conflict there will be no one to solve it. That's why it is a conflict, so that it can be solved by a person, because the computer could not figure it out.
Understanding these basic things should be enough to “clarify” the situation. But how can it arise?
Everything is very simple: the repository is used by more than one person (or one, but on several machines), or a rebase was made in the local depository. In any of these options, the appearance of what is displayed in the second picture may occur. And since this situation has arisen for you (that is, you did a “git fetch”, and saw that everything is so), then let's agree that on the HEAD server it points to A (remotes / origin / master), to you locally - on B (master).
How to solve the problem? There are two options, and both of them lead to the fact that A will merge with such a commit (let's call it X), for which A will be the ancestor. How to achieve this?
Option One: Merge. You need to locally merge your code (B) and the one that is on the server (A). The merge will result in a new commit in your local history, the same X - and the push server will not refuse to make a push for it. For persuasiveness - picture:
......- B-xxxA-
\ \ <- merge operation A to B: git merge origin / master
xxx-b-x
Option Two: rebase. Rebase is the operation of "transferring" part of the story in such a way as to change the "root" of the branch, but not to change the branch itself. For clarity, the picture again. The initial state is the same as Figure 2, and the state after rebase will be like this:
......- B-xxxA
\ <- rebase operation: git rebase origin / master
xxx-b
In the result, the role of the commit X is played by B - but this is another B, let's say B '. This can be easily verified by looking at commit-id before rebase and after. But as already mentioned, the branch itself (that is, the contents of the commits that make it up) will not change - only commit-id will change. But the goal is achieved - you can do push.
Which option to choose is up to you. On the net, more than one pack of dogs was eaten about this. My recommendation for beginners is the first option, as it is simpler and less error prone.
There is a third solution, and it was defuz who proposed forced push, for example: It will cause the server to accept your commits, and of course
git push origin +master
change its HEAD so that it points to the last of your commits - to B. Thus, the server will “forget” all “your” commits that you don’t have (from B to A). This is the very data loss that I talked about at the beginning. However, sometimes there are times when this is exactly the operation that you need. But if you have just such a case, and you understand this, then probably this article has not been needed for a long time.
Well, to put all the points above and - regarding the answer alekciy . Since its version does not use forced push, there will be no data loss. But it’s quite obvious that after merge, firstly, there’s nothing to put in stash (except for new files that are not yet monitored by git), and secondly, rebase is no longer needed (and if there are new files, it will break off) .
I hope this material clarified what is happening and will help solve the problem.
PS1: It’s very convenient to use “gitk -a” (linux) to view the history.
UPD: Terentich Windows also works fine.
UPD: eyeofhell And this also works on OSX :)
PS2: Stash (as well as many other operations) is a topic for another discussion.