threads / discuss / 6618

Instructions concerning detached head lead to lost local changes

Subject: Instructions concerning detached head lead to lost local changes

## tl;dr

9 messages between Feb 1, 2007 and Feb 2, 2007.

replies: 8people: 5as markdown or json

Carl Worth· Feb 1, 2007, 22:51 UTC · lore

So after some recent experiments to explore all this detached head stuff, I found myself making a change to my code having forgotten I had some old state checked out.

So, I tried doing something like:
	git checkout master
in order to get where I wanted to be. I was stopped with the warning:
	You are not on any branch and switching to branch 'master'
	may lose your changes.  At this point, you can do one of two things:
	 (1) Decide it is Ok and say 'git checkout -f master';
	 (2) Start a new branch from the current commit, by saying
	     'git checkout -b <branch-name>'.

At this point, I _knew_ that I didn't care about the state I was leaving, (it was something random that I had done for testing). So I definitely didn't want to name it with option (2).

I also knew I hadn't done any commits while detached, which is what I thought the warning was all about. So I wasn't worried about losing nothing with option (1).

But I found that after "git checkout -f master" I lost the dirty changes I had made. And I don't see anything in the message above telling me a better way to get from the state I was in to the state I wanted.

I don't know if the "-f" always existed for git-checkout, or if it was added to support this warning. But clearly, the current warning and suggestion did not help in this case.

And what would have worked great is if the original checkout had just worked and carried along my dirty changes, (just as would have happened if I found I had accidentally made changes while on the wrong branch instead of accidentally making changes while detached).

Can we fix this please?
-Carl
Linus Torvalds· Feb 1, 2007, 23:49 UTC · re: Carl Worth · lore

Re: Instructions concerning detached head lead to lost local changes

On Thu, 1 Feb 2007, Carl Worth wrote:
Show 5 quoted lines
> 
> But I found that after "git checkout -f master" I lost the dirty
> changes I had made. And I don't see anything in the message above
> telling me a better way to get from the state I was in to the state I
> wanted.
Yeah, I think "-f" was a mistake. 

There was already real meaning for "-f": force update of all the files, regardless of whether they are dirty or unmerged or anything else.

And that meaning is totally independent of the new meaning of -f, which says "drop the old detached head". Sometimes you might want to use them together, but they shouldn't really be tied together.

Maybe something like this, which adds "--drop" as the flag to say "ok to drop the old detached head"? I'm not married to that name, it's meant more as an RFC than as anything to be applied as-is.

NOTE! This makes "-f" _imply_ "--drop" too, since it does make sense that if somebody just wants to force it, he can - regardless of any dirty state, regardless of the current state of the HEAD.

But using just "git checkout --drop new-head" will have the old "dirty state follows along with branch changes" semantics.

		Linus

PS. I don't think "--drop" really makes much sense as a name. Especially when written out like that, it sounds like we're dropping the *new* branch, not the old one. Maybe something like "--no-check" instead?

---
diff --git a/git-checkout.sh b/git-checkout.sh
index deb4795..8e852df 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -11,6 +11,7 @@ oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
 new=
 new_name=
 force=
+drop=
 branch=
 newbranch=
 newbranch_log=
@@ -36,10 +37,14 @@ while [ "$#" != "0" ]; do
 		;;
 	"-f")
 		force=1
+		drop=1
 		;;
 	-m)
 		merge=1
 		;;
+	--drop)
+		drop=1
+		;;
 	--)
 		break
 		;;
@@ -163,7 +168,7 @@ If you want to create a new branch from this checkout, you may do so
 elif test -z "$oldbranch" && test -n "$branch"
 then
 	# Coming back...
-	if test -z "$force"
+	if test -z "$drop"
 	then
 		git show-ref -d -s | grep "$old" >/dev/null || {
 			echo >&2 \
Junio C Hamano· Feb 2, 2007, 01:41 UTC · re: Carl Worth · lore

Re: Instructions concerning detached head lead to lost local changes

Carl Worth <cworth@cworth.org> writes:
Show 7 quoted lines
> At this point, I _knew_ that I didn't care about the state I was
> leaving, (it was something random that I had done for testing). So I
> definitely didn't want to name it with option (2).
>
> I also knew I hadn't done any commits while detached, which is what I
> thought the warning was all about. So I wasn't worried about losing
> nothing with option (1).

Well, the wording could probably be improved because you _did_ care about the state you were leaving (the "state" is not just what commit HEAD points at), but the suggestions did make it sound as if your had to choose between only these two, and neither would have worked for you.

> Can we fix this please?
The question is how.

We can admit that suggestions are just suggestions and listing only two is more confusing than not saying anything concrete.

Alternatively we could add yet another suggestion that let's you discard the detached HEAD but still keep your local changes. Either --drop by Linus renamed to some sensible name, or "the obscure but useful trick".

I dunno.
Carl Worth· Feb 2, 2007, 02:00 UTC · re: Junio C Hamano · lore

Re: Instructions concerning detached head lead to lost local changes

On Thu, 01 Feb 2007 17:41:11 -0800, Junio C Hamano wrote:
> > Can we fix this please?
>
> The question is how.
Why not just allow the checkout when there's nothing to lose??
Is there some technical reason this can't be done?

It's exactly what I wanted in this case and solves all problems about how to word the message and what flags to require by not printing any message and not requiring any flags.

What am I missing here? This seems quite obvious.
-Carl
Carl Worth· Feb 2, 2007, 02:06 UTC · re: Junio C Hamano · lore

Re: Instructions concerning detached head lead to lost local changes

On Thu, 01 Feb 2007 17:41:11 -0800, Junio C Hamano wrote:
> The question is how.
I replied already about the no-warning-necessary case.
> Alternatively we could add yet another suggestion that let's you
> discard the detached HEAD but still keep your local changes.
> Either --drop by Linus renamed to some sensible name, or "the
> obscure but useful trick".

But, when the warning is necessary, then I would recommend changing the -f suggestion to instead suggest --drop, (or whatever it ends up getting named---I don't care much at all). I think it's fine (and safer) to just remove the -f option altogether, rather than adding a third option.

-Carl
Nicolas Pitre· Feb 2, 2007, 02:49 UTC · re: Junio C Hamano · lore

Re: Instructions concerning detached head lead to lost local changes

On Thu, 1 Feb 2007, Junio C Hamano wrote:
Show 6 quoted lines
> Alternatively we could add yet another suggestion that let's you
> discard the detached HEAD but still keep your local changes.
> Either --drop by Linus renamed to some sensible name, or "the
> obscure but useful trick".
> 
> I dunno.

I have a feeling that proper reflog for HEAD would make the issue so much simpler.

But in the mean time I tend to agree with Ted about the fact that simply losing the detached position is not _that_ important. If you go to HEAD^ and want to come back to master I think it should just work (with a display of what the last position was as Carl mentioned).

It might be some work to get to a given position with a detached head and this very position might be valuable information, but if you then do "checkout HEAD^" you will still be detached but your previous position is lost just like it would be if you moved to master. Yet you're not prevented from going to HEAD^ but you are prevented from going to master.

Same issue if you perform a commit on top of a detached head. Nothing prevents you from doing "checkout HEAD^" which will leave your head detached but it will also silently drop your last commit dangling.

In short I think there is no magic solution other than proper reflog for HEAD.

Nicolas
Theodore Tso· Feb 2, 2007, 06:59 UTC · re: Nicolas Pitre · lore

Re: Instructions concerning detached head lead to lost local changes

On Thu, Feb 01, 2007 at 09:49:27PM -0500, Nicolas Pitre wrote:
Show 6 quoted lines
> It might be some work to get to a given position with a detached head 
> and this very position might be valuable information, but if you then do 
> "checkout HEAD^" you will still be detached but your previous position 
> is lost just like it would be if you moved to master.  Yet you're not 
> prevented from going to HEAD^ but you are prevented from going to 
> master.
Exactly.  With Junio's reasoning, then why aren't we forcing -f in this sequence:

git checkout HEAD^ git checkout HEAD^^ git checkout HEAD^^^ git checkout -f master

The first three are just as likely to "lose" information as the last. Personally, I don't think any of this is "losing" information, any more than I "lose" information in the following sequence of commands:

cd /usr/src/linux/drivers/net cd /usr/src/linux/drivers/char cd /usr/src/linux/fs/ext3 cd /home/tytso

The current working directory is just like the detached HEAD. If I'm moving it around, there is no loss of data. cd != rm.

						- Ted
Nicolas Pitre· Feb 2, 2007, 07:14 UTC · re: Theodore Tso · lore

Re: Instructions concerning detached head lead to lost local changes

On Fri, 2 Feb 2007, Theodore Tso wrote:
Show 26 quoted lines
> On Thu, Feb 01, 2007 at 09:49:27PM -0500, Nicolas Pitre wrote:
> > It might be some work to get to a given position with a detached head 
> > and this very position might be valuable information, but if you then do 
> > "checkout HEAD^" you will still be detached but your previous position 
> > is lost just like it would be if you moved to master.  Yet you're not 
> > prevented from going to HEAD^ but you are prevented from going to 
> > master.
> 
> Exactly.  With Junio's reasoning, then why aren't we forcing -f in this sequence:
> 
> git checkout HEAD^
> git checkout HEAD^^
> git checkout HEAD^^^
> git checkout -f master
> 
> The first three are just as likely to "lose" information as the last.
> Personally, I don't think any of this is "losing" information, any
> more than I "lose" information in the following sequence of commands:
> 
> cd /usr/src/linux/drivers/net
> cd /usr/src/linux/drivers/char
> cd /usr/src/linux/fs/ext3
> cd /home/tytso
> 
> The current working directory is just like the detached HEAD.  If I'm
> moving it around, there is no loss of data.  cd != rm.

It's just that moving around amongst thousands of commits to pin point a particular commit might require some digging work. This is why there might be some value in a particular position and why there is an attempt at protecting that "work".

But since moving to another position while still remaining detached from any branch has the same potential for losing the important position and so without any kind of protection then it makes no sense to have such a protection when moving back to a branch.

Nicolas
Junio C Hamano· Feb 2, 2007, 07:44 UTC · re: Nicolas Pitre · lore

Re: Instructions concerning detached head lead to lost local changes

Nicolas Pitre <nico@cam.org> writes:
Show 9 quoted lines
> It's just that moving around amongst thousands of commits to pin point a 
> particular commit might require some digging work.  This is why there 
> might be some value in a particular position and why there is an attempt 
> at protecting that "work".
>
> But since moving to another position while still remaining detached from 
> any branch has the same potential for losing the important position and 
> so without any kind of protection then it makes no sense to have such a 
> protection when moving back to a branch.
Thanks -- I'd agree.

Also I agree that "reflog on HEAD" is the right direction as it makes these 'protection' unnecessary. I've applied all your patches from today on the topic, along with Johannes's "log -g @{now}".

← back to recent threads