{"thread":{"id":"63145","subject":"git switch --force vs --discard-changes: docs don't match behavior","startedAt":"2025-03-17T16:50:14Z","lastAt":"2025-03-17T18:13:26Z","messageCount":4,"participants":["David Mandelberg","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"514414","messageId":"bce5a500-76c4-4462-8f99-b17a6f21f7ec@mandelberg.org","threadId":"63145","inReplyTo":null,"subject":"git switch --force vs --discard-changes: docs don't match behavior","fromName":"David Mandelberg","fromEmail":"david@mandelberg.org","sentAt":"2025-03-17T16:50:12Z","receivedAt":"2025-03-17T16:50:14Z","isPatch":false,"sender":{"key":"david@mandelberg.org","avatar":"https://avatars.githubusercontent.com/u/15314936?v=4"},"body":"Hi,\n\nhttps://github.com/git/git/blob/683c54c999c301c2cd6f715c411407c413b1d84e/Documentation/git-switch.adoc?plain=1#L115-L124 \ndescribes --force as \"An alias for `--discard-changes`.\"\n\nAt least on git 2.47.2 though[1], it looks like --force will force more \nthings than --discard-changes:\n\n$ touch Makefile\n$ git switch master\nerror: The following untracked working tree files would be overwritten \nby checkout:\n         Makefile\nPlease move or remove them before you switch branches.\nAborting\n$ git switch --discard-changes master\nerror: Untracked working tree file 'Makefile' would be overwritten by merge.\n$ git switch --force master\nbranch 'master' set up to track 'origin/master'.\nSwitched to a new branch 'master'\n\nIs this a bug in the code or documentation?\n\n[1] I searched the mailing list to see if there were any relevant \nchanges since then.\n"},{"id":"514421","messageId":"xmqqo6xza838.fsf@gitster.g","threadId":"63145","inReplyTo":"bce5a500-76c4-4462-8f99-b17a6f21f7ec@mandelberg.org","subject":"Re: git switch --force vs --discard-changes: docs don't match behavior","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-17T17:19:07Z","receivedAt":"2025-03-17T17:19:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Mandelberg <david@mandelberg.org> writes:\n\n> $ touch Makefile\n\nYou'd need to explain this example a bit better.  The reproduction\nmust be done in a repository where the master branch has Makefile\ntracked, you are not on the master branch, and the commit you have\nchecked out does not have Makefile tracked.  IOW, this is not making\nMakefile stat-dirty, but is creating a new untracked file.\n\nAs I wasted a few minutes scratching my head before figuring out the\nabove, I'm leaving a note for others.\n\n> $ git switch master\n> error: The following untracked working tree files would be overwritten\n> by checkout:\n>         Makefile\n> Please move or remove them before you switch branches.\n> Aborting\n\nMakes sense (note.  I do not use \"git switch\" myself).\n\n> $ git switch --discard-changes master\n> error: Untracked working tree file 'Makefile' would be overwritten by merge.\n\nI would guess that this \"discard changes\" takes the word \"change\" in\na reasonbly strict sense (note.  I do not use \"git switch\" myself,\n\"git checkout\" does not have this option, and I do not recall\nthinking about how \"--discard-changes\" should operate when the\nfeature was added, so this is pretty much my first time to think\nabout this issue).  Untracked \"Makefile\", by definition, is not\nknown by Git, so it cannot even tell if it was changed or not.\n\nIf \"Makefile\" is tracked in the current commit, this would have\ndiscarded changes from that modified file and allowed you to switch\nto the master branch.\n\n> $ git switch --force master\n> branch 'master' set up to track 'origin/master'.\n> Switched to a new branch 'master'\n\nAnd forcing would give us the same behaviour as \"checkout -f\".\n\n> Is this a bug in the code or documentation?\n\nI do not have a strong opinion either way.  It may appear to some\nusers that giving a finer grained control is a merit.  Even when you\nare willing to throw away changes to already tracked content,\ngetting stopped when you may lose a totally untracked thing might be\nnicer.\n\nOn the other hand, I suspect to many others this finer grained\ncontrol does not give much value while adding more confusion.\n\nI am obviously biased because I am accustomed not to have this\ndistinction and accept \"checkout -f\" as a reasonable way to force\nswitching to another branch discarding any and all local\nmodifications including untracked new files that get in the way,\nthough.  But I do not feel strongly enough to say that the behaviour\nand the feature itself is misguided and we should rip it out.  As\nlong as that \"finer grained control\" is working in a consistent and\nexplainable way, I'd actually vote for fixing the documentation to\nexplain how \"--discard-changes\" is a bit milder than \"--force'.\n\nThanks.\n"},{"id":"514422","messageId":"xmqqiko7a75u.fsf@gitster.g","threadId":"63145","inReplyTo":"xmqqo6xza838.fsf@gitster.g","subject":"Re: git switch --force vs --discard-changes: docs don't match behavior","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-17T17:39:09Z","receivedAt":"2025-03-17T17:39:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I do not have a strong opinion either way.  It may appear to some\n> users that giving a finer grained control is a merit.  Even when you\n> are willing to throw away changes to already tracked content,\n> getting stopped when you may lose a totally untracked thing might be\n> nicer.\n>\n> On the other hand, I suspect to many others this finer grained\n> control does not give much value while adding more confusion.\n>\n> I am obviously biased because I am accustomed not to have this\n> distinction and accept \"checkout -f\" as a reasonable way to force\n> switching to another branch discarding any and all local\n> modifications including untracked new files that get in the way,\n> though.  But I do not feel strongly enough to say that the behaviour\n> and the feature itself is misguided and we should rip it out.  As\n> long as that \"finer grained control\" is working in a consistent and\n> explainable way, I'd actually vote for fixing the documentation to\n> explain how \"--discard-changes\" is a bit milder than \"--force'.\n\nIt seems that this was designed from day one to work this way, or\nthis was buggy from day one to work this way.  Take your pick ;-)\n\nWhen 3ec37ad1 (switch: add --discard-changes, 2019-03-29) introduced\nthe --discard-changes option, it seems that it was intended to be\ntruly the synonym, but the code change was done in a somewhat\nconfusing way and added an extra bit (a new .discard_changes member\nwas added, and is initialized by the value given to the .force\nmember.  Many existing conditionals that looked at the .force bit\nwas changed to look at the .discard_changes bit, but not all of\nthem.\n\nIt did not offer the protection against overwriting untracked files\nin builtin/checkout.c:reset_tree().  The .reset member was always\nset to 1 to allow it.\n\nLater 480d3d6b (Change unpack_trees' 'reset' flag into an enum,\n2021-09-27) added the \"protect against overwriting untracked files\"\nfeature, and made it pay attention to the \"force\" option, not the\nother member.  That is where this discrepancy was introduced.  In\nretrospect, it might have been better if 3ec37ad1 removed .force and\nkept only \"--force\" at the UI layer, internally used the new\n.discard_changes everywhere.  Or it would have been even better if\nthe commit did not happen at all, and added \"--discard-changes\" only\nat the UI layer.  Then the discrepancy would never have happened.\n\nThe author of 480d3d6b Cc'ed to ask for the original intention for\nreference, but that is merely to satisfy my curiosity.  At this\npoint, I think it is way too late for the original intention to sway\nthe conclusion, as existing users of \"switch\" may be relying on the\ncurrent behaviour.  The best course of action may be to update the\ndocumentation to match the behaviour.\n\nThanks.\n"},{"id":"514428","messageId":"feeef061-1b44-44c2-9fde-06b73e58c516@mandelberg.org","threadId":"63145","inReplyTo":"xmqqo6xza838.fsf@gitster.g","subject":"Re: git switch --force vs --discard-changes: docs don't match behavior","fromName":"David Mandelberg","fromEmail":"david@mandelberg.org","sentAt":"2025-03-17T18:13:24Z","receivedAt":"2025-03-17T18:13:26Z","isPatch":false,"sender":{"key":"david@mandelberg.org","avatar":"https://avatars.githubusercontent.com/u/15314936?v=4"},"body":"Op 2025-03-17 om 13:19 schreef Junio C Hamano:\n> David Mandelberg <david@mandelberg.org> writes:\n> \n>> $ touch Makefile\n> \n> You'd need to explain this example a bit better.  The reproduction\n> must be done in a repository where the master branch has Makefile\n> tracked, you are not on the master branch, and the commit you have\n> checked out does not have Makefile tracked.  IOW, this is not making\n> Makefile stat-dirty, but is creating a new untracked file.\n\nSorry about that. I had originally meant to copy/paste all the commands \nI ran, but the `git fetch` command produced a lot of output and I \ndecided to only paste the ones that showed the bug, not the setup ones. \nHere are the commands I had run, starting in an empty directory:\n\ngit init\ngit remote add origin https://github.com/git/git.git\ngit fetch origin\n# a few status and show commands, but those hopefully aren't important\n\nSo I actually didn't have any commit checked out, but I think having a \ncommit checked out that did not have a Makefile would have done the same \nthing.\n\n>> Is this a bug in the code or documentation?\n> \n> I do not have a strong opinion either way.  It may appear to some\n> users that giving a finer grained control is a merit.  Even when you\n> are willing to throw away changes to already tracked content,\n> getting stopped when you may lose a totally untracked thing might be\n> nicer.\n> \n> On the other hand, I suspect to many others this finer grained\n> control does not give much value while adding more confusion.\n> \n> I am obviously biased because I am accustomed not to have this\n> distinction and accept \"checkout -f\" as a reasonable way to force\n> switching to another branch discarding any and all local\n> modifications including untracked new files that get in the way,\n> though.  But I do not feel strongly enough to say that the behaviour\n> and the feature itself is misguided and we should rip it out.  As\n> long as that \"finer grained control\" is working in a consistent and\n> explainable way, I'd actually vote for fixing the documentation to\n> explain how \"--discard-changes\" is a bit milder than \"--force'.\n\nMakes sense. For what it's worth, I don't have much of an opinion one \nway or another. I want some way to do what --force currently does, but I \ncan always use `git checkout` for that if needed.\n"}]}