This mini-note in the first place is the answer to the
question .
Since my account is read-only, this is the answer. “And life is getting better!” ©The first conclusion after reading the question and answers - do not do it as suggested by
defuz . He does not understand the essence of the problem, and if you do as they are asked, most likely you will lose data.
Second:
alekciy is also not quite right, but there are much less chances for data loss. Almost none.
And the third: damn, well, when will people realize that it is really necessary to own a used tool? Read the documentation!
I will say right away that many of the details and “insides” of the work of git are omitted or simplified, but not to the detriment of understanding. It is the emergence of understanding and the purpose of the article, and if you already have it, and you are sure that it is correct :) - then nothing new for you will be here. If there is no understanding yet, then let's go!
')
First, you need to figure out what fast-forward is. This is just one of the options (methods, strategies) for performing the operation merge. It is possible only if the current commit (the one to which the merge will take place, we denote it A), is the ancestor of the “merged” commit (the one with which the merge will take place, we denote it B). Graphically, it looks like this (x - just other commits that are not important to us):
......- xxxxAxxx-B
If the commit history looks like this:
......- B-xxxA
\
\ xxx-B
then merging from A to B with the fast-forward method will not work - But it is not an ancestor for B. In the same way, in this situation you cannot merge with the fast-forward method from B to A. But, any of the merges “from B to A and B from B to B is possible, and when performing one of these merges, git (by default) will use the fast-forward method.
Now the next thing you need to understand is what push is. There are two key points in this operation. First: it is not just the transfer of data about your commits “to the other side”, but also the
mandatory subsequent merge operation “on that side”. And the second: “that side” for the operation merge will use
only fast-forward. Why is it that way?
Well, the first is obvious. To put something in history, you need to create a new commit object in history (which will be a descendant of the current one) and change the pointer to the last commit in a branch (called HEAD), to this new commit. By and large, there are only two operations for this: commit and merge (the merge, which in most cases will lead to the appearance of a new commit). Obviously, nobody makes commits “manually” on the server. And if you just transfer your history (new commits) to the server, this will not change HEAD. So the server just has to merge with your new commits - it simply has no other option to change HEAD.
The second point is also not difficult, but not immediately obvious. Why does the server, when doing merge, use only fast-forward? After all, there is a
bunch of other wonderful 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 (besides those that you have already added). After all, the server cannot generate new commits by itself, since to create a git commit requires the indication of the author — the server can only store your commits. And what is even more important - this method (fast-forward) during merging
never gives conflicts: just by the nature of the fast-forward method itself, conflicts with merging are not possible. For the server it is important, since in case of a conflict, there will be no one to solve it. On that he and the conflict that the person solved it, therefore as the computer could not understand.
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 is possible. And since you had such a situation (that is, you made a “git fetch”, and you saw that this is the case), then let's agree that HEAD points to A (remotes / origin / master) on your server 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 an ancestor. How to achieve this?
Option one: merge. You need to locally merge your code (B) and the one on server (A). The result of the merger will be a new commit in your local history, the same X - and you will not refuse to make a push server for it. For credibility - the picture:
......- B-xxxA-
\ \ <- merge operation A in B: git merge origin / master
xxx-bx
Option Two: rebase. Rebase is the operation of “transferring” a part of a story in such a way as to change the “root” of a branch, but not to change the branch itself. For clarity - again the picture. 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 resulting result, the role of commit X is performed by B - but this is another B, let's say, B '. You can easily verify this by looking at the commit-id before and after rebase. But as already mentioned, the branch itself (that is, the contents of commits, its components) will not change - only the commit-id will change. But the goal is achieved - you can push.
Which option to choose is up to you. In the network already not one pack of dogs eaten about this. My recommendation for newbies is the first option, as it is simpler and less susceptible to bugs.
There is a third solution, and it was suggested by
defuz - forced push, like this:
git push origin +master
It will cause the server to accept your commits, and will
unconditionally change its HEAD so that it points to the last of your commits — B. So all “your” commits that you do not have (from B to A) are server "Forget." This is the very data loss that I mentioned at the beginning. However, sometimes there are times when this is the operation you need. But if you have such a case, and you understand it, then you probably no longer need this article.
Well, in order to completely dot everything and - regarding the answer
alekciy . Since its version does not use forced push, there will be no data loss. But it is quite obvious that after the merge, firstly, there will be nothing to put in the stash (except for new files that are not under the supervision of git), and secondly, the rebase is no longer needed (and if there are new files, it will break off) .
I hope this material has clarified what is happening and will help solve the problem.
PS1: It is very convenient to use “gitk -a” (linux) to view the history
UPD:
Terentich In Windows also works great.
UPD:
eyeofhell And it works in OSX too :)
PS2: Stash (as well as many other operations) is a topic for another conversation.