* git-checkout silently throws away the dirty status of index without a warning? @ 2011-09-01 15:47 Tzu-Jung Lee 2011-09-01 16:56 ` Jeff King 2011-09-01 17:11 ` Junio C Hamano 0 siblings, 2 replies; 6+ messages in thread From: Tzu-Jung Lee @ 2011-09-01 15:47 UTC (permalink / raw) To: git Hi guys, Correct me if I'm wrong: git-checkout saves the changes to index and working-tree, and tries to apply them to the destined commit. If the changes are applicable, then git-checkout the destined commit and apply the changes. Otherwise, git-checkout fails with warnings and leaves the current status untouched. If the above correct. Please help me clarify if the following corner case an intended or unexpected behavior. Setup and git repo with two commits to illustrate the scenario: $ git init $ echo aaa >> aaa.txt $ echo bbb >> bbb.txt $ git add . $ git commit -a -m commit1 $ echo bbb >> bbb.txt $ echo aaa >> aaa.txt $ git commit -a -m commit2 Forge a unclean index with changes that are subset of the destined commit we are about to switching to. $ git checkout -b br1 $ git reset HEAD^ Unstaged changes after reset: M aaa.txt M bbb.txt $ git checkout HEAD aaa.txt $ git status --short M bbb.txt $ git add bbb.txt $ git status # On branch br1 # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: bbb.txt # $ git checkout master Switched to branch 'master' git silently switch to master without warning against the index are "RESTORE/RESET" to clean. $ git checkout br1 $ git status # On branch br1 nothing to commit (working directory clean) Is this an intended behavior? Though the status and changes can be safely restore from the database with some manipulation. But it may become difficult and tedious if the number of involved files are large. Regards, Roy ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: git-checkout silently throws away the dirty status of index without a warning? 2011-09-01 15:47 git-checkout silently throws away the dirty status of index without a warning? Tzu-Jung Lee @ 2011-09-01 16:56 ` Jeff King 2011-09-01 17:50 ` Tzu-Jung Lee 2011-09-01 17:11 ` Junio C Hamano 1 sibling, 1 reply; 6+ messages in thread From: Jeff King @ 2011-09-01 16:56 UTC (permalink / raw) To: Tzu-Jung Lee; +Cc: git On Thu, Sep 01, 2011 at 11:47:59PM +0800, Tzu-Jung Lee wrote: > Correct me if I'm wrong: > > git-checkout saves the changes to index and working-tree, and > tries to apply them to the destined commit. > If the changes are applicable, then git-checkout the destined > commit and apply the changes. > Otherwise, git-checkout fails with warnings and leaves the current > status untouched. Not exactly. "git checkout <branch>" will switch your HEAD to <branch>, and then try to make your index and working tree match the contents of <branch>, with two exceptions: 1. If you have local changes in a file, but the contents of the file in <branch> do not differ from what's in the current HEAD, then the file will be left alone (i.e., your local changes will be preserved). 2. If you have local changes in a file, and the contents of the file in <branch> differ both from what's in your working tree and from what's in your current HEAD, git will print an error and refuse to overwrite your changes (though you can ask it to merge them with "git checkout -m"). So it is not about "do these changes apply", but rather that we will give up any time file-level merging is required (unless "-m" is specified). The other form, "git checkout <branch> [--] <file>", is not about switching branches at all, but about putting content from <branch> into the current index and working tree, overwriting what's there. > If the above correct. Please help me clarify if the following corner > case an intended or unexpected behavior. > [...] > $ git checkout -b br1 > $ git reset HEAD^ > Unstaged changes after reset: > M aaa.txt > M bbb.txt So you have changes in two commits... > $ git checkout HEAD aaa.txt And here you explicitly overwrite the changes in aaa.txt. > $ git status --short > M bbb.txt ...leaving only the changes in bbb.txt. > $ git add bbb.txt > $ git status > > # On branch br1 > # Changes to be committed: > # (use "git reset HEAD <file>..." to unstage) > # > # modified: bbb.txt > # OK, now it's staged. > $ git checkout master > Switched to branch 'master' > > git silently switch to master without warning against the index are > "RESTORE/RESET" to clean. Yes, because the changes in your index were identical to what was in the destination branch. So we didn't drop any changes; they're still in the index and in the working tree. It's simply that when compared to your new HEAD, they are uninteresting. > $ git checkout br1 > $ git status > # On branch br1 > nothing to commit (working directory clean) And now when we switch to br1, you have no changes against master in your working tree or index, so there is no dirty state to block switching branches. I think git is working as intended here. I agree it is a somewhat surprising corner case, but only because your changes happened to exactly match the difference between the two branches you are switching between. But it makes sense when you think about what "dirty state" means: it is differences between HEAD and your index and working tree. So we usually think of creating or removing dirty state by changing the working tree. But you could equally well do it by changing the HEAD without changing the working tree, which is what you did here. -Peff ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: git-checkout silently throws away the dirty status of index without a warning? 2011-09-01 16:56 ` Jeff King @ 2011-09-01 17:50 ` Tzu-Jung Lee 0 siblings, 0 replies; 6+ messages in thread From: Tzu-Jung Lee @ 2011-09-01 17:50 UTC (permalink / raw) To: Jeff King; +Cc: git On Fri, Sep 2, 2011 at 12:56 AM, Jeff King <peff@peff.net> wrote: > On Thu, Sep 01, 2011 at 11:47:59PM +0800, Tzu-Jung Lee wrote: > >> Correct me if I'm wrong: >> >> git-checkout saves the changes to index and working-tree, and >> tries to apply them to the destined commit. >> If the changes are applicable, then git-checkout the destined >> commit and apply the changes. >> Otherwise, git-checkout fails with warnings and leaves the current >> status untouched. > > Not exactly. "git checkout <branch>" will switch your HEAD to <branch>, > and then try to make your index and working tree match the contents of > <branch>, with two exceptions: > > 1. If you have local changes in a file, but the contents of the file > in <branch> do not differ from what's in the current HEAD, then the > file will be left alone (i.e., your local changes will be > preserved). > > 2. If you have local changes in a file, and the contents of the file > in <branch> differ both from what's in your working tree and from > what's in your current HEAD, git will print an error and refuse to > overwrite your changes (though you can ask it to merge them with > "git checkout -m"). > > So it is not about "do these changes apply", but rather that we will > give up any time file-level merging is required (unless "-m" is > specified). > Ah!!! I think it was the "file-level merging" that surprised me so much. I used to think it's an atomic "commit-level merging" -- cleanly apply all the changes or touch nothing at all. Having using git for years, I never notice this difference and neither did it cause any trouble to me. Until the corner case came to me today... Thanks for the quick and precise explanation. > The other form, "git checkout <branch> [--] <file>", is not about > switching branches at all, but about putting content from <branch> into > the current index and working tree, overwriting what's there. > >> If the above correct. Please help me clarify if the following corner >> case an intended or unexpected behavior. >> [...] >> $ git checkout -b br1 >> $ git reset HEAD^ >> Unstaged changes after reset: >> M aaa.txt >> M bbb.txt > > So you have changes in two commits... > >> $ git checkout HEAD aaa.txt > > And here you explicitly overwrite the changes in aaa.txt. > >> $ git status --short >> M bbb.txt > > ...leaving only the changes in bbb.txt. > >> $ git add bbb.txt >> $ git status >> >> # On branch br1 >> # Changes to be committed: >> # (use "git reset HEAD <file>..." to unstage) >> # >> # modified: bbb.txt >> # > > OK, now it's staged. > >> $ git checkout master >> Switched to branch 'master' >> >> git silently switch to master without warning against the index are >> "RESTORE/RESET" to clean. > > Yes, because the changes in your index were identical to what was in the > destination branch. So we didn't drop any changes; they're still in the > index and in the working tree. It's simply that when compared to your > new HEAD, they are uninteresting. > >> $ git checkout br1 >> $ git status >> # On branch br1 >> nothing to commit (working directory clean) > > And now when we switch to br1, you have no changes against master in > your working tree or index, so there is no dirty state to block > switching branches. > > I think git is working as intended here. I agree it is a somewhat > surprising corner case, but only because your changes happened to > exactly match the difference between the two branches you are switching > between. But it makes sense when you think about what "dirty state" > means: it is differences between HEAD and your index and working tree. > So we usually think of creating or removing dirty state by changing the > working tree. But you could equally well do it by changing the HEAD > without changing the working tree, which is what you did here. > > -Peff > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: git-checkout silently throws away the dirty status of index without a warning? 2011-09-01 15:47 git-checkout silently throws away the dirty status of index without a warning? Tzu-Jung Lee 2011-09-01 16:56 ` Jeff King @ 2011-09-01 17:11 ` Junio C Hamano 2011-09-01 18:28 ` Tzu-Jung Lee 1 sibling, 1 reply; 6+ messages in thread From: Junio C Hamano @ 2011-09-01 17:11 UTC (permalink / raw) To: Tzu-Jung Lee; +Cc: git Tzu-Jung Lee <roylee17@gmail.com> writes: > Is this an intended behavior? Yes, I think you are talking about the case where "the current index does not match the current HEAD, but it does match the tree we are switching to" case. In that case we take the contents of switched-to branch. It is the last case in the table in this old design document: http://thread.gmane.org/gmane.comp.version-control.git/4641 bug ignore the terminology (stage#). Read only the body of the table, with the understanding that the three entries in each row talk about the state for the same path in the index entry, the tree entry in the current HEAD, and the tree entry in the switched-to branch. Also the table does not talk about the checking performed on the working tree file, but assume that we do not overwrite it when the resulting entry in the index does not match what you have there. The reason we allow branch switching in this case, instead of failing, is so that you can be in a state where you applied the same change (relative to the current branch to the branch you are switching to) lying around already in your workspace and safely switch to the new branch without losing any work (after all, the content matches). By the way, the first six lines of your original message that describes "saving and applying" is not correct. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: git-checkout silently throws away the dirty status of index without a warning? 2011-09-01 17:11 ` Junio C Hamano @ 2011-09-01 18:28 ` Tzu-Jung Lee 2011-09-02 1:52 ` Andrew Ardill 0 siblings, 1 reply; 6+ messages in thread From: Tzu-Jung Lee @ 2011-09-01 18:28 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Fri, Sep 2, 2011 at 1:11 AM, Junio C Hamano <gitster@pobox.com> wrote: > Tzu-Jung Lee <roylee17@gmail.com> writes: > >> Is this an intended behavior? > > Yes, I think you are talking about the case where "the current index does > not match the current HEAD, but it does match the tree we are switching > to" case. In that case we take the contents of switched-to branch. > > It is the last case in the table in this old design document: > > http://thread.gmane.org/gmane.comp.version-control.git/4641 > Cool! It's really valuable resource about the internals. Do all the entries of the matrix still hold true today? Maybe we can have an updated version put in the manual or other official documentation. > bug ignore the terminology (stage#). Read only the body of the table, with > the understanding that the three entries in each row talk about the state > for the same path in the index entry, the tree entry in the current HEAD, > and the tree entry in the switched-to branch. Also the table does not talk > about the checking performed on the working tree file, but assume that we > do not overwrite it when the resulting entry in the index does not match > what you have there. > > The reason we allow branch switching in this case, instead of failing, is > so that you can be in a state where you applied the same change (relative > to the current branch to the branch you are switching to) lying around > already in your workspace and safely switch to the new branch without > losing any work (after all, the content matches). I understand it's a safe behavior in the sense that all the 'content' are preserved. But the 'work' may still lost in another sense. Consider the extended scenario. I was in the middle of doing... kind of 'in-commit-bisection' (btw, how would you call this?). Finding out which are the guilty changes in a giant patch that has large number of changes including binary updates. (good) (good) (bad) index HEAD switchd-to A1 A1 A2 B2 B1 B2 C2 C1 C2 D2 D1 D2 E1 E1 E2 ... I accidentally switch to the switched-to branch without committing the intermediate status. The 'work' was gone, and I had no idea how to bring it back. > By the way, the first six lines of your original message that describes > "saving and applying" is not correct. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: git-checkout silently throws away the dirty status of index without a warning? 2011-09-01 18:28 ` Tzu-Jung Lee @ 2011-09-02 1:52 ` Andrew Ardill 0 siblings, 0 replies; 6+ messages in thread From: Andrew Ardill @ 2011-09-02 1:52 UTC (permalink / raw) To: Tzu-Jung Lee; +Cc: Junio C Hamano, git > I accidentally switch to the switched-to branch without committing the > intermediate status. > The 'work' was gone, and I had no idea how to bring it back. Forgive me if I am missing something here, but can't you use reflog to go back to the point just before you checked the switched-to branch out? I guess the question is in general, how do you revert to a previous instance of the working index AND previous checked out branch? I don't know the 'switch branch with modified working index' mapping well enough to be able to determine if there is a bijection possible, a concise description of it would probably show us. If not, the only way to achieve this is to keep the history available. Regards, Andrew Ardill ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2011-09-02 1:53 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-09-01 15:47 git-checkout silently throws away the dirty status of index without a warning? Tzu-Jung Lee 2011-09-01 16:56 ` Jeff King 2011-09-01 17:50 ` Tzu-Jung Lee 2011-09-01 17:11 ` Junio C Hamano 2011-09-01 18:28 ` Tzu-Jung Lee 2011-09-02 1:52 ` Andrew Ardill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).