{"thread":{"id":"7467","subject":"[PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","startedAt":"2007-03-29T08:02:50Z","lastAt":"2007-04-04T01:55:03Z","messageCount":8,"participants":["Junio C Hamano","Xavier Maillard","Alex Riesen","Jakub Narebski","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"38289","messageId":"7vr6r879zp.fsf@assigned-by-dhcp.cox.net","threadId":"7467","inReplyTo":null,"subject":"[PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-03-29T08:02:50Z","receivedAt":"2007-03-29T08:02:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"You cannot currently checkout the tip of an existing branch\nwithout moving to the branch.\n\nThis allows you to detach your HEAD and place it at such a\ncommit, with:\n\n    $ git checkout -d master\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-checkout.txt |    6 +++-\n git-checkout.sh                |   18 +++++++++--\n t/t7201-co.sh                  |   63 +++++++++++++++++++++++++++++++++++++++-\n 3 files changed, 81 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex f5b2d50..d00eeaa 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch\n SYNOPSIS\n --------\n [verse]\n-'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l]] [-m] [<branch>]\n+'git-checkout' [-q] [-f] [-b [--track | --no-track] <new_branch> [-l] | -d] [-m] [<branch>]\n 'git-checkout' [<tree-ish>] <paths>...\n \n DESCRIPTION\n@@ -61,6 +61,10 @@ OPTIONS\n \tall changes to made the branch ref, enabling use of date\n \tbased sha1 expressions such as \"<branchname>@{yesterday}\".\n \n+-d::\n+\tExplicitly ask to detach HEAD, even when named revision\n+\tto switch to is at the tip of a branch.\n+\n -m::\n \tIf you have local modifications to one or more files that\n \tare different between the current branch and the branch to\ndiff --git a/git-checkout.sh b/git-checkout.sh\nindex a7390e8..e551443 100755\n--- a/git-checkout.sh\n+++ b/git-checkout.sh\n@@ -1,6 +1,6 @@\n #!/bin/sh\n \n-USAGE='[-q] [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'\n+USAGE='[-q] [-f] [-b <new_branch> | -d] [-m] [<branch>] [<paths>...]'\n SUBDIRECTORY_OK=Sometimes\n . git-sh-setup\n require_work_tree\n@@ -17,6 +17,7 @@ newbranch=\n newbranch_log=\n merge=\n quiet=\n+explicit_detach=\n LF='\n '\n while [ \"$#\" != \"0\" ]; do\n@@ -39,6 +40,9 @@ while [ \"$#\" != \"0\" ]; do\n \t\"--track\"|\"--no-track\")\n \t\ttrack=\"$arg\"\n \t\t;;\n+\t-d)\n+\t\texplicit_detach=1\n+\t\t;;\n \t\"-f\")\n \t\tforce=1\n \t\t;;\n@@ -94,6 +98,11 @@ case \"$newbranch,$track\" in\n \tdie \"git checkout: --track and --no-track require -b\"\n esac\n \n+case \"$newbranch$explicit_detach\" in\n+11)\n+\tdie \"git checkout: -d and -b are incompatible\"\n+esac\n+\n case \"$force$merge\" in\n 11)\n \tdie \"git checkout: -f and -m are incompatible\"\n@@ -117,7 +126,7 @@ then\n \t\thint=\"\n Did you intend to checkout '$@' which can not be resolved as commit?\"\n \tfi\n-\tif test '' != \"$newbranch$force$merge\"\n+\tif test '' != \"$newbranch$force$merge$explicit_detach\"\n \tthen\n \t\tdie \"git checkout: updating paths is incompatible with switching branches/forcing$hint\"\n \tfi\n@@ -170,7 +179,8 @@ describe_detached_head () {\n \t}\n }\n \n-if test -z \"$branch$newbranch\" && test \"$new\" != \"$old\"\n+if test -z \"$branch$newbranch\" && test \"$new\" != \"$old\" ||\n+   test -n \"$explicit_detach\"\n then\n \tdetached=\"$new\"\n \tif test -n \"$oldbranch\" && test -z \"$quiet\"\n@@ -254,7 +264,7 @@ if [ \"$?\" -eq 0 ]; then\n \t\tgit-branch $track $newbranch_log \"$newbranch\" \"$new_name\" || exit\n \t\tbranch=\"$newbranch\"\n \tfi\n-\tif test -n \"$branch\"\n+\tif test -n \"$branch\" && test -z \"$explicit_detach\"\n \tthen\n \t\tGIT_DIR=\"$GIT_DIR\" git-symbolic-ref -m \"checkout: moving to $branch\" HEAD \"refs/heads/$branch\"\n \t\tif test -n \"$quiet\"\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 867bbd2..caa04f9 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -3,7 +3,20 @@\n # Copyright (c) 2006 Junio C Hamano\n #\n \n-test_description='git-checkout tests.'\n+test_description='git-checkout tests.\n+\n+Creates master, forks renamer and side branches from it.\n+Test switching across them.\n+\n+  ! [master] Initial A one, A two\n+   * [renamer] Renamer R one->uno, M two\n+    ! [side] Side M one, D two, A three\n+  ---\n+    + [side] Side M one, D two, A three\n+   *  [renamer] Renamer R one->uno, M two\n+  +*+ [master] Initial A one, A two\n+\n+'\n \n . ./test-lib.sh\n \n@@ -129,4 +142,52 @@ test_expect_success 'checkout -m with merge conflict' '\n \t! test -s current\n '\n \n+test_expect_success 'checkout to detach HEAD' '\n+\n+\tgit checkout -f renamer && git clean &&\n+\tgit checkout renamer^ &&\n+\tH=$(git rev-parse --verify HEAD) &&\n+\tM=$(git show-ref -s --verify refs/heads/master) &&\n+\ttest \"z$H\" = \"z$M\" &&\n+\tif git symbolic-ref HEAD >/dev/null 2>&1\n+\tthen\n+\t\techo \"OOPS, HEAD is still symbolic???\"\n+\t\tfalse\n+\telse\n+\t\t: happy\n+\tfi\n+'\n+\n+test_expect_success 'checkout to detach HEAD with explicit -d' '\n+\n+\tgit checkout -f master && git clean &&\n+\tgit checkout -d renamer^ &&\n+\tH=$(git rev-parse --verify HEAD) &&\n+\tM=$(git show-ref -s --verify refs/heads/master) &&\n+\ttest \"z$H\" = \"z$M\" &&\n+\tif git symbolic-ref HEAD >/dev/null 2>&1\n+\tthen\n+\t\techo \"OOPS, HEAD is still symbolic???\"\n+\t\tfalse\n+\telse\n+\t\t: happy\n+\tfi\n+'\n+\n+test_expect_success 'checkout to detach HEAD with explicit -d' '\n+\n+\tgit checkout -f master && git clean &&\n+\tgit checkout -d &&\n+\tH=$(git rev-parse --verify HEAD) &&\n+\tM=$(git show-ref -s --verify refs/heads/master) &&\n+\ttest \"z$H\" = \"z$M\" &&\n+\tif git symbolic-ref HEAD >/dev/null 2>&1\n+\tthen\n+\t\techo \"OOPS, HEAD is still symbolic???\"\n+\t\tfalse\n+\telse\n+\t\t: happy\n+\tfi\n+'\n+\n test_done\n-- \n1.5.1.rc3.1.ga429d\n"},{"id":"38333","messageId":"200703291044.l2TAig5M021533@localhost.localdomain","threadId":"7467","inReplyTo":"7vr6r879zp.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Xavier Maillard","fromEmail":"zedek@gnu.org","sentAt":"2007-03-29T10:44:42Z","receivedAt":"2007-03-29T10:44:42Z","isPatch":true,"sender":{"key":"zedek@gnu.org","avatar":null},"body":"   This allows you to detach your HEAD and place it at such a\n\n       $ git checkout -d master\n\nHum, excuse my ignorance, but can you explain what exactly could\nbe a use case of this ?\n\nXavier\n"},{"id":"38338","messageId":"20070329171024.GA2809@steel.home","threadId":"7467","inReplyTo":"200703291044.l2TAig5M021533@localhost.localdomain","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2007-03-29T17:10:24Z","receivedAt":"2007-03-29T17:10:24Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Xavier Maillard, Thu, Mar 29, 2007 12:44:42 +0200:\n>    This allows you to detach your HEAD and place it at such a\n> \n>        $ git checkout -d master\n> \n> Hum, excuse my ignorance, but can you explain what exactly could\n> be a use case of this ?\n\nA throw-away or an experimental work. A commit importer which cares\nabout the branch it commits on: to update the branch reference only\nwhen it has reached a stable state.\n"},{"id":"38359","messageId":"200703292054.l2TKsNRi010668@localhost.localdomain","threadId":"7467","inReplyTo":"20070329171024.GA2809@steel.home","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Xavier Maillard","fromEmail":"zedek@gnu.org","sentAt":"2007-03-29T20:54:23Z","receivedAt":"2007-03-29T20:54:23Z","isPatch":true,"sender":{"key":"zedek@gnu.org","avatar":null},"body":"\n   A throw-away or an experimental work. A commit importer which cares\n   about the branch it commits on: to update the branch reference only\n   when it has reached a stable state.\n\nHum, yes, I see. Thank you.\n\nXavier\n"},{"id":"38390","messageId":"euk8ps$vu1$1@sea.gmane.org","threadId":"7467","inReplyTo":"7vr6r879zp.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-03-31T00:11:45Z","receivedAt":"2007-03-31T00:11:45Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> You cannot currently checkout the tip of an existing branch\n> without moving to the branch.\n> \n> This allows you to detach your HEAD and place it at such a\n> commit, with:\n> \n>     $ git checkout -d master\n\nWhat about\n\n      $ git checkout master^0\n\ntrick to force detaching?\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"38482","messageId":"200703311455.l2VEtHO5021300@localhost.localdomain","threadId":"7467","inReplyTo":"euk8ps$vu1$1@sea.gmane.org","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Xavier Maillard","fromEmail":"zedek@gnu.org","sentAt":"2007-03-31T14:55:17Z","receivedAt":"2007-03-31T14:55:17Z","isPatch":true,"sender":{"key":"zedek@gnu.org","avatar":null},"body":"\n   > You cannot currently checkout the tip of an existing branch\n   > without moving to the branch.\n   > \n   > This allows you to detach your HEAD and place it at such a\n   > commit, with:\n   > \n   >     $ git checkout -d master\n\n   What about\n\n\t $ git checkout master^0\n\n   trick to force detaching?\n\nI love this idea.\n\nXavier\n"},{"id":"38576","messageId":"7vtzvxt2v3.fsf@assigned-by-dhcp.cox.net","threadId":"7467","inReplyTo":"200703311455.l2VEtHO5021300@localhost.localdomain","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-04T00:11:44Z","receivedAt":"2007-04-04T00:11:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Xavier Maillard <zedek@gnu.org> writes:\n\n>    > You cannot currently checkout the tip of an existing branch\n>    > without moving to the branch.\n>    > \n>    > This allows you to detach your HEAD and place it at such a\n>    > commit, with:\n>    > \n>    >     $ git checkout -d master\n>\n>    What about\n>\n> \t $ git checkout master^0\n>\n>    trick to force detaching?\n>\n> I love this idea.\n\nCould anybody remind me why we have the \"new != old\" check here?\n\ndiff --git a/git-checkout.sh b/git-checkout.sh\nindex a7390e8..573a3c0 100755\n--- a/git-checkout.sh\n+++ b/git-checkout.sh\n@@ -170,7 +170,7 @@ describe_detached_head () {\n \t}\n }\n \n-if test -z \"$branch$newbranch\" && test \"$new\" != \"$old\"\n+if test -z \"$branch$newbranch\"\n then\n \tdetached=\"$new\"\n \tif test -n \"$oldbranch\" && test -z \"$quiet\"\n\nI think I do not need an explicit -d option if we just remove\nthat second test.  It is coming from an ancient commit 91dcdfd3,\nand I _think_ it was to prevent something like:\n\n\tgit-checkout-script v2.6.12^0\n\nfrom succeeding, while allowing\n\n\tgit-checkout-script HEAD^0\n\nto be a no-op (as it happens to be naming the same commit).\n\ncommit 91dcdfd3b5331d955cfb60edf8930f1b5c142905\nAuthor: Linus Torvalds <torvalds@g5.osdl.org>\nDate:   Mon Jul 11 20:44:20 2005 -0700\n\n    Make \"git checkout\" create new branches on demand\n\ndiff --git a/git-checkout-script b/git-checkout-script\nindex 48e1da9..7e70338 100755\n--- a/git-checkout-script\n+++ b/git-checkout-script\n@@ -5,10 +5,19 @@ old=$(git-rev-parse HEAD)\n ...\n@@ -32,6 +41,16 @@ while [ \"$#\" != \"0\" ]; do\n done\n [ -z \"$new\" ] && new=$old\n \n+#\n+# If we don't have an old branch that we're switching to,\n+# and we don't have a new branch name for the target we\n+# are switching to, then we'd better just be checking out\n+# what we already had\n+#\n+[ -z \"$branch$newbranch\" ] &&\n+\t[ \"$new\" != \"$old\" ] &&\n+\tdie \"git checkout: you need to specify a new branch name\"\n+\n if [ \"$force\" ]\n then\n     git-read-tree --reset $new &&\n@@ -47,6 +66,10 @@ fi\n ...\n"},{"id":"38581","messageId":"Pine.LNX.4.64.0704031850240.6730@woody.linux-foundation.org","threadId":"7467","inReplyTo":"7vtzvxt2v3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] checkout -d: explicitly detach HEAD even when switching to the tip of a branch","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2007-04-04T01:55:03Z","receivedAt":"2007-04-04T01:55:03Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 3 Apr 2007, Junio C Hamano wrote:\n> \n> Could anybody remind me why we have the \"new != old\" check here?\n\nAfaik, it's just so that you can do\n\n\tgit checkout -f\n\nwithout having it complain. And I think it just comes from the earlier:\n\n\t[ -z \"$new\" ] && new=$old && new_name=\"$old_name\"\n\nthat we did - ie without that it would incorrectly just overwrite the \ncurrent working tree with the new commit, but not actually do the \"git \nreset\" part.\n\nSo without that check, the way things used to work (*before* detached \nheads), if you were to have done\n\n\tgit checkout <some-random-head>\n\nit would have screwed up the current branch horribly.\n\nWith detached heads, I don't think it's needed.\n\n\t\tLinus\n"}]}