Search code examples
gitgit-stashgit-checkout

git checkout fails due to local changes but stash applies cleanly afterwards


It happens often that I try to checkout a new remote branch (to create a local branch out of it, but this might be irrelevant to my issue) and git fails with the following error

error: Your local changes to the following files would be overwritten by checkout:
Please, commit your changes or stash them before you can switch branches.

Now, when I see this, one could naively expect that if one stashes the changes, checkout the new branch and then apply the stashed changes then there would be a conflict, but that is almost never the case. What happens is that the stashed changes apply cleanly and I don't lose anything from what I had before I checked out the new branch. Why is git giving this seemingly misleading error? if I can stash before checking out and apply the stash at the end cleanly, why isn't git checkout doing just that under the hood?

Edit:
To make it clearer, I am not asking why the checkout fails, or why sometimes a checkout with a dirty workspace succeeds, I understand all that. My questions is that in this case there is a course of action that is 100% data-loss free (or is there some corner case where data could be lost that I fail to see??) so why doesn't git just do it?

If I said to someone new to git that a modif on file foo line 100 would conflict with another modif on file foo line 2 it would kind of make sense to them an accept it as truth, not complain and would easily fix the conflict. But because git is a nice tool, it does the smart thing and doesn't even bother you with a non-issue that it can fix without any risks of corruption. Why is that not the same philosophy in this scenario with git checkout?


Solution

  • The git checkout command complains (and does not switch branches) if some modified file(s) must be wiped out and replaced by the checkout action.

    This means that there is a difference between the work-tree version and the HEAD version (so that the file is modified) and a difference between the HEAD version and the target commit (so that the file must be replaced).

    It does not mean that the difference between the work-tree version and HEAD version conflicts in any way with the target commit, just that the target commit differs from the HEAD commit and the HEAD commit differs from the work-tree version. For instance, suppose the work-tree version of README changes the spelling of "color" to "colour" in line 17 out of 35, and the difference from the HEAD version to the target version adds a note at the end of the file (adds a line 36). In this case it's trivial to apply the spelling change as well, but git checkout won't do that, it will just refuse to check out the target commit.

    [Edit to add, from comment] It's not that git checkout cannot do merges, just that it won't by default. Use git checkout -m to tell git checkout to use the merge code.