2

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?

Hilikus
  • 9,954
  • 14
  • 65
  • 118
  • Probably because a checkout is not a merge. Git doesn't think you want a merge and is kind enough to point that out. – isherwood Feb 01 '16 at 20:51
  • i *don't* want a merge. I want to keep those local changes as local changes except that with a different branch checked out. You can't merge what's not committed – Hilikus Feb 01 '16 at 20:53
  • Your discussion of applied stashes led me to believe otherwise. You may want to revise your question a bit. – isherwood Feb 01 '16 at 20:55
  • Possible duplicate of [git: Switch branch and ignore any changes without committing](http://stackoverflow.com/questions/1304626/git-switch-branch-and-ignore-any-changes-without-committing) – isherwood Feb 01 '16 at 20:55
  • SInce you want to switch and keep your local changes, your procedure seems theright way to go. But others might have a diferent objective. So, I think that git does it correct to advice you, but to leave the procedure to apply to you... – Sebastiaan Mannem Feb 01 '16 at 20:59
  • @SebastiaanMannem the procedure is indeed the right way, my question is more, why is git complaining about a non-issue. There is no data-loss at all so why block the checkout? – Hilikus Feb 01 '16 at 21:01
  • git is not doing this under the hood, because whether he should depends on your motives. Suppose you are developing in a development branch and suddenly get confronted with a bug you need to resolve ASAP in another branch. You don't want git to switch to the other branch and leave your changed development files out there. In that case you should not stash, branch and pop, but (most certainly) commit and branch, or stash, branch and not pop. Anyway, as I'm stating, git doesn't choose the actions for you, since it is up to you what actions ould be appropriate. – Sebastiaan Mannem Feb 01 '16 at 21:13
  • I see your point @SebastiaanMannem, it is basically, "you don't always want to preserve those local changes when you checkout a new branch", right? the problem I see with that logic is that git checkout doesn't refuse when you have local changes on files that were not modified in the new branch, as explained by torek. So it can't be an issue of, "maybe you don't want those changes in your new branch". What do you think? – Hilikus Feb 01 '16 at 21:24
  • Seems you got me there. I can only respond with 'I think' reponses from here. I guess that git expects your local changes to be complementary. At least your changes would be applied to the same origin (the files don't differ in between commits). – Sebastiaan Mannem Feb 01 '16 at 21:41

1 Answers1

7

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.

torek
  • 448,244
  • 59
  • 642
  • 775
  • that much I figured, but my question is more, why is git blocking an action that is (at least it seems) harmless. I understand that doing an automatic resolution if the stash conflicts is impossible, but if there is no data-loss, why wouldn't git do the necessary steps programmatically? – Hilikus Feb 01 '16 at 21:05
  • He is not blocking it. He simply does not know what to do with your local changes, while you are switching to a different branch... – Sebastiaan Mannem Feb 01 '16 at 21:14
  • @torek your example on the last paragraph exactly represents my issue. If you followed that same logic with merges, git would refuse to do such a merge because it changed the same file, *but it doesn't refuse*, it knows that since the changes were on different lines, it can do a merge without losing anything. Why doesn't it do the same with checkouts? there is no loss of data – Hilikus Feb 01 '16 at 21:35
  • @SebastiaanMannem like I mentioned in your other comment, if it was a matter of not knowing what to do with local changes then a git checkout with a dirty workspace would *always* fail, but that is not the case: `git checkout -b b1; touch myfile; git checkout -b b2` works just fine – Hilikus Feb 01 '16 at 21:47
  • 1
    It's just a design decision: `git checkout` does not do merges. `git checkout` *could* do merges, and `git checkout -m` *does* do merges, so add `-m` if that's what you want. – torek Feb 01 '16 at 22:34
  • i didn't know about -m. that is exactly what i was looking for. still i'm a bit surprised it is not the default behaviour vs `git merge` where it is, but that's less important now that I know about the flag. thank you – Hilikus Feb 02 '16 at 18:44