I have a question in GIT pull process.
Let us say, I am working on local file called "Program.java". (lets assume that I cloned the repo initially from remote). And after I finish my changes, I commit them in my local repository.
Meanwhile when I worked locally, someone updated the same file "Program.java" in the remote branch. The change what the other person made was, addition of some lines at the end of the file. The changes I made locally was, addition of few lines at the beginning of the file.
Now when I invoke 'git pull', git silently fast forwards the merge between my local and remote branches!!
This is not correct. Isn't it ? Ideally it should have shown conflicts. Because, someone modified the same file remotely while I think that nothing was modified in the Program.java file.
When I test the program (Program.java), I realize that there was some modification happened at remote and finally I end up catching the guy who performed the update and correct my file.
This is dangerous and git, rather fast forwarding the merge, should have shown conflicts. This would have alerted me before I sensed the updated from other person.
Can someone please hep me to understand why this happens in GIT.
Note: Git shows conflict if the same line was updated between remote and local branches which is exactly was expected.
Thanks in advance.
CodePudding user response:
Git considers two lines that are far apart but in the same file to be as independent of each other as two lines in different files.
Imagine the following situation:
Developer A removes function foo
from the beginning of file bar
while developer B changes code at the end of that file to use function foo
.
Git will happily merge these changes as they don't touch the same lines. Yet still there is a conflict, because the code now uses a deleted function. But the same can happen with two different files.
Consider the following, very similar situation:
Developer A removes function foo
from file library
while developer B changes code in file bar
to use function foo
from library
.
Git will also merge these two changes without conflict and since the changes are in different files no one will argue with that. Yet we still have the same conflict, the code is now calling a deleted function.
So there are only the two extreme options:
- Assume every line depends on every other line in every other file and therefore don't allow merges without conflicts at all.
- Only show a conflict, when it is absolutely necessary, i.e. when the same lines are changed.
It is important to remember that git not reporting a conflict does not mean that there is none. Communicate with the other developers, read their changes when merging and make sure you do have (preferably automatic) tests.
CodePudding user response:
First of all, you need to take responsibility for you. You said git pull
. That means "fetch and merge". So you can hardly complain when that's exactly what Git does, and if you didn't want that to happen, you shouldn't have said that.
Personally, I never say pull
, and for exactly this reason: it does stuff to your branch, and who knows what it will be? I'd rather have a chance to know what Git might do to my branch without actually doing it until I know what it is.
So, suppose we are on branch mybranch
. I say
git fetch
git status
Now Git tells me what would happen if I were to merge origin/mybranch
into mybranch
: which one is "ahead", and whether this would be a fast-forward.
If I'm still in doubt, I can compare myorigin/mybranch
with mybranch
using git diff
, to see what the differences between them are.
If this would be a fast-forward and I'm happy with what would happen if it were performed, I now say
git merge --ff-only
and the fast-forward is performed.
But not so fast. What is a fast-forward? It isn't really a merge at all. It is done only in the situation where your branch and the remote branch are a perfect match right up to your last commit, and after that there are some additional commits on the remote branch:
A -- B -- C -- D (the remote mybranch)
A -- B (your mybranch)
In that situation, Git will simply append C and D to your mybranch
. No muss, no fuss. And no harm done. After all, these are all clean individual commits, and someone really did add them on the remote, so it is right for Git to assume that when you merge, you are saying you want them.
If for some reason Git is wrong about what you wanted, and you regret appending C and D to your branch, then just undo the merge:
git reset --hard <SHA of B>
(For more about regret, see my https://stackoverflow.com/a/59675191/341994.)
Finally, let's talk about what you said in your question. You said that you made some changes locally and commited them, and someone else made some changes in the same file and committed them on the remote. But you then said that git pull
did a fast-forward. That is not true. If you have changes on your side that the remote does not have, Git will not fast-forward; it can't. It will do a true merge.
That is exactly why, when I do the merge, I say --ff-only
. That way, if we have gotten into the situation you describe, nothing happens — we are telling Git, "If something has gone wrong and someone has added commits to mybranch
on the remote, do not merge at all." Which is exactly what you wish you had told Git.