11

So, I have faced this nasty problem quite often, and tried looking for any solutions online, but I am not sure if this is git's expected behavior or not.

Whenever I have a few local commits and I do a git pull, git brings all the other peoples' commits into my staging area in case, I have a merge conflict. Now, although the conflict is on a single file, I do not understand why git brings other peoples' non-conflicting commits into my staging area, asking me to commit them as if they were changes/ commits made by me.

Recently, I had this same behavior on a colleague's laptop, and we unstaged all the files not added by the colleague before committing, and then committed the only file that was conflicting. And what happened, was quite unexpected. Git apparently deleted all those files, undid all the changes, as if our commit reversed them.

However, the strangest thing of all was, we looked in Stash (we use Stash for managing the central repo), and I did not see those files being undone by our commit in Stash.

I am at a loss as how did this happen. Any plausible explanation? Also, I have seen Stash act very weirdly at times. It seems unreliable. It will not show any changes between two commits, and yet there are at times, changes. I have no idea how this happens, but has anyone else experienced these issues?

Update : I realized this later that Stash wasn't weird just a little unintuitive. When doing a merge, it showed changes based on two different parents. And there was a small dropdown that would let you change the parent to see changes corresponding to it. I thought it would be good to update it here so people do not have misleading ideas.

Setafire
  • 719
  • 2
  • 9
  • 21
  • 1
    One thing you can do works differently depending on whether you've completed the merge it presents you with. Before completing it, try `git diff HEAD...HEAD@{u}` (note the three dots, see the `git diff` docs for that) to see what changes are being merged in; after completing it, to see what was done, when you've got the merge checked out do `git diff HEAD^...HEAD^2` (the two commands just use different ways of specifying the tips to work with since you're starting from different commits). – jthill Apr 05 '15 at 23:36

2 Answers2

14

The staged files you are seeing are the results of all the changes between the last time you synchronised your local branch with the remote branch (I guess master as you do not mention it).

Git pull has 'fetched' the changes since you last pulled, then it tries to merge this local copy of the remote branch into your local branch. It then hit a conflict in one of the changed files.

So it has to stop in the middle of the merge to ask you how to resolve the conflict to the one file.

So the other files are ready to merge, have been staged and would have been merged automatically if there hadn't been any other conflicts.

To complete the automatic merge, you need to a) resolve the conflict then b) hit commit to finish merging the other people's changes into your local branch. (Some GUIs automate the commit completion step after you click a "I've finished resolving" button)

You will also notice in the commit window of your GUI, there is a pre-fabricated merge message for the pending commit? It will say something like "merging origin/x into x... Conflicts: y". Once all the changed files can be added to the stage you are ready to complete this automatic commit which was paused.

So this sounds like expected behaviour to me, but you are just seeing "inside" one of git's internal processes.

Stashing shouldn't be necessary or involved here. Although some GUIs do auto-stash, git itself doesn't use stashing during a pull.

Note: you shouldn't have any changed files locally when you do a pull. (i.e. commit everything clean before doing any branch operations, is a good best practice) Having a GUI clean up your changed files is when auto-stash is useful, but it still has its complications when there are conflicts. i.e. you need to resolve the conflicts then remember to pop the stash afterwards. If you rely on the automation too much, it gets confusing when you have to complete the automated process you don't know about! So I'd recommend always keeping your working directory clean.

scipilot
  • 6,681
  • 1
  • 46
  • 65
  • Thanks for the explanation. So seems like this is an expected behavior. So, you are supposed to commit those other peoples' staged files. I always wonder how does that affect the history of that file? Won't that show extra commits/ person always where nothing changed? Also, the Stash I am talking here, is not git stash, its the Atlassian Stash GUI for accessing the remote central repo. – Setafire Apr 06 '15 at 00:18
  • Yes you must commit the staged files to complete the merge. Otherwise your files will not exactly mirror the remote branch (ie. what everyone else has). – scipilot Apr 06 '15 at 02:16
  • Yes, it will affect the history of the file, but only in that branch. When you come to push the branch, you will push the merge commit, but the file will be the same as on the remote, so there are no actual further changes. It just shows the history as it was, you did a local merge and also had a conflict. People can see from the commit diff that there were no actual changes to that file made by you, but it does take a bit of practise to see this. – scipilot Apr 06 '15 at 02:18
  • Ah re Stash - yes I did wonder after seeing the capital S. Sorry, I've only briefly used Stash but never found it unreliable. – scipilot Apr 06 '15 at 02:20
  • 1
    @Setafire: yes, it is normal, expected behavior. One way to think about git is that it does not really have *file* history, it has *commit* history: when you finally make the merge commit, git records that you made the merge. The *files* attached to it have no more-specific authorship. This is true whether git makes the merge for you, or stops and has you resolve conflicts and makes the merge. A merge has two parent commits, though, and if a file in the merge commit matches one of the two parents but not the other, its contents must have come from the matching commit. – torek Apr 06 '15 at 03:26
  • @torek this should really be an answer. Thanks :) – Setafire Apr 06 '15 at 03:42
3

The default behaviour of git pull is to perform a fetch and then a merge. A merge is an actual, new, commit; normally this is resolved automatically so you see no staged changes. However, in the case of a conflict, the commit cannot be performed automatically, hence the visible staged changes.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • 3
    Yes, I know its a fetch and merge. But, what I don't get is why does it stage even the non-conflicting files into my staging area everytime? Doesn't that change the history of those files if I recommit them without even touching them? And how can git simple undo those changes/ delete the files if I unstage them? – Setafire Apr 05 '15 at 22:45
  • @Setafire did you find a solution to this? – Artem Malchenko Oct 11 '21 at 20:32