{"thread":{"id":"17182","subject":"[PATCH] checkout: implement \"-\" shortcut name for last branch","startedAt":"2009-01-15T00:06:16Z","lastAt":"2009-02-05T08:38:13Z","messageCount":46,"participants":["Thomas Rast","Johannes Schindelin","Johannes Sixt","Johan Herland","Junio C Hamano","Santi Béjar","David Kastrup","Boyd Stephen Smith Jr."],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"100512","messageId":"1231977976-8739-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":null,"subject":"[PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-15T00:06:16Z","receivedAt":"2009-01-15T00:06:16Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Let git-checkout save the old branch as a symref in LAST_HEAD, and\nmake 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\nthe shell.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nI really wished I had this earlier today.  I'm just not sure if it's a\ngood idea, or even possible, to reserve the '-'.  I can't seem to\ncheck out a branch '-foo' with git-checkout, but it's easy to create\none with 'git branch -- -foo'.  git-check-ref-format(1) doesn't forbid\nit either, although the actual 'git check-ref-format -foo' exits with\nstatus 1.\n\n Documentation/git-checkout.txt         |    3 +++\n Documentation/gitrepository-layout.txt |    4 ++++\n builtin-checkout.c                     |   26 +++++++++++++++++++++++++-\n 3 files changed, 32 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 9cd5151..1397745 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -133,6 +133,9 @@ the conflicted merge in the specified paths.\n +\n When this parameter names a non-branch (but still a valid commit object),\n your HEAD becomes 'detached'.\n++\n+You may also specify \"`-`\", which denotes the last branch you were on\n+before the current HEAD.\n \n \n Detached HEAD\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex 1befca9..f506c98 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -123,6 +123,10 @@ is often called 'detached HEAD', and almost all commands work\n identically as normal.  See linkgit:git-checkout[1] for\n details.\n \n+LAST_HEAD::\n+\tA symref that holds the value of HEAD before the last\n+\tbranch switch.\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify URL to 'git-fetch', 'git-pull' and 'git-push'\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex b5dd9c0..356ad6c 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -480,6 +480,15 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n+static void save_old_branch(struct branch_info *old, char *msg)\n+{\n+\tif (old->path) {\n+\t\tcreate_symref(\"LAST_HEAD\", old->path, msg);\n+\t} else\n+\t\tupdate_ref(msg, \"LAST_HEAD\", old->commit->object.sha1, NULL,\n+\t\t\t   REF_NODEREF, DIE_ON_ERR);\n+}\n+\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -505,12 +514,15 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\tif (old->path && !strcmp(new->path, old->path))\n \t\t\t\tfprintf(stderr, \"Already on \\\"%s\\\"\\n\",\n \t\t\t\t\tnew->name);\n-\t\t\telse\n+\t\t\telse {\n \t\t\t\tfprintf(stderr, \"Switched to%s branch \\\"%s\\\"\\n\",\n \t\t\t\t\topts->new_branch ? \" a new\" : \"\",\n \t\t\t\t\tnew->name);\n+\t\t\t\tsave_old_branch(old, msg.buf);\n+\t\t\t}\n \t\t}\n \t} else if (strcmp(new->name, \"HEAD\")) {\n+\t\tsave_old_branch(old, msg.buf);\n \t\tupdate_ref(msg.buf, \"HEAD\", new->commit->object.sha1, NULL,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n@@ -533,6 +545,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \tint flag;\n \tmemset(&old, 0, sizeof(old));\n \told.path = resolve_ref(\"HEAD\", rev, 0, &flag);\n+\tif (old.path)\n+\t\told.path = strdup(old.path);\n \told.commit = lookup_commit_reference_gently(rev, 1);\n \tif (!(flag & REF_ISSYMREF))\n \t\told.path = NULL;\n@@ -604,6 +618,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \tint has_dash_dash;\n+\tint flag;\n \n \tmemset(&opts, 0, sizeof(opts));\n \tmemset(&new, 0, sizeof(new));\n@@ -671,6 +686,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targ = argv[0];\n \t\thas_dash_dash = (argc > 1) && !strcmp(argv[1], \"--\");\n \n+\t\tif (!strcmp(arg, \"-\")) {\n+\t\t\targ = resolve_ref(\"LAST_HEAD\", rev, 0, &flag);\n+\t\t\tif (!arg)\n+\t\t\t\tdie(\"No last branch saved.\");\n+\t\t\tif(!prefixcmp(arg, \"refs/heads/\"))\n+\t\t\t\targ += 11;\n+\t\t\targ = strdup(arg);\n+\t\t}\n+\n \t\tif (get_sha1(arg, rev)) {\n \t\t\tif (has_dash_dash)          /* case (1) */\n \t\t\t\tdie(\"invalid reference: %s\", arg);\n-- \n1.6.1.282.gae4091.dirty\n"},{"id":"100513","messageId":"1231978322-21228-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"1231977976-8739-1-git-send-email-trast@student.ethz.ch","subject":"[PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-15T00:12:02Z","receivedAt":"2009-01-15T00:12:02Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Let git-checkout save the old branch as a symref in LAST_HEAD, and\nmake 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\nthe shell.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nBah, sorry.  I managed to keep it uncommitted AGAIN.\n\nBut this fixed version passes tests.  All of them.  Really!  ;-)\n\n\n Documentation/git-checkout.txt         |    3 ++\n Documentation/gitrepository-layout.txt |    4 ++\n builtin-checkout.c                     |   27 ++++++++++++++++-\n t/t2012-checkout-last.sh               |   50 ++++++++++++++++++++++++++++++++\n 4 files changed, 83 insertions(+), 1 deletions(-)\n create mode 100755 t/t2012-checkout-last.sh\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 9cd5151..1397745 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -133,6 +133,9 @@ the conflicted merge in the specified paths.\n +\n When this parameter names a non-branch (but still a valid commit object),\n your HEAD becomes 'detached'.\n++\n+You may also specify \"`-`\", which denotes the last branch you were on\n+before the current HEAD.\n \n \n Detached HEAD\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex 1befca9..f506c98 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -123,6 +123,10 @@ is often called 'detached HEAD', and almost all commands work\n identically as normal.  See linkgit:git-checkout[1] for\n details.\n \n+LAST_HEAD::\n+\tA symref that holds the value of HEAD before the last\n+\tbranch switch.\n+\n branches::\n \tA slightly deprecated way to store shorthands to be used\n \tto specify URL to 'git-fetch', 'git-pull' and 'git-push'\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex b5dd9c0..da74831 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -480,6 +480,16 @@ static void report_tracking(struct branch_info *new)\n \tstrbuf_release(&sb);\n }\n \n+static void save_old_branch(struct branch_info *old, char *msg)\n+{\n+\tif (old->path) {\n+\t\tcreate_symref(\"LAST_HEAD\", old->path, msg);\n+\t} else if (old->commit) {\n+\t\tupdate_ref(msg, \"LAST_HEAD\", old->commit->object.sha1, NULL,\n+\t\t\t   REF_NODEREF, DIE_ON_ERR);\n+\t}\n+}\n+\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -505,12 +515,15 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\tif (old->path && !strcmp(new->path, old->path))\n \t\t\t\tfprintf(stderr, \"Already on \\\"%s\\\"\\n\",\n \t\t\t\t\tnew->name);\n-\t\t\telse\n+\t\t\telse {\n \t\t\t\tfprintf(stderr, \"Switched to%s branch \\\"%s\\\"\\n\",\n \t\t\t\t\topts->new_branch ? \" a new\" : \"\",\n \t\t\t\t\tnew->name);\n+\t\t\t\tsave_old_branch(old, msg.buf);\n+\t\t\t}\n \t\t}\n \t} else if (strcmp(new->name, \"HEAD\")) {\n+\t\tsave_old_branch(old, msg.buf);\n \t\tupdate_ref(msg.buf, \"HEAD\", new->commit->object.sha1, NULL,\n \t\t\t   REF_NODEREF, DIE_ON_ERR);\n \t\tif (!opts->quiet) {\n@@ -533,6 +546,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \tint flag;\n \tmemset(&old, 0, sizeof(old));\n \told.path = resolve_ref(\"HEAD\", rev, 0, &flag);\n+\tif (old.path)\n+\t\told.path = strdup(old.path);\n \told.commit = lookup_commit_reference_gently(rev, 1);\n \tif (!(flag & REF_ISSYMREF))\n \t\told.path = NULL;\n@@ -604,6 +619,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \tint has_dash_dash;\n+\tint flag;\n \n \tmemset(&opts, 0, sizeof(opts));\n \tmemset(&new, 0, sizeof(new));\n@@ -671,6 +687,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\targ = argv[0];\n \t\thas_dash_dash = (argc > 1) && !strcmp(argv[1], \"--\");\n \n+\t\tif (!strcmp(arg, \"-\")) {\n+\t\t\targ = resolve_ref(\"LAST_HEAD\", rev, 0, &flag);\n+\t\t\tif (!arg)\n+\t\t\t\tdie(\"No last branch saved.\");\n+\t\t\tif(!prefixcmp(arg, \"refs/heads/\"))\n+\t\t\t\targ += 11;\n+\t\t\targ = strdup(arg);\n+\t\t}\n+\n \t\tif (get_sha1(arg, rev)) {\n \t\t\tif (has_dash_dash)          /* case (1) */\n \t\t\t\tdie(\"invalid reference: %s\", arg);\ndiff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh\nnew file mode 100755\nindex 0000000..320f6eb\n--- /dev/null\n+++ b/t/t2012-checkout-last.sh\n@@ -0,0 +1,50 @@\n+#!/bin/sh\n+\n+test_description='checkout can switch to last branch'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo hello >world &&\n+\tgit add world &&\n+\tgit commit -m initial &&\n+\tgit branch other &&\n+\techo \"hello again\" >>world &&\n+\tgit add world &&\n+\tgit commit -m second\n+'\n+\n+test_expect_success '\"checkout -\" does not work initially' '\n+\ttest_must_fail git checkout -\n+'\n+\n+test_expect_success 'first branch switch' '\n+\tgit checkout other\n+'\n+\n+test_expect_success '\"checkout -\" switches back' '\n+\tgit checkout - &&\n+\ttest \"z$(git symbolic-ref HEAD)\" = \"zrefs/heads/master\"\n+'\n+\n+test_expect_success '\"checkout -\" switches forth' '\n+\tgit checkout - &&\n+\ttest \"z$(git symbolic-ref HEAD)\" = \"zrefs/heads/other\"\n+'\n+\n+test_expect_success 'detach HEAD' '\n+\tgit checkout $(git rev-parse HEAD)\n+'\n+\n+test_expect_success '\"checkout -\" attaches again' '\n+\tgit checkout - &&\n+\ttest \"z$(git symbolic-ref HEAD)\" = \"zrefs/heads/other\"\n+'\n+\n+test_expect_success '\"checkout -\" detaches again' '\n+\tgit checkout - &&\n+\ttest \"z$(git rev-parse HEAD)\" = \"z$(git rev-parse other)\" &&\n+\ttest_must_fail git symbolic-ref HEAD\n+'\n+\n+test_done\n-- \n1.6.1.282.gae4091.dirty\n"},{"id":"100523","messageId":"alpine.DEB.1.00.0901150141570.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"1231977976-8739-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T00:45:22Z","receivedAt":"2009-01-15T00:45:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Thomas Rast wrote:\n\n> Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> the shell.\n\nActually, what you want is in the reflog, no?  So... parsing \n.git/logs/HEAD for the latest occurrence of \"checkout: moving from \" and \nthen using everything up until the next space should give you the branch \nname, right?\n\nIt could be a SHA-1, though, if the last branch switch was from a detached \nHEAD, though.\n\nCiao,\nDscho\n"},{"id":"100547","messageId":"496EE559.3060901@viscovery.net","threadId":"17182","inReplyTo":"1231978322-21228-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2009-01-15T07:27:21Z","receivedAt":"2009-01-15T07:27:21Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Thomas Rast schrieb:\n> Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> the shell.\n\n/me likes this feature.\n\ngit rebase (-i or not) calls checkout behind the scenes if the\ntwo-argument form is used:\n\n   git rebase [-i] master topic\n\nand 'topic' is not the current branch. You may want to add a test that\nensures that rebase sets LAST_HEAD in this case.\n\nYou must make sure that commits referenced by LAST_HEAD are not\ngarbage-collected. (I don't know if this happens anyway for symrefs in .git.)\n\n-- Hannes\n"},{"id":"100572","messageId":"alpine.DEB.1.00.0901151413250.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"496EE559.3060901@viscovery.net","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T13:15:19Z","receivedAt":"2009-01-15T13:15:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Johannes Sixt wrote:\n\n> Thomas Rast schrieb:\n> > Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> > make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> > the shell.\n> \n> /me likes this feature.\n> \n> git rebase (-i or not) calls checkout behind the scenes if the\n> two-argument form is used:\n> \n>    git rebase [-i] master topic\n> \n> and 'topic' is not the current branch. You may want to add a test that\n> ensures that rebase sets LAST_HEAD in this case.\n> \n> You must make sure that commits referenced by LAST_HEAD are not\n> garbage-collected. (I don't know if this happens anyway for symrefs in .git.)\n\nNote: if you used reflogs for that feature, the garbage collection could \nnot have killed the commit.  However, it is quite possible that the \nbranch was deleted.\n\nCiao,\nDscho\n"},{"id":"100590","messageId":"200901151500.01876.trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151413250.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-15T13:59:58Z","receivedAt":"2009-01-15T13:59:58Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> On Thu, 15 Jan 2009, Johannes Sixt wrote:\n> > You must make sure that commits referenced by LAST_HEAD are not\n> > garbage-collected. (I don't know if this happens anyway for symrefs in .git.)\n> \n> Note: if you used reflogs for that feature, the garbage collection could \n> not have killed the commit.  However, it is quite possible that the \n> branch was deleted.\n\nSuddenly I'm not so sure about either behaviour any more.\n\nConsider:\n\n  $ git commit -m initial\n  [master (root-commit)]: created 812c476: \"initial\"\n   1 files changed, 1 insertions(+), 0 deletions(-)\n   create mode 100644 foo\n  $ git checkout $(git rev-parse HEAD)\n  Note: moving to \"812c476ca23e25efa7e4d7081153ba657a127d95\" which isn't a local branch\n  If you want to create a new branch from this checkout, you may do so\n  (now or later) by using -b with the checkout command again. Example:\n    git checkout -b <new_branch_name>\n  HEAD is now at 812c476... initial\n  $ git branch -D master\n  Deleted branch master (812c476).\n  $ git for-each-ref\n  $ git reflog expire --expire=now --all\n  $ git prune --expire now\n  $ git show\n  fatal: bad object HEAD\n  $ git show 812c476\n  fatal: ambiguous argument '812c476': unknown revision or path not in the working tree.\n  Use '--' to separate paths from revisions\n\nOops.\n\nSome quick RTFS shows that it indeed \"only\" cares about refs and\nreflogs.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n\n"},{"id":"100591","messageId":"200901151501.26394.trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901150141570.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-15T14:01:24Z","receivedAt":"2009-01-15T14:01:24Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> On Thu, 15 Jan 2009, Thomas Rast wrote:\n> \n> > Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> > make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> > the shell.\n> \n> Actually, what you want is in the reflog, no?  So... parsing \n> .git/logs/HEAD for the latest occurrence of \"checkout: moving from \" and \n> then using everything up until the next space should give you the branch \n> name, right?\n\nIt just feels wrong to grab that information from there; it's a\nfree-form comment field for user consumption.  And it wasn't even that\nhard to implement a LAST_HEAD.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n\n"},{"id":"100593","messageId":"alpine.DEB.1.00.0901151508540.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"200901151500.01876.trast@student.ethz.ch","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T14:09:32Z","receivedAt":"2009-01-15T14:09:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Thomas Rast wrote:\n\n> Johannes Schindelin wrote:\n> > On Thu, 15 Jan 2009, Johannes Sixt wrote:\n> > > You must make sure that commits referenced by LAST_HEAD are not\n> > > garbage-collected. (I don't know if this happens anyway for symrefs in .git.)\n> > \n> > Note: if you used reflogs for that feature, the garbage collection could \n> > not have killed the commit.  However, it is quite possible that the \n> > branch was deleted.\n> \n> Suddenly I'm not so sure about either behaviour any more.\n> \n> Consider:\n> \n>   $ git commit -m initial\n>   [master (root-commit)]: created 812c476: \"initial\"\n>    1 files changed, 1 insertions(+), 0 deletions(-)\n>    create mode 100644 foo\n>   $ git checkout $(git rev-parse HEAD)\n>   Note: moving to \"812c476ca23e25efa7e4d7081153ba657a127d95\" which isn't a local branch\n>   If you want to create a new branch from this checkout, you may do so\n>   (now or later) by using -b with the checkout command again. Example:\n>     git checkout -b <new_branch_name>\n>   HEAD is now at 812c476... initial\n>   $ git branch -D master\n>   Deleted branch master (812c476).\n>   $ git for-each-ref\n>   $ git reflog expire --expire=now --all\n>   $ git prune --expire now\n>   $ git show\n>   fatal: bad object HEAD\n>   $ git show 812c476\n>   fatal: ambiguous argument '812c476': unknown revision or path not in the working tree.\n>   Use '--' to separate paths from revisions\n> \n> Oops.\n> \n> Some quick RTFS shows that it indeed \"only\" cares about refs and\n> reflogs.\n\nMaybe something like this would help (completely untested, though the \nidea should be clear)?\n\n-- snipsnap --\n[PATCH] pack-objects --all: include HEAD, which could be detached\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n builtin-pack-objects.c |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-pack-objects.c b/builtin-pack-objects.c\nindex cb51916..da55671 100644\n--- a/builtin-pack-objects.c\n+++ b/builtin-pack-objects.c\n@@ -2219,6 +2219,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t\t rp_ac_alloc * sizeof(*rp_av));\n \t\t\t}\n \t\t\trp_av[rp_ac++] = arg;\n+\t\t\tif (!strcmp(\"--all\", arg)) {\n+\t\t\t\tALLOC_GROW(rp_av, rp_ac + 1, rp_ac_alloc);\n+\t\t\t\trp_av[rp_ac++] = \"HEAD\";\n+\t\t\t}\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--thin\", arg)) {\n"},{"id":"100594","messageId":"alpine.DEB.1.00.0901151510340.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"200901151501.26394.trast@student.ethz.ch","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T14:14:16Z","receivedAt":"2009-01-15T14:14:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Thomas Rast wrote:\n\n> Johannes Schindelin wrote:\n> > On Thu, 15 Jan 2009, Thomas Rast wrote:\n> > \n> > > Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> > > make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> > > the shell.\n> > \n> > Actually, what you want is in the reflog, no?  So... parsing \n> > .git/logs/HEAD for the latest occurrence of \"checkout: moving from \" and \n> > then using everything up until the next space should give you the branch \n> > name, right?\n> \n> It just feels wrong to grab that information from there; it's a\n> free-form comment field for user consumption.  And it wasn't even that\n> hard to implement a LAST_HEAD.\n\nThere are a number of issues why I would like to avoid introducing \nLAST_HEAD:\n\n- it does not work when you are using different Git versions on the same \n  repository,\n\n- it does not work when you switched recently,\n\n- you are storing redundant information,\n\n- yes, the field is meant for user consumption, but no, it is not \n  free-form,\n\n- AFAICT your version could never be convinced to resurrect deleted \n  branches, without resorting to reflogs anyway.\n\n- the reflog method reflects pretty much exactly how people work around \n  the lack of \"checkout -\" currently, so why not just use the same proven \n  approach?\n\nCiao,\nDscho\n"},{"id":"100596","messageId":"alpine.DEB.1.00.0901151517190.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151508540.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T14:17:56Z","receivedAt":"2009-01-15T14:17:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Johannes Schindelin wrote:\n\n> [PATCH] pack-objects --all: include HEAD, which could be detached\n\nIn hind sight, it would probably be better to add this to revision.c.\n\nCiao,\nDscho\n"},{"id":"100628","messageId":"200901151732.57023.johan@herland.net","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151413250.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2009-01-15T16:32:56Z","receivedAt":"2009-01-15T16:32:56Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Thursday 15 January 2009, Johannes Schindelin wrote:\n> Hi,\n>\n> On Thu, 15 Jan 2009, Johannes Sixt wrote:\n> > Thomas Rast schrieb:\n> > > Let git-checkout save the old branch as a symref in LAST_HEAD,\n> > > and make 'git checkout -' switch back to LAST_HEAD, like 'cd -'\n> > > does in the shell.\n> >\n> > /me likes this feature.\n> >\n> > git rebase (-i or not) calls checkout behind the scenes if the\n> > two-argument form is used:\n> >\n> >    git rebase [-i] master topic\n> >\n> > and 'topic' is not the current branch. You may want to add a test\n> > that ensures that rebase sets LAST_HEAD in this case.\n> >\n> > You must make sure that commits referenced by LAST_HEAD are not\n> > garbage-collected. (I don't know if this happens anyway for symrefs\n> > in .git.)\n>\n> Note: if you used reflogs for that feature, the garbage collection\n> could not have killed the commit.  However, it is quite possible that\n> the branch was deleted.\n\nI also like this feature, but as this is only a _convenience_ feature, I \nwould prefer if it didn't keep the previous branch/commit alive (if \notherwise unreachable). In any case, this new feature will _have_ to \nhandle the case where there simply is no previous branch/commit (e.g. \nafter a git clone or git init).\n\nI suggest that \"git checkout -\" looks at the reflog, and if there is no \nprevious entry in the reflog, or that entry is unreachable, then fail \nin the same manner as \"git checkout garbage\"\n\n\nHave fun! :)\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"100630","messageId":"alpine.DEB.1.00.0901151749350.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"200901151732.57023.johan@herland.net","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T16:50:46Z","receivedAt":"2009-01-15T16:50:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Johan Herland wrote:\n\n> On Thursday 15 January 2009, Johannes Schindelin wrote:\n>\n> > On Thu, 15 Jan 2009, Johannes Sixt wrote:\n> > > Thomas Rast schrieb:\n> > > > Let git-checkout save the old branch as a symref in LAST_HEAD,\n> > > > and make 'git checkout -' switch back to LAST_HEAD, like 'cd -'\n> > > > does in the shell.\n> > >\n> > > /me likes this feature.\n> > >\n> > > git rebase (-i or not) calls checkout behind the scenes if the\n> > > two-argument form is used:\n> > >\n> > >    git rebase [-i] master topic\n> > >\n> > > and 'topic' is not the current branch. You may want to add a test\n> > > that ensures that rebase sets LAST_HEAD in this case.\n> > >\n> > > You must make sure that commits referenced by LAST_HEAD are not\n> > > garbage-collected. (I don't know if this happens anyway for symrefs\n> > > in .git.)\n> >\n> > Note: if you used reflogs for that feature, the garbage collection\n> > could not have killed the commit.  However, it is quite possible that\n> > the branch was deleted.\n> \n> I also like this feature, but as this is only a _convenience_ feature, I \n> would prefer if it didn't keep the previous branch/commit alive (if \n> otherwise unreachable).\n\nYou misread me: if the information is in HEAD's reflog, the _is_ \nreachable.  From HEAD's reflog.  And therefore, the objects will not be \ngc'ed (yet).\n\n> In any case, this new feature will _have_ to handle the case where there \n> simply is no previous branch/commit (e.g. after a git clone or git \n> init).\n>\n> I suggest that \"git checkout -\" looks at the reflog, and if there is no \n> previous entry in the reflog, or that entry is unreachable, then fail \n> in the same manner as \"git checkout garbage\"\n\nExactly my thinking.\n\nCiao,\nDscho\n"},{"id":"100635","messageId":"200901151805.44747.trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151510340.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-15T17:05:42Z","receivedAt":"2009-01-15T17:05:42Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> There are a number of issues why I would like to avoid introducing \n> LAST_HEAD:\n> \n> - it does not work when you are using different Git versions on the same \n>   repository,\n> \n> - it does not work when you switched recently,\n\nIf you switch once, you'll be able to use the feature one checkout\nlater than if it was reflog-based.\n\nIf you switch a lot, the feature won't be in your git half the time\nanyway.\n\n> - you are storing redundant information,\n\nAFAIK it's the first instance of this data in a non-free-form field.\nThere's also the precedent of ORIG_HEAD.\n\n> - yes, the field is meant for user consumption, but no, it is not \n>   free-form,\n\nIt's a field of almost arbitrary character data, filled by 70% of the\nupdate-ref calls I can find in git.git in a \"<tool>: <comment>\" format\nand by the rest with things such as \"initial pull\" or\n\"refs/remotes/git-svn: updating HEAD\".  (The latter is so informative\nthat it probably deserves a fix.)  How is that not free-form?\n\n> - AFAICT your version could never be convinced to resurrect deleted \n>   branches, without resorting to reflogs anyway.\n\nNeither can any other use of git-checkout without the user manually\nrecovering some valid revspec referring to the old branch tip from the\nreflog.  I wanted to be able to abbreviate the previous branch's name,\nand it does just that.\n\n> - the reflog method reflects pretty much exactly how people work around \n>   the lack of \"checkout -\" currently, so why not just use the same proven \n>   approach?\n\nSo you can make me fight an uphill battle against your idea how it\nshould be done.\n\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n\n\n\n\n\n\n\n\n"},{"id":"100643","messageId":"alpine.DEB.1.00.0901151922360.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"200901151805.44747.trast@student.ethz.ch","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T18:34:02Z","receivedAt":"2009-01-15T18:34:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Thomas Rast wrote:\n\n> Johannes Schindelin wrote:\n> > There are a number of issues why I would like to avoid introducing \n> > LAST_HEAD:\n> > \n> > - it does not work when you are using different Git versions on the same \n> >   repository,\n> > \n> > - it does not work when you switched recently,\n> \n> If you switch once, you'll be able to use the feature one checkout\n> later than if it was reflog-based.\n> \n> If you switch a lot, the feature won't be in your git half the time\n> anyway.\n\nBut once it is, you could also have something like \"git checkout -{5}\" \nmeaning the 5th last branch you were on.\n\nNo, I am not married to that syntax\n\n> > - you are storing redundant information,\n> \n> AFAIK it's the first instance of this data in a non-free-form field.\n> There's also the precedent of ORIG_HEAD.\n\nSee below.\n\n> > - yes, the field is meant for user consumption, but no, it is not \n> >   free-form,\n> \n> It's a field of almost arbitrary character data, filled by 70% of the\n> update-ref calls I can find in git.git in a \"<tool>: <comment>\" format\n> and by the rest with things such as \"initial pull\" or\n> \"refs/remotes/git-svn: updating HEAD\".  (The latter is so informative\n> that it probably deserves a fix.)  How is that not free-form?\n\nThat is not free-form, as the \"<tool>:\" is a hard convention all obey \n(and therefore, git checkout - only relies on _checkout_ not changing the \nformat), and checkout is sufficiently plumbing that we will not change it \nall that lightly, certainly not when \"git checkout -\" depends on it.\n\nSo I think that those free-form concerns are totally unfounded.\n\nOh, and before you say that people could mess with GIT_REFLOG_ACTION, git \ncheckout is no longer a script, and creates the message itself.  So we \nhave full control over it.\n\nThey could edit the logs directly, but that applies to virtually the whole \nrepository, and can safely be ignored as a lemming behavior.\n\n> > - AFAICT your version could never be convinced to resurrect deleted \n> >   branches, without resorting to reflogs anyway.\n> \n> Neither can any other use of git-checkout without the user manually\n> recovering some valid revspec referring to the old branch tip from the\n> reflog.\n\nTo the contrary.  The reflog has this information together with the \nmessage \"moved from ...\".\n\n> > - the reflog method reflects pretty much exactly how people work around \n> >   the lack of \"checkout -\" currently, so why not just use the same proven \n> >   approach?\n> \n> So you can make me fight an uphill battle against your idea how it\n> should be done.\n\nIf you can convince me that there are benefits from introducing yet \nanother file in $GIT_DIR and duplicating information that is in the \nreflogs already, then no, it's not an uphill battle.\n\nI mean, I _like_ the feature.  Otherwise I would not spend so much time \nsuggesting what I think would be a method more in line with what we have \nalready.\n\nCiao,\nDscho\n"},{"id":"100651","messageId":"7vocy8s51o.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"1231978322-21228-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-15T20:11:31Z","receivedAt":"2009-01-15T20:11:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Let git-checkout save the old branch as a symref in LAST_HEAD, and\n> make 'git checkout -' switch back to LAST_HEAD, like 'cd -' does in\n> the shell.\n\nI do not like this for two reasons.\n\nI will not dispute that you would need to have \"checkout\" and other branch\nswitching operations to record where you were in order to be able to refer\nto \"where I was\".  And as Dscho and others point out, there already is an\nexisting mechanism that does exactly that, so it _might_ be easier to work\nwith an extra LAST_HEAD, it is not absolutely necessary.\n\nI do not see a reason to limit the new notation \"where I was\" only to \"git\ncheckout\".  Wouldn't it be handy if you can use the notation as the other\nbranch to merge from, or the commit to rebase on?\n\n\"cd -\" is a very good analogy why your \"-\" shortcut is a short-sighted\nconvenience feature that is too narrow and not well designed.  \"cd -\" can\ngo back, but you cannot say \"ls -\" to list the contents of the previous\ndirectory.\n\nSo if this topic were \"Introduce LAST_HEAD to always keep track of the\nbranch I was on before the current branch\", and were advertised as \"You\ncan use this throughout git to say things like 'git checkout LAST_HEAD',\n'git merge LAST_HEAD', and 'git rebase LAST_HEAD'\", I think it might have\nmade a bit more sense.  You could _additionally_ say \"because switching to\nLAST_HEAD happens very often, there is another short cut 'checkout -' but\nthat is exactly the same as 'checkout LAST_HEAD'\".\n\nAnother reason is the one level limitation.  If we do not use LAST_HEAD,\nand instead used HEAD reflog, to get to this information, there is no\nreason we cannot to give an equally easy access to the second from the\nlast branch the user was on.\n\nSo I think it is just the matter of coming up with a clever syntax that\nworks on reflogs to name the nth last branch we were on and teach that\nsyntax to both get_sha1() and resolve_ref().\n\nWith the attached illustration patch,\n\n     $ git checkout junk\n     $ git chekcout master\n     $ git checkout @{-1}\n\nwill take you back to junk branch.  It probably would serve as a starting\npoint, if anybody is interested.\n\nNOTE!\n\n * It will report \"Switched to branch \"junk\", not \"junk (@{-1})\" or\n   anything that hints the user used this new syntax.  switch_branches()\n   may need to be given more information to distinguish the name the end\n   user spelled to specify the branch (e.g. \"@{-1}\") and the actual name\n   of the branch (e.g. \"junk\"), and use the former together with the\n   latter when reporting to the end user and use the latter only to record\n   what happened to the reflog.  But this is a very minor point.\n\n * The reflog parser only parses \"checkout\" and not rebase action.  It\n   also does not notice \"git checkout HEAD^\" is not switching to a real\n   branch.\n\n * The code read the reflog twice, first to count how many branch\n   switching there are and then to locate the N-th entry we are interested\n   in, because I was lazy.  We may want an API to enumerate reflog entries\n   in reverse.\n\n * interpret_nth_last_branch() is not hooked to get_sha1() codepath in\n   this patch, so this is still only applicable to \"git checkout\".  But it\n   should be trivial to do so.\n\n builtin-checkout.c |   10 +++++-\n cache.h            |    1 +\n sha1_name.c        |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 87 insertions(+), 2 deletions(-)\n\ndiff --git c/builtin-checkout.c w/builtin-checkout.c\nindex b5dd9c0..a3b69d6 100644\n--- c/builtin-checkout.c\n+++ w/builtin-checkout.c\n@@ -361,8 +361,14 @@ struct branch_info {\n static void setup_branch_path(struct branch_info *branch)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstrbuf_addstr(&buf, \"refs/heads/\");\n-\tstrbuf_addstr(&buf, branch->name);\n+\n+\tif (!interpret_nth_last_branch(branch->name, &buf)) {\n+\t\tbranch->name = xstrdup(buf.buf);\n+\t\tstrbuf_splice(&buf, 0, 0, \"refs/heads/\", 11);\n+\t} else {\n+\t\tstrbuf_addstr(&buf, \"refs/heads/\");\n+\t\tstrbuf_addstr(&buf, branch->name);\n+\t}\n \tbranch->path = strbuf_detach(&buf, NULL);\n }\n \ndiff --git c/cache.h w/cache.h\nindex 8e1af26..0dd9168 100644\n--- c/cache.h\n+++ w/cache.h\n@@ -663,6 +663,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);\n extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);\n extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n+extern int interpret_nth_last_branch(const char *str, struct strbuf *);\n \n extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);\n extern const char *ref_rev_parse_rules[];\ndiff --git c/sha1_name.c w/sha1_name.c\nindex 159c2ab..6377264 100644\n--- c/sha1_name.c\n+++ w/sha1_name.c\n@@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)\n \treturn retval;\n }\n \n+struct grab_nth_branch_switch_cbdata {\n+\tint counting;\n+\tint nth;\n+\tstruct strbuf *buf;\n+};\n+\n+static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t\t  const char *email, unsigned long timestamp, int tz,\n+\t\t\t\t  const char *message, void *cb_data)\n+{\n+\tstruct grab_nth_branch_switch_cbdata *cb = cb_data;\n+\tconst char *match = NULL;\n+\n+\tif (!prefixcmp(message, \"checkout: moving to \"))\n+\t\tmatch = message + strlen(\"checkout: moving to \");\n+\telse if (!prefixcmp(message, \"checkout: moving from \")) {\n+\t\tconst char *cp = message + strlen(\"checkout: moving from \");\n+\t\tif ((cp = strstr(cp, \" to \")) != NULL) {\n+\t\t\tmatch = cp + 4;\n+\t\t}\n+\t}\n+\n+\tif (!match)\n+\t\treturn 0;\n+\n+\tif (cb->counting) {\n+\t\tcb->nth++;\n+\t\treturn 0;\n+\t}\n+\n+\tif (--cb->nth <= 0) {\n+\t\tsize_t len = strlen(match);\n+\t\twhile (match[len-1] == '\\n')\n+\t\t\tlen--;\n+\t\tstrbuf_reset(cb->buf);\n+\t\tstrbuf_add(cb->buf, match, len);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * This reads \"@{-N}\" syntax, finds the name of the Nth previous\n+ * branch we were on, and places the name of the branch in the given\n+ * buf and returns 0 if successful.\n+ *\n+ * If the input is not of the accepted format, it returns a negative\n+ * number to signal an error.\n+ */\n+int interpret_nth_last_branch(const char *name, struct strbuf *buf)\n+{\n+\tint nth, i;\n+\tstruct grab_nth_branch_switch_cbdata cb;\n+\n+\tif (name[0] != '@' || name[1] != '{' || name[2] != '-')\n+\t\treturn -1;\n+\tfor (i = 3, nth = 0; name[i] && name[i] != '}'; i++) {\n+\t\tchar ch = name[i];\n+\t\tif ('0' <= ch && ch <= '9')\n+\t\t\tnth = nth * 10 + ch - '0';\n+\t\telse\n+\t\t\treturn -1;\n+\t}\n+\tif (nth < 0 || 10 <= nth)\n+\t\treturn -1;\n+\n+\tcb.counting = 1;\n+\tcb.nth = 0;\n+\tcb.buf = buf;\n+\tfor_each_reflog_ent(\"HEAD\", grab_nth_branch_switch, &cb);\n+\n+\tcb.counting = 0;\n+\tcb.nth -= nth;\n+\tcb.buf = buf;\n+\tfor_each_reflog_ent(\"HEAD\", grab_nth_branch_switch, &cb);\n+\treturn 0;\n+}\n+\n /*\n  * This is like \"get_sha1_basic()\", except it allows \"sha1 expressions\",\n  * notably \"xyz^\" for \"parent of xyz\"\n"},{"id":"100650","messageId":"7vhc40s50t.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151517190.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-15T20:12:02Z","receivedAt":"2009-01-15T20:12:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Thu, 15 Jan 2009, Johannes Schindelin wrote:\n>\n>> [PATCH] pack-objects --all: include HEAD, which could be detached\n>\n> In hind sight, it would probably be better to add this to revision.c.\n\nIf you mean that \"git log --all\" should also include a possibly detached\nHEAD in its traversal, and a patch that implements such a fix would\nautomatically fix \"repack -a\" without the patch you are responding to, I\nthink I agree 100%.\n"},{"id":"100653","messageId":"alpine.DEB.1.00.0901152132390.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"7vhc40s50t.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-15T20:35:17Z","receivedAt":"2009-01-15T20:35:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Thu, 15 Jan 2009, Johannes Schindelin wrote:\n> >\n> >> [PATCH] pack-objects --all: include HEAD, which could be detached\n> >\n> > In hind sight, it would probably be better to add this to revision.c.\n> \n> If you mean that \"git log --all\" should also include a possibly detached\n> HEAD in its traversal, and a patch that implements such a fix would\n> automatically fix \"repack -a\" without the patch you are responding to, I\n> think I agree 100%.\n\nYes, indeed.\n\nSomething like\n\n-- snip --\ndiff --git a/revision.c b/revision.c\nindex db60f06..b065184 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1263,6 +1263,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \n \t\t\tif (!strcmp(arg, \"--all\")) {\n \t\t\t\thandle_refs(revs, flags, for_each_ref);\n+\t\t\t\thandle_refs(revs, flags, head_ref);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--branches\")) {\n-- snap --\n\nbut that was just a quick guess, and if nobody beats me to it, I'll turn \nit into a proper patch later.\n\nCiao,\nDscho\n"},{"id":"100655","messageId":"7v8wpcs38c.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"7vocy8s51o.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-15T20:50:43Z","receivedAt":"2009-01-15T20:50:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> So I think it is just the matter of coming up with a clever syntax that\n> works on reflogs to name the nth last branch we were on and teach that\n> syntax to both get_sha1() and resolve_ref().\n>\n> With the attached illustration patch,\n>\n>      $ git checkout junk\n>      $ git chekcout master\n>      $ git checkout @{-1}\n>\n> will take you back to junk branch.  It probably would serve as a starting\n> point, if anybody is interested.\n>\n> NOTE!\n> ...\n>  * interpret_nth_last_branch() is not hooked to get_sha1() codepath in\n>    this patch, so this is still only applicable to \"git checkout\".  But it\n>    should be trivial to do so.\n> ...\n> +/*\n> + * This reads \"@{-N}\" syntax, finds the name of the Nth previous\n> + * branch we were on, and places the name of the branch in the given\n> + * buf and returns 0 if successful.\n> + *\n> + * If the input is not of the accepted format, it returns a negative\n> + * number to signal an error.\n> + */\n> +int interpret_nth_last_branch(const char *name, struct strbuf *buf)\n\nA few more things to note.\n\n * interpret_nth_last_branch() probably should return how many bytes it\n   consumed, instead of returning 0 in the successful case.  This is to\n   allow things like \"git merge @{-1}~2\" to be easily parsed, either by\n   \"git merge\" itself into \"git merge junk~2\", which would result in\n   \"Merge branch junk (early part)\", or by get_sha1() which would result\n   in \"Merge commit deadbeefacebeads\".\n\n * I mentioned resolve_ref() may need to be told about this syntax but I\n   do not think it is necessary.  If a command that can take an arbitrary\n   refname or committish in the most general case does something special\n   when the end user input is a branch name (\"git checkout\" is a prime\n   example for this, but \"git merge\" also has this property, illustrated\n   by the previous \"Merge branch junk\" example), these commands has to do\n   their own special case logic before the user input hits get_sha1() or\n   resolve_ref() anyway (setup_branch_path() in builtin-checkout.c is a\n   good example of this), and such special case logic can and probably\n   should use interpret_nth_last_branch() directly.\n"},{"id":"100709","messageId":"200901161008.16234.trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901151510340.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-16T09:08:13Z","receivedAt":"2009-01-16T09:08:13Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> - AFAICT your version could never be convinced to resurrect deleted \n>   branches, without resorting to reflogs anyway.\n\nSpeaking of resurrection, there are other possible sources that a\nbranch tip could be gleaned from.  How about the script below?  The\nadvantage is that it can even be used to recover Junio's topic\nbranches by looking at the merges in 'pu'.\n\n(I'll answer the rest later.)\n\n--- 8< ---\n#!/bin/sh\n\n. git-sh-setup\n\nUSAGE=\"<branch>\"\n\ntest \"$#\" = 1 || usage\n\nbranch=\"$1\"\ncandidates=\n\nsearch_reflog () {\n\tnext=\n\tgit reflog show HEAD |\n\twhile read sha ref msg; do\n\t\tif test -n \"$next\"; then\n\t\t\tnext=\n\t\t\techo ${sha%...}\n\t\tfi\n\t\tif echo \"$msg\" | grep -q \"^checkout: moving from $branch \"; then\n\t\t\tnext=t\n\t\tfi\n\t\tif echo \"$msg\" | grep -q \"^merge $branch:\"; then\n\t\t\tgit rev-list --parents -1 ${sha%...} \\\n\t\t\t\t| cut -d' ' -f3\n\t\tfi\n\tdone\n}\n\nsearch_merges () {\n\tgit rev-list --pretty=tformat:\"%h %p:%s\" --all |\n\tgrep \"Merge branch.*'$branch'.*into\" |\n\twhile read sha rest; do\n\t\tparents=\"$(echo \"$rest\" | cut -d: -f1)\"\n\t\tcase \"$parents\" in\n\t\t    *' '*' '*)\n\t\t\twarn \"$branch took part in octopus merge $sha\"\n\t\t\twarn \"check manually!\"\n\t\t\t;;\n\t\t    *' '*)\n\t\t\techo \"$parents\" | cut -d' ' -f2\n\t\t\t;;\n\t\tesac\n\tdone\n}\n\nsearch_merge_targets () {\n\tgit rev-list --pretty=tformat:\"%h %s\" --all |\n\tgrep \"Merge branch '[^']*' into $branch$\" |\n\tcut -d' ' -f1\n}\n\ncandidates=\"$(search_reflog | sort -u)\"\nif test -z \"$candidates\"; then\n\techo \"** Searching merges... **\"\n\tcandidates=\"$( (search_merges;search_merge_targets) | sort -u)\"\nfi\n\necho \"** Candidates **\"\nfor cmt in $candidates; do\n\tgit --no-pager log --pretty=oneline --abbrev-commit -1 $cmt\ndone\n\nnewest=$(git rev-list -1 $candidates)\n\nif ! git rev-parse --verify --quiet $branch >/dev/null; then\n\tprintf \"** Restoring $branch to \"\n\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n\tgit branch $branch $newest\nelse\n\tprintf \"Most recent among them: \"\n\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n\techo \"** $branch already exists, doing nothing\"\nfi\n\n"},{"id":"100715","messageId":"alpine.DEB.1.00.0901161213370.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"200901161008.16234.trast@student.ethz.ch","subject":"Re: [PATCH] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-16T11:18:48Z","receivedAt":"2009-01-16T11:18:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 16 Jan 2009, Thomas Rast wrote:\n\n> search_reflog () {\n> \tnext=\n> \tgit reflog show HEAD |\n> \twhile read sha ref msg; do\n> \t\tif test -n \"$next\"; then\n> \t\t\tnext=\n> \t\t\techo ${sha%...}\n> \t\tfi\n> \t\tif echo \"$msg\" | grep -q \"^checkout: moving from $branch \"; then\n> \t\t\tnext=t\n> \t\tfi\n> \t\tif echo \"$msg\" | grep -q \"^merge $branch:\"; then\n> \t\t\tgit rev-list --parents -1 ${sha%...} \\\n> \t\t\t\t| cut -d' ' -f3\n> \t\tfi\n> \tdone\n> }\n\nHow about this instead:\n\nsearch_reflog () {\n\tsed -n 's/\\([^ ]*\\) .*\\tcheckout: moving from $branch .*/\\1/p' \\\n\t\t< .git/logs/HEAD\n}\n\nOf course, this leaves out the merges...  but I'd make that a command line \noption anyway: would you like to resurrect a branch that you recently were \non, or one that you recently merged, or one that was merged by someone \nelse?\n\nCiao,\nDscho\n"},{"id":"100724","messageId":"alpine.DEB.1.00.0901161329490.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"7vocy8s51o.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] checkout: implement \"-\" shortcut name for last branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-16T12:31:42Z","receivedAt":"2009-01-16T12:31:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, Junio C Hamano wrote:\n\n> I do not see a reason to limit the new notation \"where I was\" only to \n> \"git checkout\".  Wouldn't it be handy if you can use the notation as the \n> other branch to merge from, or the commit to rebase on?\n> \n> [...]\n> \n> Another reason is the one level limitation.  If we do not use LAST_HEAD,\n> and instead used HEAD reflog, to get to this information, there is no\n> reason we cannot to give an equally easy access to the second from the\n> last branch the user was on.\n> \n> So I think it is just the matter of coming up with a clever syntax that\n> works on reflogs to name the nth last branch we were on and teach that\n> syntax to both get_sha1() and resolve_ref().\n> \n> With the attached illustration patch,\n> \n>      $ git checkout junk\n>      $ git chekcout master\n>      $ git checkout @{-1}\n> \n> will take you back to junk branch.  It probably would serve as a starting\n> point, if anybody is interested.\n\nI like it.  Additionaly, we could teach \"checkout\" that \"-\" is \nequivalent to \"@{-1}\", as checkout cannot possibly take stdin, so \nit would not hurt.  Thomas?\n\nCiao,\nDscho\n"},{"id":"100727","messageId":"alpine.DEB.1.00.0901161351460.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"7vhc40s50t.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] revision walker: include a detached HEAD in --all","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-16T12:52:53Z","receivedAt":"2009-01-16T12:52:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWhen HEAD is detached, --all should list it, too, logically, as a\ndetached HEAD is by definition a temporary, unnamed branch.\n\nIt is especially necessary to list it when garbage collecting, as\nthe detached HEAD would be trashed.\n\nNoticed by Thomas Rast.\n\nNote that this affects creating bundles with --all; I contend that it\nis a good change to add the HEAD, so that cloning from such a bundle\nwill give you a current branch.  However, I had to fix t5701 as it\nassumed that --all does not imply HEAD.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tOn Thu, 15 Jan 2009, Junio C Hamano wrote:\n\n\t> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\t> \n\t> > On Thu, 15 Jan 2009, Johannes Schindelin wrote:\n\t> >\n\t> >> [PATCH] pack-objects --all: include HEAD, which could be \n\t> >> detached\n\t> >\n\t> > In hind sight, it would probably be better to add this to \n\t> > revision.c.\n\t> \n\t> If you mean that \"git log --all\" should also include a possibly \n\t> detached HEAD in its traversal, and a patch that implements such a fix \n\t> would automatically fix \"repack -a\" without the patch you are \n\t> responding to, I think I agree 100%.\n\n\tHere it is.  (Sorry for the delay, it was due to some \n\twell-deserved inebriation.)\n\n revision.c              |    1 +\n t/t5701-clone-local.sh  |    4 ++--\n t/t6014-rev-list-all.sh |   38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 41 insertions(+), 2 deletions(-)\n create mode 100755 t/t6014-rev-list-all.sh\n\ndiff --git a/revision.c b/revision.c\nindex db60f06..b065184 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1263,6 +1263,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch\n \n \t\t\tif (!strcmp(arg, \"--all\")) {\n \t\t\t\thandle_refs(revs, flags, for_each_ref);\n+\t\t\t\thandle_refs(revs, flags, head_ref);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--branches\")) {\ndiff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh\nindex 8dfaaa4..14413f8 100755\n--- a/t/t5701-clone-local.sh\n+++ b/t/t5701-clone-local.sh\n@@ -11,8 +11,8 @@ test_expect_success 'preparing origin repository' '\n \tgit clone --bare . x &&\n \ttest \"$(GIT_CONFIG=a.git/config git config --bool core.bare)\" = true &&\n \ttest \"$(GIT_CONFIG=x/config git config --bool core.bare)\" = true\n-\tgit bundle create b1.bundle --all HEAD &&\n-\tgit bundle create b2.bundle --all &&\n+\tgit bundle create b1.bundle master HEAD &&\n+\tgit bundle create b2.bundle master &&\n \tmkdir dir &&\n \tcp b1.bundle dir/b3\n \tcp b1.bundle b4\ndiff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh\nnew file mode 100755\nindex 0000000..991ab4a\n--- /dev/null\n+++ b/t/t6014-rev-list-all.sh\n@@ -0,0 +1,38 @@\n+#!/bin/sh\n+\n+test_description='--all includes detached HEADs'\n+\n+. ./test-lib.sh\n+\n+\n+commit () {\n+\ttest_tick &&\n+\techo $1 > foo &&\n+\tgit add foo &&\n+\tgit commit -m \"$1\"\n+}\n+\n+test_expect_success 'setup' '\n+\n+\tcommit one &&\n+\tcommit two &&\n+\tgit checkout HEAD^ &&\n+\tcommit detached\n+\n+'\n+\n+test_expect_success 'rev-list --all lists detached HEAD' '\n+\n+\ttest 3 = $(git rev-list --all | wc -l)\n+\n+'\n+\n+test_expect_success 'repack does not lose detached HEAD' '\n+\n+\tgit gc &&\n+\tgit prune --expire=now &&\n+\tgit show HEAD\n+\n+'\n+\n+test_done\n-- \n1.6.1.299.gfdbb\n"},{"id":"100731","messageId":"adf1fd3d0901160512i2de8f473gd471cc1dcb72afa4@mail.gmail.com","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901161351460.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2009-01-16T13:12:16Z","receivedAt":"2009-01-16T13:12:16Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>\n> When HEAD is detached, --all should list it, too, logically, as a\n> detached HEAD is by definition a temporary, unnamed branch.\n>\n> It is especially necessary to list it when garbage collecting, as\n> the detached HEAD would be trashed.\n>\n> Noticed by Thomas Rast.\n>\n> Note that this affects creating bundles with --all; I contend that it\n> is a good change to add the HEAD, so that cloning from such a bundle\n> will give you a current branch.  However, I had to fix t5701 as it\n> assumed that --all does not imply HEAD.\n\n>From the description I understand that it only affects when the HEAD\nis detached, but in t5701 the HEAD is not detached so nothing should\nbe fixed.\n\nFor gc for sure it is a good thing, but I'm not convinced of the\nothers, as a detached HEAD is a very special thing (temporary and\nunnamed branch).\n\nSanti\n"},{"id":"100732","messageId":"alpine.DEB.1.00.0901161415230.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"adf1fd3d0901160512i2de8f473gd471cc1dcb72afa4@mail.gmail.com","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-16T13:17:56Z","receivedAt":"2009-01-16T13:17:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 16 Jan 2009, Santi Béjar wrote:\n\n> 2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n> >\n> > Note that this affects creating bundles with --all; I contend that it \n> > is a good change to add the HEAD, so that cloning from such a bundle \n> > will give you a current branch.  However, I had to fix t5701 as it \n> > assumed that --all does not imply HEAD.\n> \n> From the description I understand that it only affects when the HEAD is \n> detached, but in t5701 the HEAD is not detached so nothing should be \n> fixed.\n\nThe error in t5701 was that it _wanted_ to test a bundle without a HEAD, \nbut it actually created it with --all.  That was implying that --all does \nnot mean HEAD, and I disagree with that.\n\n> For gc for sure it is a good thing, but I'm not convinced of the others, \n> as a detached HEAD is a very special thing (temporary and unnamed \n> branch).\n\nSo?  What does \"--all\" mean?  All branches or what? :-)\n\nSeriously, I think that --all should imply HEAD at all times, as the only \ntime when it makes a difference is when you have that unnamed _branch_ \nthat is a detached HEAD.\n\nMaybe I would be more amenable to your criticism if you could come up with \na scenario where implying HEAD with --all is _wrong_.\n\nCiao,\nDscho\n"},{"id":"100735","messageId":"868wpbmlmb.fsf@lola.quinscape.zz","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901161415230.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"David Kastrup","fromEmail":"dak@gnu.org","sentAt":"2009-01-16T13:22:20Z","receivedAt":"2009-01-16T13:22:20Z","isPatch":true,"sender":{"key":"dak@gnu.org","avatar":"https://avatars.githubusercontent.com/u/52141349?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Fri, 16 Jan 2009, Santi Béjar wrote:\n>\n>> 2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>> >\n>> > Note that this affects creating bundles with --all; I contend that it \n>> > is a good change to add the HEAD, so that cloning from such a bundle \n>> > will give you a current branch.  However, I had to fix t5701 as it \n>> > assumed that --all does not imply HEAD.\n>> \n>> From the description I understand that it only affects when the HEAD is \n>> detached, but in t5701 the HEAD is not detached so nothing should be \n>> fixed.\n>\n> The error in t5701 was that it _wanted_ to test a bundle without a HEAD, \n> but it actually created it with --all.  That was implying that --all does \n> not mean HEAD, and I disagree with that.\n\nI don't think that a detached HEAD should be a special case, since you\ncan have other detached symbolic references no longer on a branch.  None\nof those should be garbage-collected either, I think.\n\n-- \nDavid Kastrup\n"},{"id":"100739","messageId":"adf1fd3d0901160546o50db0594h7377774fed9fef99@mail.gmail.com","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901161415230.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2009-01-16T13:46:01Z","receivedAt":"2009-01-16T13:46:01Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n> Hi,\n>\n> On Fri, 16 Jan 2009, Santi Béjar wrote:\n>\n>> 2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>> >\n>> > Note that this affects creating bundles with --all; I contend that it\n>> > is a good change to add the HEAD, so that cloning from such a bundle\n>> > will give you a current branch.  However, I had to fix t5701 as it\n>> > assumed that --all does not imply HEAD.\n>>\n>> From the description I understand that it only affects when the HEAD is\n>> detached, but in t5701 the HEAD is not detached so nothing should be\n>> fixed.\n>\n> The error in t5701 was that it _wanted_ to test a bundle without a HEAD,\n> but it actually created it with --all.  That was implying that --all does\n> not mean HEAD\n\nYes, that is the current behaviour.\n\n> , and I disagree with that.\n\nI know you disagree, but in the commit log you said:\n\n---\n[PATCH] revision walker: include a detached HEAD in --all\n\nWhen HEAD is detached, --all should list it, too, logically, as a\ndetached HEAD is by definition a temporary, unnamed branch.\n---\n\nso nothing talks about changing the behaviour when the HEAD is not detached.\n\nBut the problem with t5701 is another thing. If you run this:\n\ngit init\n: >file\ngit add .\ngit commit -m1\ngit bundle create b1.bundle --all HEAD\ngit ls-remote b1.bundle\ngit rev-parse --all HEAD\n\nyou will see that the same rev-parse parameters in \"git bundle\"\nproduce tree lines while with \"git rev-parse\" only two are produced.\n\n\n>\n>> For gc for sure it is a good thing, but I'm not convinced of the others,\n>> as a detached HEAD is a very special thing (temporary and unnamed\n>> branch).\n>\n> So?  What does \"--all\" mean?  All branches or what? :-)\n>\n> Seriously, I think that --all should imply HEAD at all times, as the only\n> time when it makes a difference is when you have that unnamed _branch_\n> that is a detached HEAD.\n>\n> Maybe I would be more amenable to your criticism if you could come up with\n> a scenario where implying HEAD with --all is _wrong_.\n\nI don't think it is plainly wrong. I think both makes sense, but I\nthink it is not a good idea to change the behaviour now as some\nscripts may rely on it.\n\nSanti\n"},{"id":"100738","messageId":"adf1fd3d0901160550m200e9478t5755ebdc176f09a2@mail.gmail.com","threadId":"17182","inReplyTo":"adf1fd3d0901160546o50db0594h7377774fed9fef99@mail.gmail.com","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2009-01-16T13:50:45Z","receivedAt":"2009-01-16T13:50:45Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"2009/1/16 Santi Béjar <santi@agolina.net>:\n> 2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>> Hi,\n>>\n>> On Fri, 16 Jan 2009, Santi Béjar wrote:\n>>\n>>> 2009/1/16 Johannes Schindelin <Johannes.Schindelin@gmx.de>:\n>>> >\n>>> > Note that this affects creating bundles with --all; I contend that it\n>>> > is a good change to add the HEAD, so that cloning from such a bundle\n>>> > will give you a current branch.  However, I had to fix t5701 as it\n>>> > assumed that --all does not imply HEAD.\n>>>\n>>> From the description I understand that it only affects when the HEAD is\n>>> detached, but in t5701 the HEAD is not detached so nothing should be\n>>> fixed.\n>>\n>> The error in t5701 was that it _wanted_ to test a bundle without a HEAD,\n>> but it actually created it with --all.  That was implying that --all does\n>> not mean HEAD\n>\n> Yes, that is the current behaviour.\n>\n>> , and I disagree with that.\n>\n> I know you disagree, but in the commit log you said:\n>\n> ---\n> [PATCH] revision walker: include a detached HEAD in --all\n>\n> When HEAD is detached, --all should list it, too, logically, as a\n> detached HEAD is by definition a temporary, unnamed branch.\n> ---\n>\n> so nothing talks about changing the behaviour when the HEAD is not detached.\n>\n> But the problem with t5701 is another thing. If you run this:\n\n>\n> git init\n> : >file\n> git add .\n> git commit -m1\n> git bundle create b1.bundle --all HEAD\n> git ls-remote b1.bundle\n> git rev-parse --all HEAD\n>\n> you will see that the same rev-parse parameters in \"git bundle\"\n> produce tree lines while with \"git rev-parse\" only two are produced.\n>\n\nSorry, there are two problems with t5701, the one of the changing\nbehaviour of the --all flag and this one.\n\nSanti\n"},{"id":"100906","messageId":"1232242703-19086-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901161213370.3586@pacific.mpi-cbg.de","subject":"[TOY PATCH] git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-18T01:38:23Z","receivedAt":"2009-01-18T01:38:23Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a tool 'git resurrect <branch>...' that tries to find traces of\neach <branch> in the HEAD reflog and, optionally, all merge commits in\nthe repository.  It can then resurrect the branch, pointing it at the\nmost recent of all candidate commits found.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nSo here's a slightly more polished version so gmane can keep it\nforever.  Thanks for the sed trick!  I was too lazy to add more\noptions, but at least there's a \"fast\" and a \"complete\" mode.\n\n\n\n Makefile         |    1 +\n git-resurrect.sh |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 110 insertions(+), 0 deletions(-)\n create mode 100755 git-resurrect.sh\n\ndiff --git a/Makefile b/Makefile\nindex 2b873fa..87cb539 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -260,6 +260,7 @@ SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-parse-remote.sh\n SCRIPT_SH += git-pull.sh\n+SCRIPT_SH += git-resurrect.sh\n SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-rebase--interactive.sh\n SCRIPT_SH += git-rebase.sh\ndiff --git a/git-resurrect.sh b/git-resurrect.sh\nnew file mode 100755\nindex 0000000..6d5a0c7\n--- /dev/null\n+++ b/git-resurrect.sh\n@@ -0,0 +1,109 @@\n+#!/bin/sh\n+\n+USAGE=\"git resurrect [-m | --merges] [-n | --dry-run] <name>...\"\n+LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip called <name>,\n+and tries to resurrect it.  Currently, the reflog is searched for\n+checkout and merge messages.  With --merges, the history of all refs\n+is scanned for merge commit subjects, which is rather slow but allows\n+you to resurrect other people's topic branches.\"\n+\n+. git-sh-setup\n+cd_to_toplevel\n+\n+OPTIONS_SPEC=\"\\\n+git resurrect [-m | --merges] [-n | --dry-run] <name>...\n+--\n+m,merges             also scan merges (slow)\n+n,dry-run            don't recreate the branch\"\n+\n+test \"$#\" = 0 && usage\n+\n+eval \"$(echo \"$OPTIONS_SPEC\" | git rev-parse --parseopt -- \"$@\" || echo exit $?)\"\n+\n+search_reflog () {\n+        sed -n 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_reflog_merges () {\n+        sed -n 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_merges () {\n+\tgit rev-list --pretty=tformat:\"%h %p:%s\" --all |\n+\tgrep \"Merge branch.*'$branch'.*into\" |\n+\twhile read sha rest; do\n+\t\tparents=\"$(echo \"$rest\" | cut -d: -f1)\"\n+\t\tcase \"$parents\" in\n+\t\t    *' '*' '*)\n+\t\t\twarn \"$branch took part in octopus merge $sha\"\n+\t\t\twarn \"check manually!\"\n+\t\t\t;;\n+\t\t    *' '*)\n+\t\t\techo \"$parents\" | cut -d' ' -f2\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+search_merge_targets () {\n+\tgit rev-list --pretty=tformat:\"%h %s\" --all |\n+\tgrep \"Merge branch '[^']*' into $branch$\" |\n+\tcut -d' ' -f1\n+}\n+\n+dry_run=\n+scan_merges=\n+\n+while test \"$#\" != 0; do\n+\tcase \"$1\" in\n+\t    -n|--dry-run)\n+\t\tdry_run=t\n+\t\t;;\n+\t    -m|--merges)\n+\t\tscan_merges=t\n+\t\t;;\n+\t    --)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t    *)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+for branch in \"$@\"; do\n+\tcandidates=\"$(search_reflog $1; search_reflog_merges $1)\"\n+\tif test ! -z \"$scan_merges\"; then\n+\t\tcandidates=\"$candidates $(search_merges $1; search_merge_targets $1)\"\n+\tfi\n+\n+\tcandidates=\"$(git rev-parse $candidates | sort -u)\"\n+\n+\tif test -z \"$candidates\"; then\n+\t\techo \"** No candidates for $branch found **\"\n+\t\ttest -z \"$scan_merges\" && echo \"(maybe try again with -m)\"\n+\telse\n+\t\techo \"** Candidates for $branch **\"\n+\t\tfor cmt in $candidates; do\n+\t\t\tgit --no-pager log --pretty=oneline --abbrev-commit -1 $cmt\n+\t\tdone\n+\n+\t\tnewest=\"$(git rev-list -1 $candidates)\"\n+\t\tif test ! -z \"$dry_run\"; then\n+\t\t\tprintf \"Most recent: \"\n+\t\t\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\t\telif ! git rev-parse --verify --quiet $branch >/dev/null; then\n+\t\t\tprintf \"** Restoring $branch to \"\n+\t\t\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\t\t\tgit branch $branch $newest\n+\t\telse\n+\t\t\tprintf \"Most recent: \"\n+\t\t\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\t\t\techo \"** $branch already exists, doing nothing\"\n+\t\tfi\n+\tfi\n+done\n-- \n1.6.1.320.gd5dca.dirty\n"},{"id":"100919","messageId":"7v8wp917c3.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901161351460.3586@pacific.mpi-cbg.de","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-18T06:01:00Z","receivedAt":"2009-01-18T06:01:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> When HEAD is detached, --all should list it, too, logically, as a\n> detached HEAD is by definition a temporary, unnamed branch.\n>\n> It is especially necessary to list it when garbage collecting, as\n> the detached HEAD would be trashed.\n>\n> Noticed by Thomas Rast.\n>\n> Note that this affects creating bundles with --all; I contend that it\n> is a good change to add the HEAD, so that cloning from such a bundle\n> will give you a current branch.  However, I had to fix t5701 as it\n> assumed that --all does not imply HEAD.\n\nSorry, but I do not understand.\n\n> diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh\n> index 8dfaaa4..14413f8 100755\n> --- a/t/t5701-clone-local.sh\n> +++ b/t/t5701-clone-local.sh\n> @@ -11,8 +11,8 @@ test_expect_success 'preparing origin repository' '\n>  \tgit clone --bare . x &&\n>  \ttest \"$(GIT_CONFIG=a.git/config git config --bool core.bare)\" = true &&\n>  \ttest \"$(GIT_CONFIG=x/config git config --bool core.bare)\" = true\n> -\tgit bundle create b1.bundle --all HEAD &&\n> -\tgit bundle create b2.bundle --all &&\n> +\tgit bundle create b1.bundle master HEAD &&\n> +\tgit bundle create b2.bundle master &&\n\nBecause --all did not imply HEAD, \"--all HEAD\" used to be the way to say\n\"everything and HEAD\".  Now --all does imply HEAD, but it should still be\na valid way to say \"everything, by the way, do not forget HEAD\".\n\nDoes the first one need to be changed to \"master HEAD\"?  If \"--all HEAD\"\nmakes the rest of the test unhappy because HEAD is listed twice, perhaps\nthat is an independent bug that needs to be fixed?\n\nFor that matter, what does \"git bundle create x HEAD HEAD\" do?  Does it\nlist HEAD twice?\n"},{"id":"100920","messageId":"7v3afh15pi.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"7v8wp917c3.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-18T06:36:09Z","receivedAt":"2009-01-18T06:36:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> ...\n>> Note that this affects creating bundles with --all; I contend that it\n>> is a good change to add the HEAD, so that cloning from such a bundle\n>> will give you a current branch.  However, I had to fix t5701 as it\n>> assumed that --all does not imply HEAD.\n>\n> Sorry, but I do not understand.\n>\n>> diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh\n>> index 8dfaaa4..14413f8 100755\n>> --- a/t/t5701-clone-local.sh\n>> +++ b/t/t5701-clone-local.sh\n>> @@ -11,8 +11,8 @@ test_expect_success 'preparing origin repository' '\n>>  \tgit clone --bare . x &&\n>>  \ttest \"$(GIT_CONFIG=a.git/config git config --bool core.bare)\" = true &&\n>>  \ttest \"$(GIT_CONFIG=x/config git config --bool core.bare)\" = true\n>> -\tgit bundle create b1.bundle --all HEAD &&\n>> -\tgit bundle create b2.bundle --all &&\n>> +\tgit bundle create b1.bundle master HEAD &&\n>> +\tgit bundle create b2.bundle master &&\n>\n> Because --all did not imply HEAD, \"--all HEAD\" used to be the way to say\n> \"everything and HEAD\".  Now --all does imply HEAD, but it should still be\n> a valid way to say \"everything, by the way, do not forget HEAD\".\n>\n> Does the first one need to be changed to \"master HEAD\"?  If \"--all HEAD\"\n> makes the rest of the test unhappy because HEAD is listed twice, perhaps\n> that is an independent bug that needs to be fixed?\n>\n> For that matter, what does \"git bundle create x HEAD HEAD\" do?  Does it\n> list HEAD twice?\n\nWith a patch like this, I think b1.bundle can be created with \"--all HEAD\"\nas before.\n\nOf course, to advertise that --all now includes HEAD and it is a _good_\nthing, we may want to even say \"git bundle create b1.bundle --all\" in the\nabove test sequence.\n\nCreation of b2.bundle should say \"master\" explicitly as in your patch,\nbecause the point of that bundle is to test a use of such HEAD-less bundle\nin the later parts of the script.\n\n-- >8 --\nSubject: [PATCH] bundle: allow the same ref to be given more than once\n\n\"git bundle create x master master\" used to create a bundle that lists\nthe same branch (master) twice.  Cloning from such a bundle resulted in\na needless warning \"warning: Duplicated ref: refs/remotes/origin/master\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n bundle.c |    2 ++\n object.c |   19 +++++++++++++++++++\n object.h |    1 +\n 3 files changed, 22 insertions(+), 0 deletions(-)\n\ndiff --git a/bundle.c b/bundle.c\nindex daecd8e..b20f210 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -240,6 +240,8 @@ int create_bundle(struct bundle_header *header, const char *path,\n \t\treturn error(\"unrecognized argument: %s'\", argv[i]);\n \t}\n \n+\tobject_array_remove_duplicates(&revs.pending);\n+\n \tfor (i = 0; i < revs.pending.nr; i++) {\n \t\tstruct object_array_entry *e = revs.pending.objects + i;\n \t\tunsigned char sha1[20];\ndiff --git a/object.c b/object.c\nindex 50b6528..7e6a92c 100644\n--- a/object.c\n+++ b/object.c\n@@ -268,3 +268,22 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj\n \tobjects[nr].mode = mode;\n \tarray->nr = ++nr;\n }\n+\n+void object_array_remove_duplicates(struct object_array *array)\n+{\n+\tint ref, src, dst;\n+\tstruct object_array_entry *objects = array->objects;\n+\n+\tfor (ref = 0; ref < array->nr - 1; ref++) {\n+\t\tfor (src = ref + 1, dst = src;\n+\t\t     src < array->nr;\n+\t\t     src++) {\n+\t\t\tif (!strcmp(objects[ref].name, objects[src].name))\n+\t\t\t\tcontinue;\n+\t\t\tif (src != dst)\n+\t\t\t\tobjects[dst] = objects[src];\n+\t\t\tdst++;\n+\t\t}\n+\t\tarray->nr = dst;\n+\t}\n+}\ndiff --git a/object.h b/object.h\nindex 036bd66..3193916 100644\n--- a/object.h\n+++ b/object.h\n@@ -71,5 +71,6 @@ int object_list_contains(struct object_list *list, struct object *obj);\n /* Object array handling .. */\n void add_object_array(struct object *obj, const char *name, struct object_array *array);\n void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);\n+void object_array_remove_duplicates(struct object_array *);\n \n #endif /* OBJECT_H */\n-- \n1.6.1.208.g58df\n"},{"id":"100964","messageId":"alpine.DEB.1.00.0901181440550.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"7v8wp917c3.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-18T13:42:25Z","receivedAt":"2009-01-18T13:42:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 17 Jan 2009, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > When HEAD is detached, --all should list it, too, logically, as a\n> > detached HEAD is by definition a temporary, unnamed branch.\n> >\n> > It is especially necessary to list it when garbage collecting, as\n> > the detached HEAD would be trashed.\n> >\n> > Noticed by Thomas Rast.\n> >\n> > Note that this affects creating bundles with --all; I contend that it\n> > is a good change to add the HEAD, so that cloning from such a bundle\n> > will give you a current branch.  However, I had to fix t5701 as it\n> > assumed that --all does not imply HEAD.\n> \n> Sorry, but I do not understand.\n> \n> > diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh\n> > index 8dfaaa4..14413f8 100755\n> > --- a/t/t5701-clone-local.sh\n> > +++ b/t/t5701-clone-local.sh\n> > @@ -11,8 +11,8 @@ test_expect_success 'preparing origin repository' '\n> >  \tgit clone --bare . x &&\n> >  \ttest \"$(GIT_CONFIG=a.git/config git config --bool core.bare)\" = true &&\n> >  \ttest \"$(GIT_CONFIG=x/config git config --bool core.bare)\" = true\n> > -\tgit bundle create b1.bundle --all HEAD &&\n> > -\tgit bundle create b2.bundle --all &&\n> > +\tgit bundle create b1.bundle master HEAD &&\n> > +\tgit bundle create b2.bundle master &&\n> \n> Because --all did not imply HEAD, \"--all HEAD\" used to be the way to say\n> \"everything and HEAD\".  Now --all does imply HEAD, but it should still be\n> a valid way to say \"everything, by the way, do not forget HEAD\".\n> \n> Does the first one need to be changed to \"master HEAD\"?  If \"--all HEAD\"\n> makes the rest of the test unhappy because HEAD is listed twice, perhaps\n> that is an independent bug that needs to be fixed?\n\nI changed it away from --all because I am a fan of being explicit.  We \nwant a bundle here that has master and HEAD in it.  This being a test \ncase, being lazy is so wrong here.  You should describe what you actually \nwant, not use a set of parameters that just happens to work (by chance as \nwe saw).\n\nCiao,\nDscho\n"},{"id":"100965","messageId":"alpine.DEB.1.00.0901181442370.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"7v3afh15pi.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] revision walker: include a detached HEAD in --all","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-18T14:06:42Z","receivedAt":"2009-01-18T14:06:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 17 Jan 2009, Junio C Hamano wrote:\n\n> Subject: [PATCH] bundle: allow the same ref to be given more than once\n> \n> \"git bundle create x master master\" used to create a bundle that lists\n> the same branch (master) twice.  Cloning from such a bundle resulted in\n> a needless warning \"warning: Duplicated ref: refs/remotes/origin/master\".\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  bundle.c |    2 ++\n>  object.c |   19 +++++++++++++++++++\n>  object.h |    1 +\n>  3 files changed, 22 insertions(+), 0 deletions(-)\n\nYes, that would be good.  You have my ACK on that if you want.\n\nAnother thing I am thinking about on and off:\n\nYou cannot really use bundles as a replacement for regular transports \n(e.g. when you administrator does not let you ssh or git:// out, and you \ndo not have an HTTP server available [*1*]).\n\nSuppose you have two branches, 'master' and 'side'.  Now you make changes \nto 'master' and send the complete repository as a bundle to your friend.  \nNow you delete the branch 'side', and send the next bundle (created with \n--all implying HEAD, and --since=$(stat -c %Y <first-bundle>)).\n\nThen your friend has no idea if 'side' was deleted or untouched.\n\nSo I think we'd need some option for \"create bundle\" to list all specified \nrefs, and if they have not really changed, their SHA-1s as prerequisites, \ntoo.  Maybe \"--full-bundle\", or just \"--full\"?\n\nAnother problem: suppose you have a branch, called 'private', that you \nexcluded from your bundles.  Now you happened to make changes to it, and \nby mistake, the branch gets included in the incremental bundle.  No \nproblem for you, as your friend lacks the prerequisites to reconstruct it.\n\nBut unfortunately, your friend cannot even pull 'master' from it, because \nof our overzealous verification process which refuses all fetches when \nsome prerequisites are missing locally, even if they are not even needed.\n\nThis problem is much harder to solve, I think, and maybe we just want to \nleave it: as bundles are just fed into index-pack --fix-thin, which has no \nidea what objects can be skipped.  Maybe there is no clean solution to \nthat to begin with.\n\nCiao,\nDscho\n\n[*1*] When people are stuck behind such a stupidly restrictive firewall, \noften people come with \"helpful\" suggestions to use a VPN, or to publish \nyour private repository, or get an external machine to HTTP proxy their \nconnections.  I find it outright mean to waste the time of people who \nalready have a big problem.\n\nHowever, I believe that a mail based bundle exchange should be a \nrelatively easy way out for those situations, once it works.\n"},{"id":"101015","messageId":"alpine.DEB.1.00.0901181718370.3586@pacific.mpi-cbg.de","threadId":"17182","inReplyTo":"1232242703-19086-1-git-send-email-trast@student.ethz.ch","subject":"Re: [TOY PATCH] git-resurrect: find traces of a branch name and resurrect it","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-18T16:19:14Z","receivedAt":"2009-01-18T16:19:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 18 Jan 2009, Thomas Rast wrote:\n\n>  Makefile         |    1 +\n>  git-resurrect.sh |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\nMaybe have it in contrib/ instead?\n\nCiao,\nDscho\n"},{"id":"101236","messageId":"200901201001.54979.trast@student.ethz.ch","threadId":"17182","inReplyTo":"alpine.DEB.1.00.0901181718370.3586@pacific.mpi-cbg.de","subject":"Re: [TOY PATCH] git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-20T09:01:50Z","receivedAt":"2009-01-20T09:01:50Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"[Sorry for missing this message, it seems KMail4 does have some rather\nannoying filtering bugs...]\n\nJohannes Schindelin wrote:\n> On Sun, 18 Jan 2009, Thomas Rast wrote:\n> \n> >  Makefile         |    1 +\n> >  git-resurrect.sh |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n> \n> Maybe have it in contrib/ instead?\n\nIt was really intended as a toy patch, but if people find it useful\n(Boyd?) I can add the rest of the options so that all searches can be\nchosen independently, and shape it as a \"real\" contrib patch.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"101271","messageId":"200901201057.18127.bss@iguanasuicide.net","threadId":"17182","inReplyTo":"200901201001.54979.trast@student.ethz.ch","subject":"Re: [TOY PATCH] git-resurrect: find traces of a branch name and resurrect it","fromName":"Boyd Stephen Smith Jr.","fromEmail":"bss@iguanasuicide.net","sentAt":"2009-01-20T16:57:17Z","receivedAt":"2009-01-20T16:57:17Z","isPatch":true,"sender":{"key":"bss@iguanasuicide.net","avatar":"https://gravatar.com/avatar/84b95eeff194b816c1568b1339e63e4b229825298664a9037b9f1ec713ead1e3?d=mp&s=160"},"body":"On Tuesday 2009 January 20 03:01:50 Thomas Rast wrote:\n>It was really intended as a toy patch, but if people find it useful\n>(Boyd?) I can add the rest of the options so that all searches can be\n>chosen independently, and shape it as a \"real\" contrib patch.\n\nI'll test it out later today and get back to you.\n\n[OT]\nI actually prefer Stephen; My father is Boyd.\n-- \nBoyd Stephen Smith Jr.                     ,= ,-_-. =. \nbss@iguanasuicide.net                     ((_/)o o(\\_))\nICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' \nhttp://iguanasuicide.net/                      \\_/     \n"},{"id":"101299","messageId":"200901201450.53450.bss@iguanasuicide.net","threadId":"17182","inReplyTo":"200901201057.18127.bss@iguanasuicide.net","subject":"Re: [TOY PATCH] git-resurrect: find traces of a branch name and resurrect it","fromName":"Boyd Stephen Smith Jr.","fromEmail":"bss@iguanasuicide.net","sentAt":"2009-01-20T20:50:48Z","receivedAt":"2009-01-20T20:50:48Z","isPatch":true,"sender":{"key":"bss@iguanasuicide.net","avatar":"https://gravatar.com/avatar/84b95eeff194b816c1568b1339e63e4b229825298664a9037b9f1ec713ead1e3?d=mp&s=160"},"body":"On Tuesday 2009 January 20 10:57:17 Boyd Stephen Smith Jr. wrote:\n>On Tuesday 2009 January 20 03:01:50 Thomas Rast wrote:\n>>It was really intended as a toy patch, but if people find it useful\n>>(Boyd?) I can add the rest of the options so that all searches can be\n>>chosen independently, and shape it as a \"real\" contrib patch.\n>\n>I'll test it out later today and get back to you.\n\nIn my particular case, it wasn't useful without the -m option, but I \nunderstand why it is not the default.\n\nI think it could be quite nice; \"undelete\"-type commands are generally \nwell-received by users and when run against reflogs alone, that's what the \ncommand is.\n\nIt's useful enough to me that I'd love to see it mainlined.\n-- \nBoyd Stephen Smith Jr.                     ,= ,-_-. =. \nbss@iguanasuicide.net                     ((_/)o o(\\_))\nICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' \nhttp://iguanasuicide.net/                      \\_/     \n"},{"id":"101684","messageId":"1232740985-4551-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"200901201450.53450.bss@iguanasuicide.net","subject":"[PATCH] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-23T20:03:05Z","receivedAt":"2009-01-23T20:03:05Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\nthe <branch> in the HEAD reflog and, optionally, all merge commits in\nthe repository.  It can then resurrect the branch, pointing it at the\nmost recent of all candidate commits found.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nBoyd Stephen Smith Jr. wrote:\n> I think it could be quite nice; \"undelete\"-type commands are generally \n> well-received by users and when run against reflogs alone, that's what the \n> command is.\n> \n> It's useful enough to me that I'd love to see it mainlined.\n\nSo here's a version for contrib with more options and some other\ntweaks.\n\nI removed the ability to \"batch resurrect\" with several <name>\narguments since that would have conflicted with -b <newname>, but\notherwise the features are the same.\n\n> In my particular case, it wasn't useful without the -m option, but I \n> understand why it is not the default.\n\nAside from the obvious speed reasons, I don't really want to teach\npeople that commits \"know\" the branch they were on.  It is a pure\ncoincidence if you can resurrect a topic branch from merge messages;\nan equivalent merge could have gone through as a fast-forward, and\nyou'd never know.\n\n\n\n contrib/git-resurrect.sh |  140 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 140 insertions(+), 0 deletions(-)\n create mode 100755 contrib/git-resurrect.sh\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\nnew file mode 100755\nindex 0000000..29bf723\n--- /dev/null\n+++ b/contrib/git-resurrect.sh\n@@ -0,0 +1,140 @@\n+#!/bin/sh\n+\n+USAGE=\"[-h] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n+LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n+called <name>, and tries to resurrect it.  Currently, the reflog is\n+searched for checkout messages, and with -r also merge messages.  With\n+-m and -t, the history of all refs is scanned for Merge <name> into\n+other/Merge <other> into <name> (respectively) commit subjects, which\n+is rather slow but allows you to resurrect other people's topic\n+branches.\"\n+\n+OPTIONS_SPEC=\"\\\n+git resurrect [-h] [-r] [-m] [-t] [-n] [-b <newname>] <name>\n+--\n+b,branch=            save branch as <newname> instead of <name>\n+H,try-hard           same as -r -m -t\n+r,reflog-merges      scan for merges recorded in reflog\n+m,merges             scan for merges into other branches (slow)\n+t,merge-targets      scan for merges of other branches into <name>\n+n,dry-run            don't recreate the branch\"\n+\n+. git-sh-setup\n+cd_to_toplevel\n+\n+search_reflog () {\n+        sed -n 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_reflog_merges () {\n+        sed -n 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_merges () {\n+\tgit rev-list --pretty=tformat:\"%h %p:%s\" --all |\n+\tgrep \"Merge branch.*'$branch'.*into\" |\n+\twhile read sha rest; do\n+\t\tparents=\"$(echo \"$rest\" | cut -d: -f1)\"\n+\t\tcase \"$parents\" in\n+\t\t    *' '*' '*)\n+\t\t\twarn \"$branch took part in octopus merge $sha\"\n+\t\t\twarn \"check manually!\"\n+\t\t\t;;\n+\t\t    *' '*)\n+\t\t\techo \"$parents\" | cut -d' ' -f2\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+search_merge_targets () {\n+\tgit rev-list --pretty=tformat:\"%h %s\" --all |\n+\tgrep \"Merge branch '[^']*' into $branch$\" |\n+\tcut -d' ' -f1\n+}\n+\n+dry_run=\n+scan_reflog_merges=\n+scan_merges=\n+scan_merge_targets=\n+new_name=\n+\n+while test \"$#\" != 0; do\n+\tcase \"$1\" in\n+\t    -b|--branch)\n+\t\tshift\n+\t\tnew_name=\"$1\"\n+\t\t;;\n+\t    -n|--dry-run)\n+\t\tdry_run=t\n+\t\t;;\n+\t    -m|--merges)\n+\t\tscan_merges=t\n+\t\t;;\n+\t    -r|--reflog_merges)\n+\t\tscan_reflog_merges=t\n+\t\t;;\n+\t    -t|--merge-targets)\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    -H|--try-hard)\n+\t\tscan_reflog_merges=t\n+\t\tscan_merges=t\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t    *)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+test \"$#\" = 1 || usage\n+\n+branch=\"$1\"\n+test -z \"$new_name\" && new_name=\"$branch\"q\n+\n+candidates=\"$(search_reflog $1)\"\n+if test ! -z \"$scan_reflog_merges\"; then\n+\tcandidates=\"$candidates $(search_reflog_merges $1)\"\n+fi\n+if test ! -z \"$scan_merges\"; then\n+\tcandidates=\"$candidates $(search_merges $1)\"\n+fi\n+if test ! -z \"$scan_merge_targets\"; then\n+\tcandidates=\"$candidates $(search_merge_targets $1)\"\n+fi\n+\n+candidates=\"$(git rev-parse $candidates | sort -u)\"\n+\n+if test -z \"$candidates\"; then\n+\thint=\n+\ttest \"z$scan_merges$scan_reflog_merges$scan_merge_targets\" != \"zttt\" \\\n+\t\t&& hint=\"(maybe try again with -H)\"\n+\tdie \"no candidates for $branch found\" $hint\n+fi\n+\n+echo \"** Candidates for $branch **\"\n+for cmt in $candidates; do\n+\tgit --no-pager log --pretty=oneline --abbrev-commit -1 $cmt\n+done\n+\n+newest=\"$(git rev-list -1 $candidates)\"\n+if test ! -z \"$dry_run\"; then\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n+\tprintf \"** Restoring $new_name to \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\tgit branch $new_name $newest\n+else\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\techo \"** $new_name already exists, doing nothing\"\n+fi\n-- \n1.6.1.447.gbdf1d\n"},{"id":"101690","messageId":"200901231500.23182.bss@iguanasuicide.net","threadId":"17182","inReplyTo":"1232740985-4551-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Boyd Stephen Smith Jr.","fromEmail":"bss@iguanasuicide.net","sentAt":"2009-01-23T21:00:17Z","receivedAt":"2009-01-23T21:00:17Z","isPatch":true,"sender":{"key":"bss@iguanasuicide.net","avatar":"https://gravatar.com/avatar/84b95eeff194b816c1568b1339e63e4b229825298664a9037b9f1ec713ead1e3?d=mp&s=160"},"body":"On Friday 2009 January 23 14:03:05 Thomas Rast wrote:\n>Boyd Stephen Smith Jr. wrote:\n>> I think it could be quite nice; \"undelete\"-type commands are generally\n>> well-received by users and when run against reflogs alone, that's what the\n>> command is.\n>>\n>> It's useful enough to me that I'd love to see it mainlined.\n>\n>So here's a version for contrib with more options and some other\n>tweaks.\n\nI wanted/needed the ability to ignore reflogs entirely.  Use went something \nlike this:\n1. resurrect branch from origin/pu\n2. add patches, mail to list\n3. # wait 24 hours\n4. pull, see from logs that branch was modified, but not just my changes (or \nwithout all of my changes).\n5. delete local branch\n6. Try to resurrect branch from origin/pu, get local version I just deleted.\n7. delete reflog for that branch\n8. Try to resurrect branch from origin/pu, get local version I merged into \nmaster at some point.\n9. Add new option.\n\nSo, I added a couple of options locally: --only-merges, so it would only look \nat the first line of commit logs, ignoring my local reflogs entirely; \nand --revisions, to specify arguments to pass to rev-list so it wouldn't even \nsee my local merges (I passed 'origin/pu origin/next').\n\nYeah, my usage might be abusage, but it worked for me. :)\n\nWould you object to a patch adding a --reflog option and allowing each of the \nscan options to be negated?\n\n>I removed the ability to \"batch resurrect\" with several <name>\n>arguments since that would have conflicted with -b <newname>, but\n>otherwise the features are the same.\n\nIn my local version, which I was going to try and clean up over the weekend, I \nwas going to support both, by borrowing refspec syntax from fetch/push.  \nSpecifically.  Resurrecting 'js/notes' as 'pu/js/notes' would look like:\ngit-resurrect -H js/notes:pu/js/notes\n\nWould you object to a patch that dropped -b in favor of the refspec syntax?\n\n>> In my particular case, it wasn't useful without the -m option, but I\n>> understand why it is not the default.\n>\n>Aside from the obvious speed reasons, I don't really want to teach\n>people that commits \"know\" the branch they were on.  It is a pure\n>coincidence if you can resurrect a topic branch from merge messages;\n>an equivalent merge could have gone through as a fast-forward, and\n>you'd never know.\n\nYeah, agreed.  I made this more clear in my local version by changing the \ndocumentation from \"scan for merges\" to \"scan first line of commit messages \nfor possible merges\".  It's more wordy, but it make it clear that it is \ndependent on the message, and it's not tracked outside of that.\n\nI also tend to merge topic branches with --no-ff so that I do get the merge \nmessage, so it has a better chance of working against my repository.  (I also \nenjoy octopus merging when possible so the history indicates the patch sets \nare separable, but maybe I'm just a little \"touched\" and haven't been bitten \nby by an octopus yet.[1])\n\nNot directly related to any issue you bring up:\n\nThere seems to be some needless redundancy between USAGE and OPTIONS_SPEC.\n\nWould you object to a patch that used $USAGE inside OPTIONS_SPEC?\n-- \nBoyd Stephen Smith Jr.                     ,= ,-_-. =. \nbss@iguanasuicide.net                     ((_/)o o(\\_))\nICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' \nhttp://iguanasuicide.net/                      \\_/     \n\n[1] I hear they are even more feral than penguins.\n"},{"id":"101980","messageId":"200901261254.39360.trast@student.ethz.ch","threadId":"17182","inReplyTo":"200901231500.23182.bss@iguanasuicide.net","subject":"Re: [PATCH] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-26T11:54:17Z","receivedAt":"2009-01-26T11:54:17Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Hi Stephen,\n\nSorry for the long delay.  I'm going to roll a v2 with two small\nfixes.  After that it's all yours ;-)\n\nBoyd Stephen Smith Jr. wrote:\n[...]\n> 6. Try to resurrect branch from origin/pu, get local version I just deleted.\n> 7. delete reflog for that branch\n> 8. Try to resurrect branch from origin/pu, get local version I merged into \n> master at some point.\n> 9. Add new option.\n> \n> So, I added a couple of options locally: --only-merges, so it would only look \n> at the first line of commit logs, ignoring my local reflogs entirely; \n> and --revisions, to specify arguments to pass to rev-list so it wouldn't even \n> see my local merges (I passed 'origin/pu origin/next').\n> \n> Yeah, my usage might be abusage, but it worked for me. :)\n\nI'm fine with adding such an option, but I still wonder what was wrong\nwith the original scheme of asking 'git rev-list -1' for the newest\ncommit.  I thought rev-list always listed by date, so that command\nshould always pick the newest candidate commit from all candidates\nselected.  Do you have an example where that breaks?  Or did you just\nhave a use-case in which you wanted something other than the newest\ncandidate?\n\n> In my local version, which I was going to try and clean up over the weekend, I \n> was going to support both, by borrowing refspec syntax from fetch/push.  \n> Specifically.  Resurrecting 'js/notes' as 'pu/js/notes' would look like:\n> git-resurrect -H js/notes:pu/js/notes\n> \n> Would you object to a patch that dropped -b in favor of the refspec syntax?\n\nNo, that would be fine by me.\n\n> There seems to be some needless redundancy between USAGE and OPTIONS_SPEC.\n> \n> Would you object to a patch that used $USAGE inside OPTIONS_SPEC?\n\nAlso a good idea.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"101981","messageId":"1232973657-31444-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"200901261254.39360.trast@student.ethz.ch","subject":"[PATCH v2] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-26T12:40:57Z","receivedAt":"2009-01-26T12:40:57Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\nthe <branch> in the HEAD reflog and, optionally, all merge commits in\nthe repository.  It can then resurrect the branch, pointing it at the\nmost recent of all candidate commits found.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nFixed the -h to upper-case in the short options summaries, and removed\na stray 'q' in the default assignment of new_name.\n\n\n contrib/git-resurrect.sh |  140 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 140 insertions(+), 0 deletions(-)\n create mode 100755 contrib/git-resurrect.sh\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\nnew file mode 100755\nindex 0000000..3c1c946\n--- /dev/null\n+++ b/contrib/git-resurrect.sh\n@@ -0,0 +1,140 @@\n+#!/bin/sh\n+\n+USAGE=\"[-H] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n+LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n+called <name>, and tries to resurrect it.  Currently, the reflog is\n+searched for checkout messages, and with -r also merge messages.  With\n+-m and -t, the history of all refs is scanned for Merge <name> into\n+other/Merge <other> into <name> (respectively) commit subjects, which\n+is rather slow but allows you to resurrect other people's topic\n+branches.\"\n+\n+OPTIONS_SPEC=\"\\\n+git resurrect [-H] [-r] [-m] [-t] [-n] [-b <newname>] <name>\n+--\n+b,branch=            save branch as <newname> instead of <name>\n+H,try-hard           same as -r -m -t\n+r,reflog-merges      scan for merges recorded in reflog\n+m,merges             scan for merges into other branches (slow)\n+t,merge-targets      scan for merges of other branches into <name>\n+n,dry-run            don't recreate the branch\"\n+\n+. git-sh-setup\n+cd_to_toplevel\n+\n+search_reflog () {\n+        sed -n 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_reflog_merges () {\n+        sed -n 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':~\\1~p' \\\n+                < .git/logs/HEAD\n+}\n+\n+search_merges () {\n+\tgit rev-list --pretty=tformat:\"%h %p:%s\" --all |\n+\tgrep \"Merge branch.*'$branch'.*into\" |\n+\twhile read sha rest; do\n+\t\tparents=\"$(echo \"$rest\" | cut -d: -f1)\"\n+\t\tcase \"$parents\" in\n+\t\t    *' '*' '*)\n+\t\t\twarn \"$branch took part in octopus merge $sha\"\n+\t\t\twarn \"check manually!\"\n+\t\t\t;;\n+\t\t    *' '*)\n+\t\t\techo \"$parents\" | cut -d' ' -f2\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+search_merge_targets () {\n+\tgit rev-list --pretty=tformat:\"%h %s\" --all |\n+\tgrep \"Merge branch '[^']*' into $branch$\" |\n+\tcut -d' ' -f1\n+}\n+\n+dry_run=\n+scan_reflog_merges=\n+scan_merges=\n+scan_merge_targets=\n+new_name=\n+\n+while test \"$#\" != 0; do\n+\tcase \"$1\" in\n+\t    -b|--branch)\n+\t\tshift\n+\t\tnew_name=\"$1\"\n+\t\t;;\n+\t    -n|--dry-run)\n+\t\tdry_run=t\n+\t\t;;\n+\t    -m|--merges)\n+\t\tscan_merges=t\n+\t\t;;\n+\t    -r|--reflog_merges)\n+\t\tscan_reflog_merges=t\n+\t\t;;\n+\t    -t|--merge-targets)\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    -H|--try-hard)\n+\t\tscan_reflog_merges=t\n+\t\tscan_merges=t\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t    *)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+test \"$#\" = 1 || usage\n+\n+branch=\"$1\"\n+test -z \"$new_name\" && new_name=\"$branch\"\n+\n+candidates=\"$(search_reflog $1)\"\n+if test ! -z \"$scan_reflog_merges\"; then\n+\tcandidates=\"$candidates $(search_reflog_merges $1)\"\n+fi\n+if test ! -z \"$scan_merges\"; then\n+\tcandidates=\"$candidates $(search_merges $1)\"\n+fi\n+if test ! -z \"$scan_merge_targets\"; then\n+\tcandidates=\"$candidates $(search_merge_targets $1)\"\n+fi\n+\n+candidates=\"$(git rev-parse $candidates | sort -u)\"\n+\n+if test -z \"$candidates\"; then\n+\thint=\n+\ttest \"z$scan_merges$scan_reflog_merges$scan_merge_targets\" != \"zttt\" \\\n+\t\t&& hint=\"(maybe try again with -H)\"\n+\tdie \"no candidates for $branch found\" $hint\n+fi\n+\n+echo \"** Candidates for $branch **\"\n+for cmt in $candidates; do\n+\tgit --no-pager log --pretty=oneline --abbrev-commit -1 $cmt\n+done\n+\n+newest=\"$(git rev-list -1 $candidates)\"\n+if test ! -z \"$dry_run\"; then\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n+\tprintf \"** Restoring $new_name to \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\tgit branch $new_name $newest\n+else\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\techo \"** $new_name already exists, doing nothing\"\n+fi\n-- \n1.6.1.469.g6f3d5\n"},{"id":"102092","messageId":"7vwschz2dc.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"1232973657-31444-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v2] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-27T06:31:27Z","receivedAt":"2009-01-27T06:31:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\n> the <branch> in the HEAD reflog and, optionally, all merge commits in\n> the repository.  It can then resurrect the branch, pointing it at the\n> most recent of all candidate commits found.\n>\n> Signed-off-by: Thomas Rast <trast@student.ethz.ch>\n> ---\n>\n> Fixed the -h to upper-case in the short options summaries, and removed\n> a stray 'q' in the default assignment of new_name.\n\nI hate to paint bikeshed, but -H \"try-hard\" looks somewhat unusual doesn't\nit?  It sounds more like --all (find from all possible sources).\n\n> +. git-sh-setup\n> +cd_to_toplevel\n\nWhy?\n\n> +search_reflog () {\n> +        sed -n 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n> +                < .git/logs/HEAD\n> +}\n\nOnce you used \". git-sh-setup\", use \"$GIT_DIR/logs/HEAD\".  That way, you\ncan work in a bare repository (and you do not have to cd_to_toplevel,\neither, I think).\n\nOh, don't forget to skip this step if the reflog does not exist.\n\n> +search_reflog_merges () {\n> +        sed -n 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':~\\1~p' \\\n> +                < .git/logs/HEAD\n> +}\n\nThe two commits both point at the HEAD that merges the other branch into,\nso this finds a merge commit that has the tip of target branch as its\nsecond parent.  Is that really what you want?\n\n> +search_merges () {\n> +\tgit rev-list --pretty=tformat:\"%h %p:%s\" --all |\n> +\tgrep \"Merge branch.*'$branch'.*into\" |\n\n\"git merge tr/topic~4\" can say \"Merge branch 'tr/topic' (early part)\".\nAlso merge into 'master' won't have \"into ...\".\n\n> +\twhile read sha rest; do\n> +\t\tparents=\"$(echo \"$rest\" | cut -d: -f1)\"\n> +\t\tcase \"$parents\" in\n> +\t\t    *' '*' '*)\n> +\t\t\twarn \"$branch took part in octopus merge $sha\"\n> +\t\t\twarn \"check manually!\"\n> +\t\t\t;;\n> +\t\t    *' '*)\n> +\t\t\techo \"$parents\" | cut -d' ' -f2\n> +\t\t\t;;\n> +\t\tesac\n> +\tdone\n\nReading everything down to the root commit sounds like fun.  rev-list\ngives you the output from newer to older so you may want to break out once\nyou have found enough candidates.\n\nAnyway, if I were doing this script, I'd write this part like this without\na shell loop:\n\n        _x40=\"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\"\n        _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n\n\tgit rev-list --all --grep=\"Merge branch '$1'\" \\\n        \t--pretty=tformat:\"%H %P %s\" |\n\tsed -ne \"s/^$_x40 $_x40 \\($_x40\\) Merge .*/\\1/p\"\n"},{"id":"102654","messageId":"200901302352.45247.trast@student.ethz.ch","threadId":"17182","inReplyTo":"7vwschz2dc.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-01-30T22:52:42Z","receivedAt":"2009-01-30T22:52:42Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\n> \n> > Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\n\nThanks for your review.  I've been busy and thus out of the loop all\nweek, but I'll try and make an improved version Soon(tm).\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"102782","messageId":"1233524085-25342-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"7vwschz2dc.fsf@gitster.siamese.dyndns.org","subject":"[PATCH v3] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-01T21:34:44Z","receivedAt":"2009-02-01T21:34:44Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\nthe <branch> in the HEAD reflog and, optionally, all merge commits in\nthe repository.  It can then resurrect the branch, pointing it at the\nmost recent of all candidate commits found.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nJunio C Hamano wrote:\n> I hate to paint bikeshed, but -H \"try-hard\" looks somewhat unusual doesn't\n> it?  It sounds more like --all (find from all possible sources).\n\nWhy not.  I had '-h' but then found out the hard way that it's\nreserved...\n\n> > +search_reflog_merges () {\n> > +        sed -n 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':~\\1~p' \\\n> > +                < .git/logs/HEAD\n> > +}\n> \n> The two commits both point at the HEAD that merges the other branch into,\n> so this finds a merge commit that has the tip of target branch as its\n> second parent.  Is that really what you want?\n\nGood point.  Furthermore the sed expression was broken, it would not\nremove the remainder of the line.  Sadly it's not possible to insert\nthe reflog message and sha1 via --pretty=format, so I now use\nrev-parse.\n\n> Reading everything down to the root commit sounds like fun.  rev-list\n> gives you the output from newer to older so you may want to break out once\n> you have found enough candidates.\n> \n> Anyway, if I were doing this script, I'd write this part like this without\n> a shell loop:\n> \n>         _x40=\"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\"\n>         _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n> \n> \tgit rev-list --all --grep=\"Merge branch '$1'\" \\\n>         \t--pretty=tformat:\"%H %P %s\" |\n> \tsed -ne \"s/^$_x40 $_x40 \\($_x40\\) Merge .*/\\1/p\"\n\nNice trick.  The same also works for scan_merge_targets() and gives it\na nice speed boost too.  Unfortunately my sed-fu is not good enough to\nfigure out how to only print the first line (for resurrections from\npu, we expect there to be a single match).  All uses of 'q' I could\ncome up with resulted in an early exit independent of the\nsubstitutions.  Appending '| head -n 1' does not seem to make any\ndifference either.\n\nI also added the relative committer time to the candidate list, and\nmade it sort according to time; it seems somewhat more readable now.\n\n\n contrib/git-resurrect.sh |  172 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 172 insertions(+), 0 deletions(-)\n create mode 100755 contrib/git-resurrect.sh\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\nnew file mode 100755\nindex 0000000..3a040ae\n--- /dev/null\n+++ b/contrib/git-resurrect.sh\n@@ -0,0 +1,172 @@\n+#!/bin/sh\n+\n+USAGE=\"[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n+LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n+called <name>, and tries to resurrect it.  Currently, the reflog is\n+searched for checkout messages, and with -r also merge messages.  With\n+-m and -t, the history of all refs is scanned for Merge <name> into\n+other/Merge <other> into <name> (respectively) commit subjects, which\n+is rather slow but allows you to resurrect other people's topic\n+branches.\"\n+\n+OPTIONS_SPEC=\"\\\n+git resurrect $USAGE\n+--\n+b,branch=            save branch as <newname> instead of <name>\n+a,all                same as -l -r -m -t\n+l,reflog             scan reflog for checkouts (enabled by default)\n+r,reflog-merges      scan for merges recorded in reflog\n+m,merges             scan for merges into other branches (slow)\n+t,merge-targets      scan for merges of other branches into <name>\n+n,dry-run            don't recreate the branch\"\n+\n+. git-sh-setup\n+\n+search_reflog () {\n+        sed -ne 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n+                < \"$GIT_DIR\"/logs/HEAD\n+}\n+\n+search_reflog_merges () {\n+\tgit rev-parse $(\n+\t\tsed -ne 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':.*~\\1^2~p' \\\n+\t\t\t< \"$GIT_DIR\"/logs/HEAD\n+\t)\n+}\n+\n+_x40=\"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\"\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+\n+search_merges () {\n+        git rev-list --all --grep=\"Merge branch '$1'\" \\\n+                --pretty=tformat:\"%P %s\" |\n+        sed -ne \"s/^$_x40 \\($_x40\\) Merge .*/\\1/p\"\n+}\n+\n+search_merge_targets () {\n+\tgit rev-list --all --grep=\"Merge branch '[^']*' into $branch\\$\" \\\n+\t\t--pretty=tformat:\"%H %s\" --all |\n+\tsed -ne \"s/^\\($_x40\\) Merge .*/\\1/p\"\n+}\n+\n+dry_run=\n+scan_reflog=t\n+scan_reflog_merges=\n+scan_merges=\n+scan_merge_targets=\n+new_name=\n+\n+while test \"$#\" != 0; do\n+\tcase \"$1\" in\n+\t    -b|--branch)\n+\t\tshift\n+\t\tnew_name=\"$1\"\n+\t\t;;\n+\t    -n|--dry-run)\n+\t\tdry_run=t\n+\t\t;;\n+\t    --no-dry-run)\n+\t\tdry_run=\n+\t\t;;\n+\t    -m|--merges)\n+\t\tscan_merges=t\n+\t\t;;\n+\t    --no-merges)\n+\t\tscan_merges=\n+\t\t;;\n+\t    -l|--reflog)\n+\t\tscan_reflog=t\n+\t\t;;\n+\t    --no-reflog)\n+\t\tscan_reflog=\n+\t\t;;\n+\t    -r|--reflog_merges)\n+\t\tscan_reflog_merges=t\n+\t\t;;\n+\t    --no-reflog_merges)\n+\t\tscan_reflog_merges=\n+\t\t;;\n+\t    -t|--merge-targets)\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --no-merge-targets)\n+\t\tscan_merge_targets=\n+\t\t;;\n+\t    -a|--all)\n+\t\tscan_reflog=t\n+\t\tscan_reflog_merges=t\n+\t\tscan_merges=t\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t    *)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+test \"$#\" = 1 || usage\n+\n+all_strategies=\"$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets\"\n+if test -z \"$all_strategies\"; then\n+\tdie \"must enable at least one of -lrmt\"\n+fi\n+\n+branch=\"$1\"\n+test -z \"$new_name\" && new_name=\"$branch\"\n+\n+if test ! -z \"$scan_reflog\"; then\n+\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n+\t\tcandidates=\"$(search_reflog $branch)\"\n+\telse\n+\t\tdie 'reflog scanning requested, but' \\\n+\t\t\t'$GIT_DIR/logs/HEAD not readable'\n+\tfi\n+fi\n+if test ! -z \"$scan_reflog_merges\"; then\n+\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n+\t\tcandidates=\"$candidates $(search_reflog_merges $branch)\"\n+\telse\n+\t\tdie 'reflog scanning requested, but' \\\n+\t\t\t'$GIT_DIR/logs/HEAD not readable'\n+\tfi\n+fi\n+if test ! -z \"$scan_merges\"; then\n+\tcandidates=\"$candidates $(search_merges $branch)\"\n+fi\n+if test ! -z \"$scan_merge_targets\"; then\n+\tcandidates=\"$candidates $(search_merge_targets $branch)\"\n+fi\n+\n+candidates=\"$(git rev-parse $candidates | sort -u)\"\n+\n+if test -z \"$candidates\"; then\n+\thint=\n+\ttest \"z$all_strategies\" != \"ztttt\" \\\n+\t\t&& hint=\" (maybe try again with -a)\"\n+\tdie \"no candidates for $branch found$hint\"\n+fi\n+\n+echo \"** Candidates for $branch **\"\n+for cmt in $candidates; do\n+\tgit --no-pager log --pretty=tformat:\"%ct:%h [%cr] %s\" --abbrev-commit -1 $cmt\n+done \\\n+| sort -n | cut -d: -f2-\n+\n+newest=\"$(git rev-list -1 $candidates)\"\n+if test ! -z \"$dry_run\"; then\n+\tprintf \"** Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n+\tprintf \"** Restoring $new_name to \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\tgit branch $new_name $newest\n+else\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\techo \"** $new_name already exists, doing nothing\"\n+fi\n-- \n1.6.1.2.495.gb8db2\n"},{"id":"102807","messageId":"7vljsppo14.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"1233524085-25342-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v3] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-02T02:31:51Z","receivedAt":"2009-02-02T02:31:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n>> Reading everything down to the root commit sounds like fun.  rev-list\n>> gives you the output from newer to older so you may want to break out once\n>> you have found enough candidates.\n>> \n>> Anyway, if I were doing this script, I'd write this part like this without\n>> a shell loop:\n>> \n>>         _x40=\"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\"\n>>         _x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n>> \n>> \tgit rev-list --all --grep=\"Merge branch '$1'\" \\\n>>         \t--pretty=tformat:\"%H %P %s\" |\n>> \tsed -ne \"s/^$_x40 $_x40 \\($_x40\\) Merge .*/\\1/p\"\n>\n> Nice trick.  The same also works for scan_merge_targets() and gives it\n> a nice speed boost too.  Unfortunately my sed-fu is not good enough to\n> figure out how to only print the first line (for resurrections from\n> pu, we expect there to be a single match).\n\nDo you mean something like this?\n\n\tsed -n -e \"/^$_x40 $_x40 \\($_x40\\) Merge .*/ {\n\t\ts//\\1/p\n                q\n        }\"\n"},{"id":"103102","messageId":"1233741858-13099-1-git-send-email-trast@student.ethz.ch","threadId":"17182","inReplyTo":"7vljsppo14.fsf@gitster.siamese.dyndns.org","subject":"[PATCH v4] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-04T10:04:18Z","receivedAt":"2009-02-04T10:04:18Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a tool 'git-resurrect.sh <branch>' that tries to find traces of\nthe <branch> in the HEAD reflog and, optionally, all merge commits in\nthe repository.  It can then resurrect the branch, pointing it at the\nmost recent of all candidate commits found.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nJunio C Hamano wrote:\n> Do you mean something like this?\n> \n> \tsed -n -e \"/^$_x40 $_x40 \\($_x40\\) Merge .*/ {\n> \t\ts//\\1/p\n>                 q\n>         }\"\n\nYep, precisely.  Thanks!  This indeed gives it a nice speed boost if\nall you want is a topic \"resurrection\" from pu.\n\n\n\n contrib/git-resurrect.sh |  180 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 180 insertions(+), 0 deletions(-)\n create mode 100755 contrib/git-resurrect.sh\n\ndiff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh\nnew file mode 100755\nindex 0000000..c364dda\n--- /dev/null\n+++ b/contrib/git-resurrect.sh\n@@ -0,0 +1,180 @@\n+#!/bin/sh\n+\n+USAGE=\"[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>\"\n+LONG_USAGE=\"git-resurrect attempts to find traces of a branch tip\n+called <name>, and tries to resurrect it.  Currently, the reflog is\n+searched for checkout messages, and with -r also merge messages.  With\n+-m and -t, the history of all refs is scanned for Merge <name> into\n+other/Merge <other> into <name> (respectively) commit subjects, which\n+is rather slow but allows you to resurrect other people's topic\n+branches.\"\n+\n+OPTIONS_SPEC=\"\\\n+git resurrect $USAGE\n+--\n+b,branch=            save branch as <newname> instead of <name>\n+a,all                same as -l -r -m -t\n+k,keep-going         full rev-list scan (instead of first match)\n+l,reflog             scan reflog for checkouts (enabled by default)\n+r,reflog-merges      scan for merges recorded in reflog\n+m,merges             scan for merges into other branches (slow)\n+t,merge-targets      scan for merges of other branches into <name>\n+n,dry-run            don't recreate the branch\"\n+\n+. git-sh-setup\n+\n+search_reflog () {\n+        sed -ne 's~^\\([^ ]*\\) .*\\tcheckout: moving from '\"$1\"' .*~\\1~p' \\\n+                < \"$GIT_DIR\"/logs/HEAD\n+}\n+\n+search_reflog_merges () {\n+\tgit rev-parse $(\n+\t\tsed -ne 's~^[^ ]* \\([^ ]*\\) .*\\tmerge '\"$1\"':.*~\\1^2~p' \\\n+\t\t\t< \"$GIT_DIR\"/logs/HEAD\n+\t)\n+}\n+\n+_x40=\"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\"\n+_x40=\"$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40\"\n+\n+search_merges () {\n+        git rev-list --all --grep=\"Merge branch '$1'\" \\\n+                --pretty=tformat:\"%P %s\" |\n+        sed -ne \"/^$_x40 \\($_x40\\) Merge .*/ {s//\\1/p;$early_exit}\"\n+}\n+\n+search_merge_targets () {\n+\tgit rev-list --all --grep=\"Merge branch '[^']*' into $branch\\$\" \\\n+\t\t--pretty=tformat:\"%H %s\" --all |\n+\tsed -ne \"/^\\($_x40\\) Merge .*/ {s//\\1/p;$early_exit} \"\n+}\n+\n+dry_run=\n+early_exit=q\n+scan_reflog=t\n+scan_reflog_merges=\n+scan_merges=\n+scan_merge_targets=\n+new_name=\n+\n+while test \"$#\" != 0; do\n+\tcase \"$1\" in\n+\t    -b|--branch)\n+\t\tshift\n+\t\tnew_name=\"$1\"\n+\t\t;;\n+\t    -n|--dry-run)\n+\t\tdry_run=t\n+\t\t;;\n+\t    --no-dry-run)\n+\t\tdry_run=\n+\t\t;;\n+\t    -k|--keep-going)\n+\t\tearly_exit=\n+\t\t;;\n+\t    --no-keep-going)\n+\t\tearly_exit=q\n+\t\t;;\n+\t    -m|--merges)\n+\t\tscan_merges=t\n+\t\t;;\n+\t    --no-merges)\n+\t\tscan_merges=\n+\t\t;;\n+\t    -l|--reflog)\n+\t\tscan_reflog=t\n+\t\t;;\n+\t    --no-reflog)\n+\t\tscan_reflog=\n+\t\t;;\n+\t    -r|--reflog_merges)\n+\t\tscan_reflog_merges=t\n+\t\t;;\n+\t    --no-reflog_merges)\n+\t\tscan_reflog_merges=\n+\t\t;;\n+\t    -t|--merge-targets)\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --no-merge-targets)\n+\t\tscan_merge_targets=\n+\t\t;;\n+\t    -a|--all)\n+\t\tscan_reflog=t\n+\t\tscan_reflog_merges=t\n+\t\tscan_merges=t\n+\t\tscan_merge_targets=t\n+\t\t;;\n+\t    --)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t    *)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+test \"$#\" = 1 || usage\n+\n+all_strategies=\"$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets\"\n+if test -z \"$all_strategies\"; then\n+\tdie \"must enable at least one of -lrmt\"\n+fi\n+\n+branch=\"$1\"\n+test -z \"$new_name\" && new_name=\"$branch\"\n+\n+if test ! -z \"$scan_reflog\"; then\n+\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n+\t\tcandidates=\"$(search_reflog $branch)\"\n+\telse\n+\t\tdie 'reflog scanning requested, but' \\\n+\t\t\t'$GIT_DIR/logs/HEAD not readable'\n+\tfi\n+fi\n+if test ! -z \"$scan_reflog_merges\"; then\n+\tif test -r \"$GIT_DIR\"/logs/HEAD; then\n+\t\tcandidates=\"$candidates $(search_reflog_merges $branch)\"\n+\telse\n+\t\tdie 'reflog scanning requested, but' \\\n+\t\t\t'$GIT_DIR/logs/HEAD not readable'\n+\tfi\n+fi\n+if test ! -z \"$scan_merges\"; then\n+\tcandidates=\"$candidates $(search_merges $branch)\"\n+fi\n+if test ! -z \"$scan_merge_targets\"; then\n+\tcandidates=\"$candidates $(search_merge_targets $branch)\"\n+fi\n+\n+candidates=\"$(git rev-parse $candidates | sort -u)\"\n+\n+if test -z \"$candidates\"; then\n+\thint=\n+\ttest \"z$all_strategies\" != \"ztttt\" \\\n+\t\t&& hint=\" (maybe try again with -a)\"\n+\tdie \"no candidates for $branch found$hint\"\n+fi\n+\n+echo \"** Candidates for $branch **\"\n+for cmt in $candidates; do\n+\tgit --no-pager log --pretty=tformat:\"%ct:%h [%cr] %s\" --abbrev-commit -1 $cmt\n+done \\\n+| sort -n | cut -d: -f2-\n+\n+newest=\"$(git rev-list -1 $candidates)\"\n+if test ! -z \"$dry_run\"; then\n+\tprintf \"** Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then\n+\tprintf \"** Restoring $new_name to \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\tgit branch $new_name $newest\n+else\n+\tprintf \"Most recent: \"\n+\tgit --no-pager log -1 --pretty=tformat:\"%h %s\" $newest\n+\techo \"** $new_name already exists, doing nothing\"\n+fi\n-- \n1.6.1.2.530.gdaa1c\n"},{"id":"103309","messageId":"7vtz79ffd6.fsf@gitster.siamese.dyndns.org","threadId":"17182","inReplyTo":"1233741858-13099-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v4] contrib git-resurrect: find traces of a branch name and resurrect it","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-05T08:38:13Z","receivedAt":"2009-02-05T08:38:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> Junio C Hamano wrote:\n>> Do you mean something like this?\n>> \n>> \tsed -n -e \"/^$_x40 $_x40 \\($_x40\\) Merge .*/ {\n>> \t\ts//\\1/p\n>>                 q\n>>         }\"\n>\n> Yep, precisely.  Thanks!  This indeed gives it a nice speed boost if\n> all you want is a topic \"resurrection\" from pu.\n> ...\n> +search_merges () {\n> +        git rev-list --all --grep=\"Merge branch '$1'\" \\\n> +                --pretty=tformat:\"%P %s\" |\n> +        sed -ne \"/^$_x40 \\($_x40\\) Merge .*/ {s//\\1/p;$early_exit}\"\n> +}\n\nWill apply, but just to let you know, I wrote my example on separate lines\n(and with separate -n and -e options for that matter) for a reason.  I\nrecall some implementation of sed (perhaps older BSDs, but don't quote me\non that) did not understanding semicolon with close brace on the same\nline.  It may not be a problem in practice these days, but I do not have\naccess to many different platforms to check as I used to.\n"}]}