Re: [PATCH] Detached HEAD (experimental)

Previous thread: www.kernel.org/git by Aneesh Kumar K.V on Tuesday, January 2, 2007 - 12:12 am. (3 messages)

Next thread: Re: Draft v1.5.0 release notes by davidk on Tuesday, January 2, 2007 - 12:22 am. (2 messages)
From: Junio C Hamano
Date: Tuesday, January 2, 2007 - 12:45 am

This allows "git checkout -d v1.4.3" to detach the HEAD from any
branch but point directly at the named commit.  After this, "git
branch" starts reporting that you are not on any branch.  You
can merge into "current branch" although there is not even such
a thing.

You can go back the normal state by switching to an existing
branch, say, "git checkout master" for example.  Another way to
get out of this is "git checkout -b newbranch".

This is still experimental.  While I think it makes sense to
allow commits on top of detached HEAD, it is rather dangerous
unless you are careful and know what you are doing.  Next "git
checkout master" will obviously lose what you have done, so we
might want to require "git checkout -f" out of a detached HEAD
if we find that the HEAD commit is not an ancestor of any other
branches.

On the other hand, the reason the user did not start the ad-hoc
work on a new branch with "git checkout -b" was probably because
the work was of a throw-away nature, so the convenience of not
having that safety valve might be even better.  We'll see.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c |   36 ++++++++++++++++++++++++++----------
 cache.h          |    2 +-
 git-checkout.sh  |   22 +++++++++++++++++++---
 path.c           |   26 ++++++++++++++++++--------
 setup.c          |    5 +++--
 5 files changed, 67 insertions(+), 24 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 745ee04..71f88f2 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -299,7 +299,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
 	free_ref_list(&ref_list);
 }
 
-static void create_branch(const char *name, const char *start,
+static void create_branch(const char *name, const char *start_name,
+			  unsigned char *start_sha1,
 			  int force, int reflog)
 {
 	struct ref_lock *lock;
@@ -318,9 +319,14 @@ static void create_branch(const char *name, const char *start,
 			die("Cannot force update the current ...
From: Edgar Toernig
Date: Tuesday, January 2, 2007 - 12:59 pm

[Note: casual git-user speaking]



This is fine.  Often you want to test a couple of tags, one
after another, so "git checkout v1", "git checkout v2", ...

Wth should this do?  I already noticed this line in your posting
from 29.Dec: [slightly edited]

	$ git checkout v1.5.0
	Checking out a tag -- you are not on any branch now...
	$ <modify>
	$ git commit -m 'fix' -a
	You cannot commit without a current branch.
	$ git checkout -b maint-1.5.0
	$ git commit -m 'fix' -a

I assume it will create a new branch and modify HEAD so that
the current working dir/index gets committed into that branch.
(Basically "git branch main-1.5.0 &&
            echo 'ref: refs/head/main-1.5.0' >.git/HEAD")
If that's the case, I was looking for that incantation for
a long time and couldn't find it.  I'm using the git-branch
and echo as shown above to get that.  The man-page isn't
very helpful for that special case of git-checkout.

Anyway, if I want to commit and git tells me that I can't
because I'm not on a branch, the _most unintuitive_ thing
would be calling 'git-checkout'.  I want to checkin!  No
way that I call checkout and risk losing all my changes.

What's wrong with a -b option to commit, similar to -b on
checkout?

	$ git checkout v1.5.0
	Checking out a tag -- you are not on any branch now...
	$ <modify>
	$ git commit -m 'fix' -a
	You cannot commit without a current branch.
	Give '-b <newbranch>' to commit into a new branch. 
	$ git commit -b maint-1.5.0 -m 'fix' -a

Another variant (the one I prefer): commit just updates HEAD and
git-branch can be used to give it a name (and switch to it!).
So these workflows would be possible:

Name after commit:

	$ git checkout v1.5.0
	Checking out a tag -- you are not on any branch now...
	$ git branch
	  master
	* (unnamed) c8ff51290518949225c832bae1e22b1bba6ab2cd
	$ <modify>
	$ git commit -m 'fix' -a
	Warning: data committed into unnamed branch.
	Give it a name now with "git branch <newname>"
	$ git branch
	  master
	* ...
From: Carl Worth
Date: Tuesday, January 2, 2007 - 2:56 pm

Being able to perform "checkout" with a tag like this, (and no
specific branch), is something I've been wanting git to acquire for

This part I don't understand. I don't see why it's useful to introduce
new danger to "git checkout" in that after this change it could cause
commits to become dangling. Currently, "git checkout" is entirely
safe, as are most git commands. The few commands that create dangling
commits require fairly explicit actions from the user, (such as
"--hard" for git-reset or -D instead of -d for git-branch).

So I'd vote against this aspect. I'd rather see commits to a detached
head be disallowed with a message instructing the user to do "git
checkout -b new-branch" in order to do the commit.

And with that new safer behavior, I think it would be a good idea to
just drop the "-d" option from git-checkout.

I want this new behavior not for people who know what they are doing,
but people who are using git only incidentally, (say they just want to
acquire and build the latest version of some software). So I'd like
the sequence to work along the lines of your earlier post, (as quoted
by another reply). Specifically, I wouldn't want to see any warning
about a "missing branch" until a commit was attempted.

This would allow a sequence like this to proceed without git ever
telling the user they were doing something "wrong":

	$ git clone git://git.kernel.org/pub/scm/git/git.git
	$ cd git
	$ git checkout v1.4.3
	$ make

With the recent improvements to the git-checkout error message
(thanks!) this sequence is at least successful eventually after the
user reads and responds to the following:

	git checkout: provided reference cannot be checked out directly

	  You need -b to associate a new branch with the wanted
	  checkout. Example:
	  git checkout -b <new_branch_name> v1.4.3

But the user is required to invent a name and deal with its existence
later. For example, after some time, imagine the same user wanting
to update to the latest and build ...
From: Junio C Hamano
Date: Tuesday, January 2, 2007 - 3:44 pm

I am not saying being risky is useful.  That's why I said it is
experimental.

We could do two things, and I think disallowing commits is not
necessarily a better option of the two.  We could allow commits
and prevent the user from switching out of the detached HEAD
state without an explicit action instead.  If we go the first
route, you need to also prevent merges into the detached HEAD.
If we go the latter I think you only need to add a check in
"git-checkout" but there may be other cases.  In either way, we
need a safety valve, which the experimental code does not have.

And being able to merge into the detached HEAD turns out to be
somewhat useful.  I checked out the v1.4.4.3 and tried to see if
a topic is applicable by merging into that detached HEAD and
running testsuite.  Of course, without any safety valve, I can
easily lose the merge result by switching out of the detached
HEAD state (say, "git checkout master"), but on the other hand,
creating a new branch at that point with "git checkout -b
v1.4.4.3-maint" would let me continue from that point without
losing anything.

But this is only "somewhat" -- I do not have strong opinion
either way, other than that we need a safety valve (which we
agree).

In any case, I did this because I got tired of waiting for it to
happen (I thought you wanted to hack on this over the long
week^W yearend, so I deliberately stayed away from doing this)
and I was bored.  This will not be in 'next' in the current
shape.

You've thought about the issue long enough to write your
commentary and I agree to most of your points (including
favoring "no commit allowed in this state" over "allow commits
and merges to help advanced usage" for its simplicity), so if
you code it up with a clean patch, I would not reject it on the
basis of its design.


-

From: Carl Worth
Date: Tuesday, January 2, 2007 - 4:34 pm

Yeah, that would be fine too. Personally, I'd be happy with either

OK. I guess I misinterpreted things. I was afraid that you were
proposing a safety valve on _entering_ the detached state, (perhaps
the -d option to checkout itself). It was the requirement of something
extra to checkout a tag (as opposed to checkout of a branch) that I

I'm glad you went ahead. I ended up almost not touching computers at

I don't actually prefer "no commit allowed". I just didn't want the
user to have to explicitly disable the safety before being able to
perform a checkout based on a tag.

I am still interested in this feature, so I will try to find time to
come back with a revised version of your patch with the missing safety
check (and without requiring -d on checkout). Thanks again for this
initial take on the problem. (Though if anyone else beats me to it, I
certainly will not be offended.)

-Carl

[*] I did play some nice new (to me) board games, (Zendo and DVONN
being standouts), but thats a topic for elsewhere I suppose.
From: Junio C Hamano
Date: Tuesday, January 2, 2007 - 7:45 pm

Ok, then I think we are in agreement that the safety should be

Sounds good.  

I do not mind losing -d, but I would suggest that there should
be a message that says "you are no longer on any branch" after a
checkout that makes the head detached.  The user should be
warned about such an unusal situation.

-

From: Junio C Hamano
Date: Monday, January 8, 2007 - 4:19 am

I decided to fast-track this one.  With a handful fix-ups, this
is now at the tip of 'next'.

The primary difference from the one we discussed, and then has
been sitting in 'pu', is that coming back from the detached HEAD
state is allowed only with '-f' or to a branch that is a
fast-forward of HEAD.

So you can do:

	git checkout v1.2.0 ;# detach
        ... look around ...
        git checkout v1.4.0 ;# still detached
        ... look around ...
        git checkout master ;# Ok, because v1.4.0 is an ancestor of master

but you would be warned and asked to say -f if you do:

        git checkout v1.4.0 ;# detach
        edit ...
        git commit -a -m 'some tweak'
	git checkout master ;# Not Ok -- you may lose that commit.

An alternative exit in this case is to create a new branch at
that point.  So this does work:

        git checkout v1.4.0 ;# detach
        edit ...
        git commit -a -m 'some tweak'
	git checkout -b maint-1.4.0 ;# start the maint-1.4.0 branch

Have fun.

-

From: Jeff King
Date: Monday, January 8, 2007 - 6:17 am

I haven't seen the code, waiting for kernel.org to mirror, but I have a

Hrm. So does that mean this doesn't work (without -f):

  git checkout v1.4.0
  ... look around ...
  git checkout v1.2.0

I think a better (but more expensive) check would be "coming back from
the detached HEAD is allowed only with '-f' or if HEAD is an ancestor
of any non-HEAD ref."

-Peff
-

From: Junio C Hamano
Date: Monday, January 8, 2007 - 5:19 pm

That should work.

The first checkout, because there is no branch v1.4.0, makes the
HEAD detached.  You are no longer on any branch at that point,
and "git checkout v1.2.0" that follows do not trigger the check
which is about "coming back from the detached HEAD state".

But I would probably do the second v1.2.0 "checkout" with "git
reset --hard", if what I am doing is "wandering, looking around
to see different commits".

-

From: Carl Worth
Date: Monday, January 8, 2007 - 5:43 pm

So what's the final check? Is it "can come from detached HEAD to a
branch only if the detached HEAD is reachable from the target branch"?

If so, that's still a trap for people who are just exploring with "git

You would probably do this, yes, but is it what you would recommend
in a tutorial for new users doing read-only exploration of old
versions of some piece of software? One of the main reasons I'm
interested in the "detached head" stuff is so that such users can use
"git checkout" to explore any revision and never have to worry about
doing anything "wrong", (never leave any commits dangling), nor ever
have to see any "scary" message, (ie. "use checkout -f if you know
what you are doing").

-Carl

PS. Thanks for giving detached head some attention---I'm chronically
inept at getting into actual git development like I'd really like to.
From: Junio C Hamano
Date: Monday, January 8, 2007 - 6:05 pm

An obvious alternative is not to allow building on top of a HEAD
that is detached at all, which I suggested initially.

A non-alternative is to silently lose commits, which you seem to
be suggesting, but I would rather play it safe.

The wording used for current warning that says "use checkout -f"
is horrible, and it needs to be reworded much better, but other
than that, I think playing safer is much better than making a
worse trap of silently losing the commits they may make before
they come to understand how "a branch" works.

-

From: Carl Worth
Date: Monday, January 8, 2007 - 6:15 pm

That sounds great to me. Let's just suggest "checkout -b" before
commit, (maybe even add new "merge -b <newbranch>" and "commit -b
<newbranch>" so the suggest command could be even closer to what the

No, I would never want that. We both agreed earlier that a safety
valve is necessary.

I just want to make sure that people that never actually need it don't
have to see the message. And I don't think that _that_ part would be
feasible with the safety valve at the point of "leaving detached
state". It would basically come down to having to do reachability
analysis for the current HEAD from all known branches or something
equally horrific.

So let's put the safety valve where it's cheap to detect and where we
know it will distinguish between read-only and read-write use, (that
is, put it precisely at the point where there's an attempt to create a
new commit object while in the detached state).

-Carl
From: Shawn O. Pearce
Date: Monday, January 8, 2007 - 8:26 pm

The common case is probably going to be where the argument to
`git checkout` is a fast-foward of the detached HEAD.  And that's
pretty cheap to check.  So we perform that check, and if we fail
that then we search through every ref to determine if the detached
HEAD is fully contained in any of those.  Currently that would be
pretty slow to do with the current tools, but a small modification
of say git-merge-base (or git-describe) might make it cheap enough
to run during this slightly less common case.

No need to complicate merge/am/rebase/revert/commit/applymbox
with a -b option.

-- 
Shawn.
-

From: Junio C Hamano
Date: Tuesday, January 9, 2007 - 12:07 am

The needed change to merge-base is quite minimum.  Let me come
up with a patch...

-

From: Luben Tuikov
Date: Tuesday, January 9, 2007 - 1:12 am

Yes, I agree.

From my point of view, the question is where does it "go" committing
changes on top of a "detached HEAD".  Commits shold probably be only
allowed on top of local branches, since creating the branch itself
shows an intention, possibly intention to do work, to commit
new things.

    Luben

-

From: Jeff King
Date: Tuesday, January 9, 2007 - 7:21 am

Oh, that's even worse, since the safety valve doesn't kick in when it
should. For example, with what's in next now, I can do this:

  git checkout v1.4.0
  hack hack hack
  git commit -m -a 'some changes which will never be seen again'
  git checkout v1.2.0


As Carl mentioned, I think recommending that workflow is a terrible idea
from a user interface perspective.

-Peff
-

From: Junio C Hamano
Date: Tuesday, January 9, 2007 - 2:20 pm

Fair enough.

We could always do the check upon "git checkout" from a detached
HEAD state, whether it takes you back on some existing branch or
leaves your HEAD still detached.

-

From: J. Bruce Fields
Date: Tuesday, January 9, 2007 - 2:31 pm

Stupid question: why can't checkout do something like this?

	if we're currently not on a branch, fail if .git/PREV
		doesn't point to the same commit as .git/HEAD.

	if we're checking out a non-branch, store its SHA1 into
		.git/PREV.

So the user gets a warning (overrideable with some kind of --force
option) if they do a checkout when the HEAD isn't exactly what they last
checked out.  Then

	git checkout master
	git checkout v1.4.0
	git checkout v1.2.0
	git checkout master

all works without complaints, but the example above gives a warning at
the "git checkout v1.2.0" point.

--b.

-

From: Carl Worth
Date: Tuesday, January 9, 2007 - 2:43 pm

I would guess the problem is that this would still cause warnings even
if the user had since given a name (created a branch) for the commits
originally made to the dangling head.

Frankly, I don't understand why so much effort is being put toward
allowing these "fragile commits" to be made in the first place. Why
not require users to name the branch before creating any commits, just
as has always been the case?

To me, the only real advantage to the new "detached head" stuff is
simply making it easier to checkout previous state without having to
name a new branch precisely _because_ the user has not intent to
commit anything. If the user is going to commit something, then the
user should be able to come up with a name for the branch.

But, whatever, if allowing fragile commits is seen as important by
those doing the work, who am I to complain about that? I'd just ask
that the following not be made slow:

	git checkout commit-from-beginning-of-time
	git checkout master

Thanks to the index, and the simplicity of what "git checkout" means,
checkout has always been blisteringly fast. All the talk of doing
reachability analysis scares me from a performance point of view,
(particularly when the _interesting_ cases (to me) of checkouts to
non-branches never need this anyway---since no commits will be made).

Thanks,

-Carl
From: J. Bruce Fields
Date: Tuesday, January 9, 2007 - 2:53 pm

I think as long as we provided a special exception for a case like "git
checkout -b":

	git checkout v1.4.0
	hack hack hack
	git commit -m -a 'some changes'
	git checkout -b new-changes

and also provide a way out (--force-checkout-losing-current-head) for
people that really know what they're doing, that should be more than
enough to handle that sort of case.

Because, I agree, the point is to make easy what 90% of users will
probably do, at least on the first encounter with git--download project
X, checkout version Y, build--and making checkouts on detached commits
convenient seems a lower priority.

--b.
-

From: Shawn O. Pearce
Date: Tuesday, January 9, 2007 - 4:44 pm

The safety valve I was proposing would be only the additional time of
running `git merge-base commit-from-begging-of-time master` to verify
the former is completely contained in the latter.  That's going to
be true, and is a relatively fast operation (roughly linear in time
with the length of the history).

However in this case:

  git checkout v1.5.0
  git checkout v1.2.0

would take slightly longer as we'd have to verify that the HEAD
from the first checkout is contained in an existing tag/ref.
Which it is.  Since its probably exactly equal to one of those
dereferenced tags this may just wind up being the cost of scanning
the .git/packed-refs file.  You do pack your refs, don't you?

In my mind that is a small price to pay for making sure the
commit currently in a detached HEAD doesn't get orphaned off
into never-never land.

-- 
Shawn.
-

From: Andreas Ericsson
Date: Wednesday, January 10, 2007 - 2:08 am

Agreed. Possibly, we could have commit (or commit-tree) issue a big fat 
warning along the lines of:

*** WARNING ***
You are about to create a commit on a detached HEAD.
It is recommended that you run "git branch <name>" to create a branch to 
commit to first. If you don't, you might lose this commit further on.
*** WARNING ***

which could be suppressed by a "--silently-ignore-detached-head" in case 
scripts (securely) use this behaviour. Since committing on detached 
heads really should be a very rare case I don't think many people will 

Indeed and as I've said before, *all* developers have "silly-names" they 
use for temporary stuff (foo, bar, frotz, nitfol, blaj, fnurg, sdf, ...) 
so it's not like we'll put a heavy burden on peoples imagination.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231
-

From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 2:46 am

Quite the contrary, I would imagine it would be quite natural to
do throw-away commits and merges on detached head while
bisecting the history (e.g. commit small fixup to make it
compile and then mark the result for bisection to hunt for real
bugs that are hidden by silly compilation problems).  

The check suggested by Linus would be safe enough for people to
whom it is "very rare" for their workflow to commitg on detached
HEAD anyway, so you should not burden "git commit" with such an
annoying warning messages.


-

From: Daniel Barkalow
Date: Wednesday, January 10, 2007 - 9:30 am

I don't think this would actually work. If you commit your build fix, and 
then mark the result as bad, won't bisect skew its choices due to 
suspecting that your build fix is the real bug?

I'd think that, if you make changes while bisecting, you probably want to 
leave those changes uncommitted, and merge or discard them when testing 
other commits.

If anything, I'd think you'd want a rather different sort of commit 
mechanism than the usual commit, which says, "whenever you consider commit 
{sha1-from-real-history}, use {tree-with-local-changes} instead of 
{tree-in-real-commit}." Or, more generally, "in order to get the trees 
I want to actually use, this patch (git diff HEAD) needs to be applied to 
every commit in some portion of the history including, at least, 
get_sha1(HEAD)".

I'm not seeing any actual benefit to causing the history to contain a 
dead-end fork off of an antique commit, and then throwing this away. And 
committing your change so that it won't get lost, with the intention of 
losing it in a little while, doesn't seem to make any sense, either.

(Of course, it also makes sense to do merges, but again, you probably want 
to create and temporarily use the working tree resulting from the merge, 
not create the commit.)

I think that the workflow that uses regular commits with a detached HEAD 
is this: do a series of commits representing real work on top of a remote 
branch or a tag, and decide later (once you've tested the results for 
worthiness) whether to turn this into a topic branch or throw it away.

But I don't think this is a good match for detached HEAD, because you may 
want to do exactly the same thing, but start with a regular local head. I 
think the right thing to do is something like "git checkout --anon", which 
puts you on a new branch with no name, which will evaporate if you leave 
it (as per "git branch -d"; you need to force it if it isn't fully 
merged).

So I think the feature which lets you make commits without being on a ...
From: Andreas Ericsson
Date: Thursday, January 11, 2007 - 2:45 am

Same here. I'd imagine temporary build-fixes to live as a patch-file 
generated by

	git diff > build-fixes.diff

after having hacked on the tree. There's no sane way of inserting 
commits into the middle of the DAG, so committing on something that 

Yes. I'd imagine "git merge --no-commit" could be used for this, to 

Perhaps, but this is also a bit weird, as you would normally hack things 
up to fit on top of some already existing branch, so then you'd detach 
the head but point it to something that already has a branch-name 
associated with it.

Otoh, I could imagine this would be sort of nifty for applying bugfixes 
on top of old tags, so perhaps it's not so weird after all. Then you'd 
probably want to create a new tag before releasing the bugfixed version, 
so Linus suggestion makes sense in this case (assuming it doesn't fsck 


Yes. I'd imagine "git merge --no-commit" could be used for this, to 
merge things only in the working directory. We could easily create a 
hack for this by doing a "git reset --mixed HEAD^1" after the merge is 


Agreed. They really are two completely different things. I see no harm 
in splitting them up codewise. Bisect could start working without its 
protected branch straight away, but commits (and merges) to detached 
heads wouldn't work at all. Then we can see what use people put this to 
and what walls they run into and make the feature accordingly.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231
-

From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 2:40 am

Then we would not be talking about detached HEAD at all.  Why
not require users to name the branch if they want to check out
what they should not be able to in the first place?

Convenience.

Some features of git are about being convenient by allowing you
to defer the decision.  You can start mucking with the working
tree files without knowing where it leads to and then from that
point with the dirty working tree state decide to fork what you
have started using "checkout -b newbranch".  Even though you may
have many dirty files in the working tree, you can selectively
update index (especially with the patch subcommand of the
interactive git-add) to prepare for commit -- you do not choose
what to edit, but you defer the decision of what to include in
the commit.

-

From: Junio C Hamano
Date: Tuesday, January 9, 2007 - 3:37 pm

I do not want to think about the consequences of adding more
cruft under .git/ directory.  For example, should PREV be
noticed by fsck and prune?  What should various forms of
'git-reset' do with it?  How does it interact with 'git-bisect'?

Being able to test merge or even make commits without being on a
branch is vastly useful.  It might or might not lead to anywhere
even after you make a handful commits -- and I would imagine
that it would be very handy to be able to be lazy and not having
to decide if it is worth a new branch.

But that may be just my imagination; I generally prefer any
feature that allows me to defer decision over something that
makes me decide early.  If Carl wants to do a patch to teach
'git-commit' (and all other things that can create commits) not
to do things from working in a detached HEAD, I would probably
not opposed to it too much, but I am fairly certain that I won't
be coding it myself.

It's tempting to forget about this whole "safety" business.
Because we allow "reset --hard" and other forms of operations
that can lose history if they were done while on a branch, only
giving the safety to "git checkout" feels somewhat silly.  And
the primary motive for detached HEAD as I understand it is for
sightseeing, and not allowing "reset --hard" to jump around is
just plain silly.

That is, after:

	git checkout v1.4.0

you are not on any branch, and we would still allow

	git reset --hard v1.2.0

which is exactly the same as:

	git checkout v1.2.0

You can still say:

	git checkout master

and we do not even check.

Which makes the "merge-base --check-ancestry" stuff I did last
night pretty much unnecessary, but that's Ok.  It will find
other uses.

-

From: Shawn O. Pearce
Date: Tuesday, January 9, 2007 - 4:39 pm

I agree.  The reachability list for those is already starting to
get out of control, and the rules for making sure those files are
always in sync with every command is getting crazy.  Didn't we just
fix `git reset --hard` to throw away .git/MERGE_MSG?  That's been
a longstanding bug right there, and that's something that has been

I agree.  I'm always creating and deleting `foof` because I need
someplace to work real quick.  Being able to work on a detached HEAD
would just slightly streamline the process, especially given that
`git checkout -b a-real-name` is readily available to move that

My concern here is to hit all of the corner cases.  reset.  bisect.
am.  rebase.  merge.  cherry-pick/revert.  Did I get all of 'em?

But isn't the --hard switch the safety valve here?  And lets not
forget that reflogs are enabled by default now so even a `reset
--hard` on a real branch isn't a total loss (its only a loss for
uncommitted files in the working directory).

But a detached HEAD has no reflog. Which means operations that
update it in a non-fastforward way would orphan work.  A subsequent
gc/prune/repack might destroy it, unless an existing ref contains

Pity.  It looked like it was a good change and would be useful here
as a safety valve.  Though based on what you said above I would
think we'd actually want it in both checkout and reset (--soft and
--hard versions).

-- 
Shawn.
-

From: Linus Torvalds
Date: Tuesday, January 9, 2007 - 4:46 pm

Yes. I think the detached head notion is really really important. I think 
it was a mistake to not allow it initially, but hey, there were various 
historical reasons, and the whole thing about how branches worked was a 
bit up in the air.

I would suggest a solution:

 - git checkout will refuse to switch AWAY from a detached head unless the 
   SHA1 of the detached head exactly matches some other branch.

Not any expensive "reachability" cheaks. Simple and straightforward: just 
say "no, I will not leave this branch-less HEAD behind, because it is not 
described by any other branch or tag".

So if you do

	git checkout v1.4.4

you'll be fine, because even though you got a detached HEAD that isn't 
attached to any branch, it still exists as a tag, so checking out 
something else is fine - you've not lost any state.

In contrast, if you actually start committing to that detached HEAD, you 
need to either

 - use some new flag ("git checkout --forget-old") to explicitly say that 
   you _want_ to leave this old naked branch behind

 - either tag the current point or make a real branch out of it (with 
   either "git tag <tagname>" or "git branch <branchname>" respectively) 
   and then you can check out some other tag/branch after that.

Doing "reachability analysis" is not only expensive, it's actually really 
wrong, because even if the current HEAD is _reachable_ from some other tag 
or branch, you're still going to drop that point in the development series 
unless it _exactly_ matchs it.

Hmm?

I'd love to see the detached HEAD series move into "master", but I do 
think we should make sure that people can't drop their work easily by 
mistake, and I think the above suggestion is both simple and workable.

Comments?

		Linus
-

From: Junio C Hamano
Date: Tuesday, January 9, 2007 - 5:10 pm

I agree with the "reachability is wrong -- you would lose the
point in the middle" reasoning.


-

From: Shawn O. Pearce
Date: Tuesday, January 9, 2007 - 5:18 pm

I have no problem with that.  Forget the reachability thing entirely.

My point about reset --hard/--soft probably also needing this check
still stands however.

-- 
Shawn.
-

From: Linus Torvalds
Date: Tuesday, January 9, 2007 - 5:54 pm

Well, I disagree, if only because the whole _point_ of "git reset" is to 
leave some point behind. I use it all the time (well, often enough) as a 
"undo" operation, and it's fundamentally different than "git checkout" at 
least to my worldview.

When you do "git reset" you _expect_ state to be reset/dropped. But when 
just switching between branches, you don't.

(I realize that "git checkout filename/goes/here" has kind of mixed up 
"git reset" and "git checkout". The "git checkout filename" syntax 
basically resets the filename, and that confuses things a bit. So in the 
above, I really do talk about just "checking out a _commit_" and do a 
state switch, not a "check out a filename" and overwrite the old contents 
of that file).

		Linus
-

From: Carl Worth
Date: Tuesday, January 9, 2007 - 5:51 pm

That's a nice cheap check.

But I've also been liking the idea of using this "detached head" stuff
for git-bisect, (instead of making it carry around its own temporary
branch). One long-standing user-interface bug with git-bisect is that
often the user doesn't know a priori what the last-known-good state is
to initially mark with "git bisect good". So I've long wanted a good
clean way to explore fairly arbitrarily in order to get git-bisect
jump started.

When I first started using git a year ago, what was suggested to me
for this, (and what I've used ever since), is:

	git checkout -b tmp some-guess-at-a-good-commit
	# check it
	git reset --hard next-guess-at-a-good-commit

Obviously, that works but fails the "good clean" test for me. Half
the time it fails for me and I have to "git branch -D tmp" first. Then
there's the fact that I want very new users to learn git-bisect---I
want random users that have hit bugs in my software to bisect those
bugs for me---and many of these users will have never seen git
before. I don't think it's kind to start their education with "git
reset --hard". I'd like to instead teach them something as simple as:

	git checkout some-guess-at-a-good-commit
	# check it
	git checkout next-guess-at-a-good-commit

I wouldn't want these uses to trigger warnings just because the user
is checking out arbitrary revisions from the logs rather than using
tags and branches.

But, yes, as soon as the user actually _commits_ in the detached
state, then a check for "HEAD == some branch" should be just fine for
checking a checkout to somewhere else.

-Carl
From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 1:02 am

I've done this, also added one fix and merged the topic to
"next".  I am hoping I can move this to "master" by the weekend.


-

From: Andy Parkins
Date: Wednesday, January 10, 2007 - 2:04 am

If the detached HEAD matches another branch what did we need a detached HEAD 
for in the first place?

Seems that this check will in practice always be true.  A detached HEAD by 
definition doesn't match some other branch.

Have I misunderstood?  Perhaps you meant the detached HEAD is /on/ some other 
branch?



Andy

-- 
Dr Andy Parkins, M Eng (hons), MIEE
andyparkins@gmail.com
-

From: Shawn O. Pearce
Date: Wednesday, January 10, 2007 - 2:05 am

Tags.  Previously you could not checkout a tag without first making
a branch from it.  Now you can.

-- 
Shawn.
-

From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 2:33 am

You are forgetting this:

	git checkout v1.0.0


-

From: Andy Parkins
Date: Wednesday, January 10, 2007 - 3:10 am

No I'm not.  Linus's suggested check is "git checkout will refuse to switch 
AWAY from a detached head unless the SHA1 of the detached head exactly 
matches some other branch."

My question is what use is that?  In exactly the situation you describe HEAD 
doesn't match a branch.

  git checkout v1.0.0

HEAD after that doesn't match any branch so the next "git checkout" will find 
that HEAD doesn't match any branch and will refuse to switch away.  Why?  A 
checkout in this case isn't dangerous at all.

Of course I could still be misunderstanding.  If Linus meant "refuse to switch 
AWAY from a detached HEAD unless the hash of the detached head exactly 
matches some other ref", I would be less confused.


Andy
-- 
Dr Andy Parkins, M Eng (hons), MIEE
andyparkins@gmail.com
-

From: Shawn O. Pearce
Date: Wednesday, January 10, 2007 - 3:25 am

I believe that's what Linus meant.  As otherwise you are right,
it doesn't make much sense.  :-)

-- 
Shawn.
-

From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 9:18 am

You are taking it too literally.  Read what Linus wrote again.

    So if you do

            git checkout v1.4.4

    you'll be fine, because even though you got a detached HEAD that isn't 
    attached to any branch, it still exists as a tag, so checking out 
    something else is fine - you've not lost any state.

The version in "next" does that, in a quite straightforward way:

	git show-ref -d -s | grep "$old" || { barf }

which should be fairly fast in a repository with packed-pruned
refs.



-

From: Jeff King
Date: Wednesday, January 10, 2007 - 7:04 am

What about

  git checkout HEAD~20

I agree that checking out tags will be more common, but it feels like we
are discouraging this usage by presenting spurious warning messages.

-Peff
-

From: Junio C Hamano
Date: Wednesday, January 10, 2007 - 5:34 pm

Once the user knows what HEAD~20 means, I think it is safe to
assume that the user knows what the branches are.

"git checkout master" will barf and suggests the user possible
common exits; "checkout -f" if there is nothing of value,
"checkout -b <branch>" or if they want to build on the current
state.

And once the user who knows what the branches are sees such, and
especially with the help from $PS1 hack of bash-completion in
contrib/ section, the user will learn to do "checkout -f" after
wandering around for sightseeing on a detached HEAD, and at that
point the annoying error message will not be even seen.


-

From: J. Bruce Fields
Date: Wednesday, January 10, 2007 - 9:31 pm

I'm interested of course in making life easy for project admins when
they need to tell testers how to get code to test out of git.  It'll be
nice to able to say:

	"Install git, then run
	git clone git://ourproject.com/ourproject.git
	cd ourproject
	git checkout <version you want>
	"

Instead of having to say

	"Install git, then run
	git clone git://ourproject.com/ourproject.git
	cd ourproject
	git checkout -b FOO <version you want>

	Then if you later need to check out another version, run
	git reset --hard <other version>
	"

I suppose <version you want> will typically be either some tagged
release or the latest head.  But it's not that farfetched to imagine

That should make it easy enough, though, I guess.

--b.
-

From: Jeff King
Date: Wednesday, January 3, 2007 - 3:46 am

I think you should only enact this safety valve if there have actually
been commits. Otherwise, people who are just tracking and do a
"git-checkout v1.4.0; look look look; git-checkout v1.5.0" will get a
confusing message.

Personally, I like the "don't allow commit without a branch" approach,
but only if you can "git-commit -b newbranch" and "git-merge -b
newbranch" to make it convenient to create a branch. Making commits that
aren't on any branch seems like a broken state (and indeed, you have to
use special options to get out of the state); it makes more sense to me
to never enter the state in the first place.

Also, the implementation should be conceptually simple. Put
refs/tags/v1.4.0 into HEAD on checkout. Disallow commit/merge unless
HEAD points to refs/heads/*.

Just my 2 cents...

-Peff
-

From: Jeff King
Date: Wednesday, January 3, 2007 - 4:59 am

Let me take that back. It is actually still annoying to implement, since
many things (at least commit-tree, branch) are unhappy with a non-branch
in HEAD. Moreover, it's not as flexible as simply putting the commit
sha1 into the HEAD, as you suggested. My suggestion allows only
non-branch refs to be checked-out; however, it's likely somebody might
want to git-checkout HEAD~10 or some other unnamed thing.

-Peff
-

From: Lars Hjemli
Date: Tuesday, January 2, 2007 - 4:22 pm

This makes git-branch show a detached HEAD as '* (no branch)'.

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
---

This might be a premature patch. But if/when we allow HEAD to be detached, 
git-branch should tell us that HEAD is the current 'branch'.

 builtin-branch.c |  103 +++++++++++++++++++++++++++++-------------------------
 1 files changed, 55 insertions(+), 48 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 71f88f2..16f86cc 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -231,29 +231,54 @@ static int ref_cmp(const void *r1, const void *r2)
 	return strcmp(c1->name, c2->name);
 }
 
-static void print_ref_info(const unsigned char *sha1, int abbrev)
+static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, 
+			   int abbrev, int current)
 {
+	char c;
+	int color;
 	struct commit *commit;
 	char subject[256];
 
+	switch (item->kind) {
+	case REF_LOCAL_BRANCH:
+		color = COLOR_BRANCH_LOCAL;
+		break;
+	case REF_REMOTE_BRANCH:
+		color = COLOR_BRANCH_REMOTE;
+		break;
+	default:
+		color = COLOR_BRANCH_PLAIN;
+		break;
+	}
 
-	commit = lookup_commit(sha1);
-	if (commit && !parse_commit(commit))
-		pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
-				    subject, sizeof(subject), 0,
-				    NULL, NULL, 0);
-	else
-		strcpy(subject, " **** invalid ref ****");
+	c = ' ';
+	if (current) {
+		c = '*';
+		color = COLOR_BRANCH_CURRENT;
+	}
 
-	printf(" %s %s\n", find_unique_abbrev(sha1, abbrev), subject);
+	if (verbose) {
+		commit = lookup_commit(item->sha1);
+		if (commit && !parse_commit(commit))
+			pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+					    subject, sizeof(subject), 0,
+					    NULL, NULL, 0);
+		else
+			strcpy(subject, " **** invalid ref ****");
+		printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
+		       maxwidth, item->name,
+		       branch_get_color(COLOR_BRANCH_RESET),
+		       find_unique_abbrev(item->sha1, abbrev), subject);
+	} else {
+		printf("%c ...
From: Shawn O. Pearce
Date: Tuesday, January 2, 2007 - 10:18 pm

It would be nicer if when you are on a remote tracking branch or
on a tag that the name of the tag or the remote tracking branch is
shown rather than '* (no branch)'.

-- 
Shawn.
-

From: Junio C Hamano
Date: Wednesday, January 3, 2007 - 12:05 am

I fully agree with the motivation, but 100 lines of change to
adjust only to detached HEAD seems too much.  What else is going
on in this patch, I wonder...

Can we have two patches, one for loop restructuring without
detached HEAD support, and then another to add support for it?


-

From: Lars Hjemli
Date: Wednesday, January 3, 2007 - 12:37 am

Sure, I'll do it tonight

-- 
larsh
-

Previous thread: www.kernel.org/git by Aneesh Kumar K.V on Tuesday, January 2, 2007 - 12:12 am. (3 messages)

Next thread: Re: Draft v1.5.0 release notes by davidk on Tuesday, January 2, 2007 - 12:22 am. (2 messages)