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 ...[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
* ...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 ...
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. -
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.
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. -
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.
-
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 -
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". -
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.
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. -
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
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. -
The needed change to merge-base is quite minimum. Let me come up with a patch... -
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
-
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 -
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. -
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. -
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
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. -
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. -
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 -
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. -
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 ...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 -
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. -
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. -
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. -
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 -
I agree with the "reachability is wrong -- you would lose the point in the middle" reasoning. -
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. -
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 -
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
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. -
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 -
Tags. Previously you could not checkout a tag without first making a branch from it. Now you can. -- Shawn. -
You are forgetting this: git checkout v1.0.0 -
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 -
I believe that's what Linus meant. As otherwise you are right, it doesn't make much sense. :-) -- Shawn. -
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.
-
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 -
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. -
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. -
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 -
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 -
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 ...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. -
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? -
