{"thread":{"id":"33057","subject":"[PATCH 0/5] nd/branch-show-rebase-bisect-state updates","startedAt":"2013-03-03T09:41:16Z","lastAt":"2013-03-24T05:30:30Z","messageCount":38,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Duy Nguyen","Matthieu Moy"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"210523","messageId":"1362303681-6585-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":null,"subject":"[PATCH 0/5] nd/branch-show-rebase-bisect-state updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:16Z","receivedAt":"2013-03-03T09:41:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This round addresses the code sharing issue in the previous round and\nshows a bit more info in both git-status and git-branch. Now when you\ndo \"git checkout v1.8.0\" or \"git checkout origin/master\", you should\nsee that ref in status/branch output, in addition to detached HEAD\nstatus.\n\nNguyễn Thái Ngọc Duy (5):\n  checkout: record full target ref in reflog\n  wt-status: split wt_status_state parsing function out\n  wt-status: move wt_status_get_state() out to wt_status_print()\n  status: show the ref that is checked out, even if it's detached\n  branch: show more information when HEAD is detached\n\n builtin/branch.c            |  25 +++++++-\n builtin/checkout.c          |  19 +++++-\n t/t6030-bisect-porcelain.sh |   2 +-\n wt-status.c                 | 139 ++++++++++++++++++++++++++++++++++----------\n wt-status.h                 |   6 +-\n 5 files changed, 152 insertions(+), 39 deletions(-)\n\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210526","messageId":"1362303681-6585-2-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/5] checkout: record full target ref in reflog","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:17Z","receivedAt":"2013-03-03T09:41:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This simplifies parsing later on because the parser does not need to\ndo dwim on the target (and later dwim may be ambiguous if new refs are\nadded).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/checkout.c | 19 ++++++++++++++++---\n 1 file changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a9c1b5a..b11bd31 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -398,6 +398,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,\n \n struct branch_info {\n \tconst char *name; /* The short name used */\n+\tconst char *full_ref; /* The full name of a real ref */\n \tconst char *path; /* The full name of a real branch */\n \tstruct commit *commit; /* The named commit */\n };\n@@ -589,7 +590,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,\n \tif (!old_desc && old->commit)\n \t\told_desc = sha1_to_hex(old->commit->object.sha1);\n \tstrbuf_addf(&msg, \"checkout: moving from %s to %s\",\n-\t\t    old_desc ? old_desc : \"(invalid)\", new->name);\n+\t\t    old_desc ? old_desc : \"(invalid)\",\n+\t\t    new->full_ref ? new->full_ref : new->name);\n \n \tif (!strcmp(new->name, \"HEAD\") && !new->path && !opts->force_detach) {\n \t\t/* Nothing to do. */\n@@ -907,10 +909,21 @@ static int parse_branchname_arg(int argc, const char **argv,\n \tsetup_branch_path(new);\n \n \tif (!check_refname_format(new->path, 0) &&\n-\t    !read_ref(new->path, branch_rev))\n+\t    !read_ref(new->path, branch_rev)) {\n \t\thashcpy(rev, branch_rev);\n-\telse\n+\t\tnew->full_ref = xstrdup(new->path);\n+\t} else {\n+\t\tchar *real_ref = NULL;\n+\t\tunsigned char sha1[20];\n \t\tnew->path = NULL; /* not an existing branch */\n+\t\tif (dwim_ref(new->name, strlen(new->name), sha1,\n+\t\t\t     &real_ref) == 1 &&\n+\t\t    !hashcmp(sha1, rev)) {\n+\t\t\tnew->full_ref = real_ref;\n+\t\t\treal_ref = NULL;\n+\t\t}\n+\t\tfree(real_ref);\n+\t}\n \n \tnew->commit = lookup_commit_reference_gently(rev, 1);\n \tif (!new->commit) {\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210525","messageId":"1362303681-6585-3-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/5] wt-status: split wt_status_state parsing function out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:18Z","receivedAt":"2013-03-03T09:41:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 52 +++++++++++++++++++++++++++++++---------------------\n wt-status.h |  5 +++--\n 2 files changed, 34 insertions(+), 23 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex ef405d0..183aafe 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -970,7 +970,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n  * Extract branch information from rebase/bisect\n  */\n static void read_and_strip_branch(struct strbuf *sb,\n-\t\t\t\t  const char **branch,\n+\t\t\t\t  char **branch,\n \t\t\t\t  const char *path)\n {\n \tunsigned char sha1[20];\n@@ -994,52 +994,62 @@ static void read_and_strip_branch(struct strbuf *sb,\n \t\tstrbuf_addstr(sb, abbrev);\n \t\t*branch = sb->buf;\n \t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n-\t\t;\n+\t\t*branch = NULL;\n \telse\t\t\t/* bisect */\n \t\t*branch = sb->buf;\n+\tif (*branch)\n+\t\t*branch = xstrdup(*branch);\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+void wt_status_get_state(struct wt_status_state *state)\n {\n-\tconst char *state_color = color(WT_STATUS_HEADER, s);\n \tstruct strbuf branch = STRBUF_INIT;\n \tstruct strbuf onto = STRBUF_INIT;\n-\tstruct wt_status_state state;\n \tstruct stat st;\n \n-\tmemset(&state, 0, sizeof(state));\n+\tmemset(state, 0, sizeof(*state));\n \n \tif (!stat(git_path(\"MERGE_HEAD\"), &st)) {\n-\t\tstate.merge_in_progress = 1;\n+\t\tstate->merge_in_progress = 1;\n \t} else if (!stat(git_path(\"rebase-apply\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-apply/applying\"), &st)) {\n-\t\t\tstate.am_in_progress = 1;\n+\t\t\tstate->am_in_progress = 1;\n \t\t\tif (!stat(git_path(\"rebase-apply/patch\"), &st) && !st.st_size)\n-\t\t\t\tstate.am_empty_patch = 1;\n+\t\t\t\tstate->am_empty_patch = 1;\n \t\t} else {\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t\t      \"rebase-apply/head-name\");\n-\t\t\tread_and_strip_branch(&onto, &state.onto,\n+\t\t\tread_and_strip_branch(&onto, &state->onto,\n \t\t\t\t\t      \"rebase-apply/onto\");\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n-\t\t\tstate.rebase_interactive_in_progress = 1;\n+\t\t\tstate->rebase_interactive_in_progress = 1;\n \t\telse\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"rebase-merge/head-name\");\n-\t\tread_and_strip_branch(&onto, &state.onto,\n+\t\tread_and_strip_branch(&onto, &state->onto,\n \t\t\t\t      \"rebase-merge/onto\");\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n-\t\tstate.cherry_pick_in_progress = 1;\n+\t\tstate->cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n-\t\tstate.bisect_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\tstate->bisect_in_progress = 1;\n+\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"BISECT_START\");\n \t}\n+\tstrbuf_release(&branch);\n+\tstrbuf_release(&onto);\n+}\n+\n+static void wt_status_print_state(struct wt_status *s)\n+{\n+\tconst char *state_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(&state);\n \n \tif (state.merge_in_progress)\n \t\tshow_merge_in_progress(s, &state, state_color);\n@@ -1051,8 +1061,8 @@ static void wt_status_print_state(struct wt_status *s)\n \t\tshow_cherry_pick_in_progress(s, &state, state_color);\n \tif (state.bisect_in_progress)\n \t\tshow_bisect_in_progress(s, &state, state_color);\n-\tstrbuf_release(&branch);\n-\tstrbuf_release(&onto);\n+\tfree(state.branch);\n+\tfree(state.onto);\n }\n \n void wt_status_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 81e1dcf..5ddcbf6 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -79,13 +79,14 @@ struct wt_status_state {\n \tint rebase_interactive_in_progress;\n \tint cherry_pick_in_progress;\n \tint bisect_in_progress;\n-\tconst char *branch;\n-\tconst char *onto;\n+\tchar *branch;\n+\tchar *onto;\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n+void wt_status_get_state(struct wt_status_state *state);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210524","messageId":"1362303681-6585-4-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/5] wt-status: move wt_status_get_state() out to wt_status_print()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:19Z","receivedAt":"2013-03-03T09:41:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 37 +++++++++++++++++++------------------\n 1 file changed, 19 insertions(+), 18 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 183aafe..6a3566b 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1044,31 +1044,29 @@ void wt_status_get_state(struct wt_status_state *state)\n \tstrbuf_release(&onto);\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+static void wt_status_print_state(struct wt_status *s,\n+\t\t\t\t  struct wt_status_state *state)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct wt_status_state state;\n-\n-\twt_status_get_state(&state);\n-\n-\tif (state.merge_in_progress)\n-\t\tshow_merge_in_progress(s, &state, state_color);\n-\telse if (state.am_in_progress)\n-\t\tshow_am_in_progress(s, &state, state_color);\n-\telse if (state.rebase_in_progress || state.rebase_interactive_in_progress)\n-\t\tshow_rebase_in_progress(s, &state, state_color);\n-\telse if (state.cherry_pick_in_progress)\n-\t\tshow_cherry_pick_in_progress(s, &state, state_color);\n-\tif (state.bisect_in_progress)\n-\t\tshow_bisect_in_progress(s, &state, state_color);\n-\tfree(state.branch);\n-\tfree(state.onto);\n+\tif (state->merge_in_progress)\n+\t\tshow_merge_in_progress(s, state, state_color);\n+\telse if (state->am_in_progress)\n+\t\tshow_am_in_progress(s, state, state_color);\n+\telse if (state->rebase_in_progress || state->rebase_interactive_in_progress)\n+\t\tshow_rebase_in_progress(s, state, state_color);\n+\telse if (state->cherry_pick_in_progress)\n+\t\tshow_cherry_pick_in_progress(s, state, state_color);\n+\tif (state->bisect_in_progress)\n+\t\tshow_bisect_in_progress(s, state, state_color);\n }\n \n void wt_status_print(struct wt_status *s)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(&state);\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1087,7 +1085,10 @@ void wt_status_print(struct wt_status *s)\n \t\t\twt_status_print_tracking(s);\n \t}\n \n-\twt_status_print_state(s);\n+\twt_status_print_state(s, &state);\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), _(\"Initial commit\"));\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210527","messageId":"1362303681-6585-5-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:20Z","receivedAt":"2013-03-03T09:41:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When a remote ref or a tag is checked out, HEAD is automatically\ndetached. There is no user friendly way to find out what ref is\nchecked out in this case. This patch digs in reflog for this\ninformation and shows \"Detached from origin/master\" or \"Detached from\nv1.8.0\" instead of \"Currently not on any branch\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n wt-status.h |  3 ++-\n 2 files changed, 70 insertions(+), 5 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 6a3566b..4b6421a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1001,7 +1001,60 @@ static void read_and_strip_branch(struct strbuf *sb,\n \t\t*branch = xstrdup(*branch);\n }\n \n-void wt_status_get_state(struct wt_status_state *state)\n+struct grab_1st_switch_cbdata {\n+\tstruct strbuf buf;\n+\tunsigned char sha1[20];\n+};\n+\n+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t   const char *email, unsigned long timestamp, int tz,\n+\t\t\t   const char *message, void *cb_data)\n+{\n+\tstruct grab_1st_switch_cbdata *cb = cb_data;\n+\tconst char *target = NULL;\n+\n+\tif (prefixcmp(message, \"checkout: moving from \"))\n+\t\treturn 0;\n+\tmessage += strlen(\"checkout: moving from \");\n+\ttarget = strstr(message, \" to \");\n+\tif (!target)\n+\t\treturn 0;\n+\ttarget += strlen(\" to \");\n+\tstrbuf_reset(&cb->buf);\n+\thashcpy(cb->sha1, nsha1);\n+\tif (!prefixcmp(target, \"refs/\")) {\n+\t\tconst char *end = target;\n+\t\twhile (*end && *end != '\\n')\n+\t\t\tend++;\n+\t\tstrbuf_add(&cb->buf, target, end - target);\n+\t}\n+\treturn 0;\n+}\n+\n+static void wt_status_get_detached_from(struct wt_status_state *state)\n+{\n+\tstruct grab_1st_switch_cbdata cb;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\n+\tstrbuf_init(&cb.buf, 0);\n+\tfor_each_recent_reflog_ent(\"HEAD\", grab_1st_switch, 40960, &cb);\n+\tif (cb.buf.len &&\n+\t    !read_ref(cb.buf.buf, sha1) &&\n+\t    (commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n+\t    !hashcmp(cb.sha1, commit->object.sha1)) {\n+\t\tint ofs = 0;\n+\t\tif (!prefixcmp(cb.buf.buf, \"refs/tags/\"))\n+\t\t\tofs = strlen(\"refs/tags/\");\n+\t\telse if (!prefixcmp(cb.buf.buf, \"refs/remotes/\"))\n+\t\t\tofs = strlen(\"refs/remotes/\");\n+\t\tstate->detached_from = xstrdup(cb.buf.buf + ofs);\n+\t}\n+\tstrbuf_release(&cb.buf);\n+}\n+\n+void wt_status_get_state(struct wt_status_state *state,\n+\t\t\t int get_detached_from)\n {\n \tstruct strbuf branch = STRBUF_INIT;\n \tstruct strbuf onto = STRBUF_INIT;\n@@ -1040,6 +1093,10 @@ void wt_status_get_state(struct wt_status_state *state)\n \t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"BISECT_START\");\n \t}\n+\n+\tif (get_detached_from)\n+\t\twt_status_get_detached_from(state);\n+\n \tstrbuf_release(&branch);\n \tstrbuf_release(&onto);\n }\n@@ -1066,7 +1123,8 @@ void wt_status_print(struct wt_status *s)\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n \tstruct wt_status_state state;\n \n-\twt_status_get_state(&state);\n+\twt_status_get_state(&state,\n+\t\t\t    s->branch && !strcmp(s->branch, \"HEAD\"));\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1074,9 +1132,14 @@ void wt_status_print(struct wt_status *s)\n \t\tif (!prefixcmp(branch_name, \"refs/heads/\"))\n \t\t\tbranch_name += 11;\n \t\telse if (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_name = \"\";\n \t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\tif (state.detached_from) {\n+\t\t\t\tbranch_name = state.detached_from;\n+\t\t\t\ton_what = _(\"Detached from \");\n+\t\t\t} else {\n+\t\t\t\tbranch_name = \"\";\n+\t\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\t}\n \t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n@@ -1088,6 +1151,7 @@ void wt_status_print(struct wt_status *s)\n \twt_status_print_state(s, &state);\n \tfree(state.branch);\n \tfree(state.onto);\n+\tfree(state.detached_from);\n \n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 5ddcbf6..74c9055 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -81,12 +81,13 @@ struct wt_status_state {\n \tint bisect_in_progress;\n \tchar *branch;\n \tchar *onto;\n+\tchar *detached_from;\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n-void wt_status_get_state(struct wt_status_state *state);\n+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210528","messageId":"1362303681-6585-6-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/5] branch: show more information when HEAD is detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-03T09:41:21Z","receivedAt":"2013-03-03T09:41:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This prints more helpful info when HEAD is detached: is it detached\nbecause of bisect or rebase? What is the original branch name in those\ncases? Is it detached because the user checks out a remote ref or a\ntag (and which one)?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/branch.c            | 25 ++++++++++++++++++++++++-\n t/t6030-bisect-porcelain.sh |  2 +-\n 2 files changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 6371bf9..02dee0d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -18,6 +18,7 @@\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"utf8.h\"\n+#include \"wt-status.h\"\n \n static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [options] [-r | -a] [--merged | --no-merged]\"),\n@@ -550,6 +551,28 @@ static int calc_maxwidth(struct ref_list *refs)\n \treturn w;\n }\n \n+static char *get_head_description()\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from)\n+\t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\n+\t\t\t    state.detached_from);\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n \n static void show_detached(struct ref_list *ref_list)\n {\n@@ -557,7 +580,7 @@ static void show_detached(struct ref_list *ref_list)\n \n \tif (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {\n \t\tstruct ref_item item;\n-\t\titem.name = xstrdup(_(\"(no branch)\"));\n+\t\titem.name = get_head_description();\n \t\titem.width = utf8_strwidth(item.name);\n \t\titem.kind = REF_LOCAL_BRANCH;\n \t\titem.dest = NULL;\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 3e0e15f..9b6f0d0 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n \tcp .git/BISECT_START saved &&\n \ttest_must_fail git bisect start $HASH4 foo -- &&\n \tgit branch > branch.output &&\n-\ttest_i18ngrep \"* (no branch)\" branch.output > /dev/null &&\n+\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n \ttest_cmp saved .git/BISECT_START\n '\n test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210543","messageId":"7vy5e49l3i.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1362303681-6585-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-03T22:25:53Z","receivedAt":"2013-03-03T22:25:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> When a remote ref or a tag is checked out, HEAD is automatically\n> detached. There is no user friendly way to find out what ref is\n> checked out in this case. This patch digs in reflog for this\n> information and shows \"Detached from origin/master\" or \"Detached from\n> v1.8.0\" instead of \"Currently not on any branch\".\n\n\"Detached from\" is a nice attempt to compromise in the phrasing.\n\nWe usually say you detach HEAD at v1.8.0, but what is shown is what\nstarted from such a state but then the user may have built more\nhistory on top of it and may no longer be at v1.8.0, so obviously we\ndo not want to say \"Detached at\".  We are in a \"detached at v1.8.0\nand then possibly built one or more commits on top\" state (would it\nbe helpful to differentiate the \"nothing built on top\" and \"some\ncommits have been built on top\" cases, I wonder).\n\nAlso I wonder if you could do a bit more to help the users who do:\n\n    $ git checkout $(git merge-base HEAD nd/branch-show-rebase-bisect-state)\n\naka\n\n    $ git checkout ...nd/branch-show-rebase-bisect-state\n\nand then do one or more commits on top.\n\nInstead of punting to \"Currently not on any branch\", would it help\nto show the place you first detached at, so that the user can then\ngrab that commit object name and run\n\n    $ git log --oneline $that_commit..\n\nor something?\n\n> +static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n> +\t\t\t   const char *email, unsigned long timestamp, int tz,\n> +\t\t\t   const char *message, void *cb_data)\n> +{\n> +\tstruct grab_1st_switch_cbdata *cb = cb_data;\n> +\tconst char *target = NULL;\n> +\n> +\tif (prefixcmp(message, \"checkout: moving from \"))\n> +\t\treturn 0;\n> +\tmessage += strlen(\"checkout: moving from \");\n> +\ttarget = strstr(message, \" to \");\n> +\tif (!target)\n> +\t\treturn 0;\n> +\ttarget += strlen(\" to \");\n> +\tstrbuf_reset(&cb->buf);\n> +\thashcpy(cb->sha1, nsha1);\n> +\tif (!prefixcmp(target, \"refs/\")) {\n> +\t\tconst char *end = target;\n> +\t\twhile (*end && *end != '\\n')\n> +\t\t\tend++;\n> +\t\tstrbuf_add(&cb->buf, target, end - target);\n> +\t}\n> +\treturn 0;\n> +}\n\nCan't this be done by generalizing grab_nth_branch_switch() and then\nexposing it as part of the general API?  \n\nI also feel uneasy about two issues this and the previous change\nintroduces:\n\n 1) It is somewhat unnerving that this step reads what comes after\n    \"to\", while nth_branch_switch() reads what comes between \"from\"\n    and \"to\", but it probably cannot be avoided because this series\n    wants to know what we switched \"to\" earlier, while \"checkout -\"\n    wants to know what we switched \"from\"\n\n 2) The previous one records this sequence in a funny way:\n\n        : start from branch A\n        git checkout B\n        git checkout C\n\n    The resulting reflog entries result in\n\n        checkout: moving from A to refs/heads/B\n        checkout: moving from B to refs/heads/C\n\n    even though existing code and tools are expecting to read\n\n        checkout: moving from A to B\n        checkout: moving from B to C\n\nBy the way, even though the title of this patch is \"status: show the\nref that is checked out, even if it's detached\", a quick check with\n\n        $ cd ../linux-3.0\n        $ git describe\n        v3.8-rc7\n        $ ../git.git/git-checkout v3.8-rc7\n        $ tail -n 1 .git/logs/HEAD | sed -e 's/.*checkout/checkout/'\n        checkout: moving from master to refs/tags/v3.8-rc7\n        $ ../git.git/git-status | head -n 1\n        # Not currently on any branch.\n        $ ../git.git/git-branch -v\n        * (no branch) 836dc9e Linux 3.8-rc7\n          master      836dc9e Linux 3.8-rc7\n\ndoes not seem to give me anything more helpful.\n"},{"id":"210544","messageId":"7vtxos9kzr.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1362303681-6585-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 5/5] branch: show more information when HEAD is detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-03T22:28:08Z","receivedAt":"2013-03-03T22:28:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This prints more helpful info when HEAD is detached: is it detached\n> because of bisect or rebase? What is the original branch name in those\n> cases? Is it detached because the user checks out a remote ref or a\n> tag (and which one)?\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  builtin/branch.c            | 25 ++++++++++++++++++++++++-\n>  t/t6030-bisect-porcelain.sh |  2 +-\n>  2 files changed, 25 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index 6371bf9..02dee0d 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -18,6 +18,7 @@\n>  #include \"string-list.h\"\n>  #include \"column.h\"\n>  #include \"utf8.h\"\n> +#include \"wt-status.h\"\n>  \n>  static const char * const builtin_branch_usage[] = {\n>  \tN_(\"git branch [options] [-r | -a] [--merged | --no-merged]\"),\n> @@ -550,6 +551,28 @@ static int calc_maxwidth(struct ref_list *refs)\n>  \treturn w;\n>  }\n>  \n> +static char *get_head_description()\n\ns/()/(void)/;\n\nI think the series is much easier to read than the previous round\n(except for some nits, which I'll send separately).  We would want\nto add tests to protect the \"where did we detach from\" feature done\nby [PATCH 4/5], which does not seem to have any.\n"},{"id":"210576","messageId":"CACsJy8A+gw9oJYrJqa-b8noKM7qJTkZEd7ovmxqR68i6miCatA@mail.gmail.com","threadId":"33057","inReplyTo":"7vy5e49l3i.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-04T12:17:02Z","receivedAt":"2013-03-04T12:17:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 4, 2013 at 5:25 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> When a remote ref or a tag is checked out, HEAD is automatically\n>> detached. There is no user friendly way to find out what ref is\n>> checked out in this case. This patch digs in reflog for this\n>> information and shows \"Detached from origin/master\" or \"Detached from\n>> v1.8.0\" instead of \"Currently not on any branch\".\n>\n> \"Detached from\" is a nice attempt to compromise in the phrasing.\n>\n> We usually say you detach HEAD at v1.8.0, but what is shown is what\n> started from such a state but then the user may have built more\n> history on top of it and may no longer be at v1.8.0, so obviously we\n> do not want to say \"Detached at\".  We are in a \"detached at v1.8.0\n> and then possibly built one or more commits on top\" state (would it\n> be helpful to differentiate the \"nothing built on top\" and \"some\n> commits have been built on top\" cases, I wonder).\n\nHmm.. never thought of that subtlety. Differentiating should be\npossible. I'm just not sure if it would be helpful. Comments people,\ndo or not do?\n\n> Also I wonder if you could do a bit more to help the users who do:\n>\n>     $ git checkout $(git merge-base HEAD nd/branch-show-rebase-bisect-state)\n>\n> aka\n>\n>     $ git checkout ...nd/branch-show-rebase-bisect-state\n>\n> and then do one or more commits on top.\n>\n> Instead of punting to \"Currently not on any branch\", would it help\n> to show the place you first detached at, so that the user can then\n> grab that commit object name and run\n>\n>     $ git log --oneline $that_commit..\n>\n> or something?\n\n$that_commit would be HEAD@{-1} right? Should that be used instead of\ngrabbing random SHA-1 shown in git-status?\n\n\n>> +static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n>\n> Can't this be done by generalizing grab_nth_branch_switch() and then\n> exposing it as part of the general API?\n\nWe could share the code, yes. Initially I wanted something that\nresolves \"@{-1}\" and gives me the reflog entry. Maybe we could add a\nfunction to do that.\n\n\n> I also feel uneasy about two issues this and the previous change\n\nI assume \"previous change\" is 1/5, reflog format change.\n\n\n>  2) The previous one records this sequence in a funny way:\n>\n>         : start from branch A\n>         git checkout B\n>         git checkout C\n>\n>     The resulting reflog entries result in\n>\n>         checkout: moving from A to refs/heads/B\n>         checkout: moving from B to refs/heads/C\n>\n>     even though existing code and tools are expecting to read\n>\n>         checkout: moving from A to B\n>         checkout: moving from B to C\n\nIt does not record exactly user input, but the \"to\" part is basically\nextended sha-1 and I don't think current tools parse them manually.\nInstead just they should just pass them to git-rev-parse to do the\njob. So this change is not really bad. But again the recent '!'\nincident in attr.c shows that I know nothing about real world usage.\nWe could do dwim when parsing the reflog, which introduces no changes\nin reflog format.\n\n\n> By the way, even though the title of this patch is \"status: show the\n> ref that is checked out, even if it's detached\", a quick check with\n>\n>         $ cd ../linux-3.0\n>         $ git describe\n>         v3.8-rc7\n>         $ ../git.git/git-checkout v3.8-rc7\n>         $ tail -n 1 .git/logs/HEAD | sed -e 's/.*checkout/checkout/'\n>         checkout: moving from master to refs/tags/v3.8-rc7\n>         $ ../git.git/git-status | head -n 1\n>         # Not currently on any branch.\n>         $ ../git.git/git-branch -v\n>         * (no branch) 836dc9e Linux 3.8-rc7\n>           master      836dc9e Linux 3.8-rc7\n>\n> does not seem to give me anything more helpful.\n\nYeah. I blame myself for copying from interpret_nth_prior_checkout()\nwithout full understanding, and git's poor documentation (there's no\ndescription about for_each_recent_reflog_ent, and that the caller is\nsupposed to call for_each_reflog_ent in some case; but I think this\nconvention is unintuitive, for_each_recent_reflog_ent should do that\nitself)\n-- \nDuy\n"},{"id":"210579","messageId":"7v38wbxizi.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"CACsJy8A+gw9oJYrJqa-b8noKM7qJTkZEd7ovmxqR68i6miCatA@mail.gmail.com","subject":"Re: [PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-04T15:49:53Z","receivedAt":"2013-03-04T15:49:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> Instead of punting to \"Currently not on any branch\", would it help\n>> to show the place you first detached at, so that the user can then\n>> grab that commit object name and run\n>>\n>>     $ git log --oneline $that_commit..\n>>\n>> or something?\n>\n> $that_commit would be HEAD@{-1} right? Should that be used instead of\n> grabbing random SHA-1 shown in git-status?\n\nYou can say \"git checkout @{-1}\" to go back.  That is the commit you\ncame _from_.\n\nBut I think what you were computing for $that_commit was different.\nIt's \"the ref that is checked out, even if it's detached\".\n\nAfter doing:\n\n\t: start from anywhere\n        git checkout master\t;# on master\n        git checkout branch~4   ;# detach at that commit\n        ... hack hack hack ...\n        git commit; git commit\n\nyour @{-1} is 'master'; \"the ref that is checked out, even if its'\ndetached\" is 'branch~4', and that is what you want as $that_commit\nin the example.  That shows your _progress_, which has no relation\nwith 'master' that is @{-1}.\n"},{"id":"210621","messageId":"20130305113958.GA26236@lanh","threadId":"33057","inReplyTo":"7vy5e49l3i.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-05T11:39:58Z","receivedAt":"2013-03-05T11:39:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"OK new try. This\n\n- no longer requires 1/5 (i'll resend full series later when the\n  wanted behavior is found)\n\n- shows either \"detached from\" or \"detached at\". We could even do \"4\n  commits from detached point XXX\", like we do \"5 commits ahead of\n  upstream\". But I'm not sure if we should do that.\n\n- otherwise shows \"detached from <sha1>\". IOW \"currently not on any\n  branch\" is no longer shown.\n\n- fixes the case when reflog is too short and\n  for_each_recent_reflog_ent returns false.\n\nI did not merge grab_1st_switch and grab_nth_branch_switch because\nthey use different part of the reflog and merging seems to make it\nmore complicated than necessary.\n\n-- 8< --\nSubject: [PATCH] status: show more info than \"currently not on any branch\"\n\nWhen a remote ref or a tag is checked out, HEAD is automatically\ndetached. There is no user friendly way to find out what ref is\nchecked out in this case. This patch digs in reflog for this\ninformation and shows \"Detached from/at origin/master\" or \"Detached\nfrom/at v1.8.0\".\n\nWhen it cannot figure out the original ref, it shows an abbreviated\nSHA-1 instead. \"Currently not on any branch\" would never display.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t7406-submodule-update.sh |  6 ++--\n t/t7512-status-help.sh      | 52 +++++++++++++++++----------\n wt-status.c                 | 87 ++++++++++++++++++++++++++++++++++++++++++---\n wt-status.h                 |  4 ++-\n 4 files changed, 123 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex 4975ec0..50ac020 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -664,8 +664,10 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'\n \n test_expect_success 'submodule update properly revives a moved submodule' '\n \t(cd super &&\n+\t H=$(git rev-parse --short HEAD) &&\n \t git commit -am \"pre move\" &&\n-\t git status >expect&&\n+\t H2=$(git rev-parse --short HEAD) &&\n+\t git status | sed \"s/$H/XXX/\" >expect &&\n \t H=$(cd submodule2; git rev-parse HEAD) &&\n \t git rm --cached submodule2 &&\n \t rm -rf submodule2 &&\n@@ -674,7 +676,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '\n \t git config -f .gitmodules submodule.submodule2.path \"moved/sub module\"\n \t git commit -am \"post move\" &&\n \t git submodule update &&\n-\t git status >actual &&\n+\t git status | sed \"s/$H2/XXX/\" >actual &&\n \t test_cmp expect actual\n \t)\n '\ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex d2da89a..c4f030f 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -76,7 +76,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -103,7 +103,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '\n \techo three >main.txt &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -135,7 +135,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '\n \tONTO=$(git rev-parse --short rebase_i_conflicts) &&\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -161,7 +161,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -187,9 +187,10 @@ test_expect_success 'status when rebasing -i in edit mode' '\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~2) &&\n+\tTGT=$(git rev-parse --short two_rebase_i) &&\n \tgit rebase -i HEAD~2 &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''rebase_i_edit'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -214,8 +215,9 @@ test_expect_success 'status when splitting a commit' '\n \tONTO=$(git rev-parse --short HEAD~3) &&\n \tgit rebase -i HEAD~3 &&\n \tgit reset HEAD^ &&\n+\tTGT=$(git rev-parse --short HEAD) &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $TGT\n \t# You are currently splitting a commit while rebasing branch '\\''split_commit'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -243,10 +245,11 @@ test_expect_success 'status after editing the last commit with --amend during a\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~3) &&\n+\tTGT=$(git rev-parse --short three_amend) &&\n \tgit rebase -i HEAD~3 &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''amend_last'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -276,7 +279,7 @@ test_expect_success 'status: (continue first edit) second edit' '\n \tgit rebase -i HEAD~3 &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -298,7 +301,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -325,7 +328,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -347,7 +350,7 @@ test_expect_success 'status: (amend first edit) second edit' '\n \tgit commit --amend -m \"a\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -370,7 +373,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -398,7 +401,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"d\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -422,7 +425,7 @@ test_expect_success 'status: (split first edit) second edit' '\n \tgit commit -m \"e\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -447,7 +450,7 @@ test_expect_success 'status: (split first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -477,7 +480,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"h\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -572,8 +575,9 @@ test_expect_success 'status when bisecting' '\n \tgit bisect start &&\n \tgit bisect bad &&\n \tgit bisect good one_bisect &&\n-\tcat >expected <<-\\EOF &&\n-\t# Not currently on any branch.\n+\tTGT=$(git rev-parse --short two_bisect) &&\n+\tcat >expected <<-EOF &&\n+\t# Detached at $TGT\n \t# You are currently bisecting branch '\\''bisect'\\''.\n \t#   (use \"git bisect reset\" to get back to the original branch)\n \t#\n@@ -596,7 +600,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# Detached at $ONTO\n \t# You are currently rebasing branch '\\''statushints_disabled'\\'' on '\\''$ONTO'\\''.\n \t#\n \t# Unmerged paths:\n@@ -662,5 +666,15 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '\n \ttest_i18ncmp expected actual\n '\n \n+test_expect_success 'status showing detached from a tag' '\n+\ttest_commit atag tagging &&\n+\tgit checkout atag &&\n+\tcat >expected <<-\\EOF\n+\t# Detached at atag\n+\tnothing to commit (use -u to show untracked files)\n+\tEOF\n+\tgit status --untracked-files=no >actual &&\n+\ttest_i18ncmp expected actual\n+'\n \n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 6a3566b..7ad3c2b 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1001,7 +1001,70 @@ static void read_and_strip_branch(struct strbuf *sb,\n \t\t*branch = xstrdup(*branch);\n }\n \n-void wt_status_get_state(struct wt_status_state *state)\n+struct grab_1st_switch_cbdata {\n+\tstruct strbuf buf;\n+\tunsigned char nsha1[20];\n+};\n+\n+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t   const char *email, unsigned long timestamp, int tz,\n+\t\t\t   const char *message, void *cb_data)\n+{\n+\tstruct grab_1st_switch_cbdata *cb = cb_data;\n+\tconst char *target = NULL, *end;\n+\n+\tif (prefixcmp(message, \"checkout: moving from \"))\n+\t\treturn 0;\n+\tmessage += strlen(\"checkout: moving from \");\n+\ttarget = strstr(message, \" to \");\n+\tif (!target)\n+\t\treturn 0;\n+\ttarget += strlen(\" to \");\n+\tstrbuf_reset(&cb->buf);\n+\thashcpy(cb->nsha1, nsha1);\n+\tfor (end = target; *end && *end != '\\n'; end++)\n+\t\t;\n+\tstrbuf_add(&cb->buf, target, end - target);\n+\treturn 0;\n+}\n+\n+static void wt_status_get_detached_from(struct wt_status_state *state)\n+{\n+\tstruct grab_1st_switch_cbdata cb;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\tchar *ref = NULL;\n+\n+\tstrbuf_init(&cb.buf, 0);\n+\tif (for_each_recent_reflog_ent(\"HEAD\", grab_1st_switch, 4096, &cb))\n+\t\tfor_each_reflog_ent(\"HEAD\", grab_1st_switch, &cb);\n+\tif (!cb.buf.len)\n+\t\treturn;\n+\n+\tif (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&\n+\t    (commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n+\t    !hashcmp(cb.nsha1, commit->object.sha1)) {\n+\t\tint ofs;\n+\t\tif (!prefixcmp(ref, \"refs/tags/\"))\n+\t\t\tofs = strlen(\"refs/tags/\");\n+\t\telse if (!prefixcmp(ref, \"refs/remotes/\"))\n+\t\t\tofs = strlen(\"refs/remotes/\");\n+\t\telse\n+\t\t\tofs = 0;\n+\t\tstate->detached_from = xstrdup(ref + ofs);\n+\t\thashcpy(state->detached_sha1, sha1);\n+\t} else if (!get_sha1(cb.buf.buf, sha1)) {\n+\t\tstate->detached_from =\n+\t\t\txstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n+\t\thashcpy(state->detached_sha1, sha1);\n+\t}\n+\n+\tfree(ref);\n+\tstrbuf_release(&cb.buf);\n+}\n+\n+void wt_status_get_state(struct wt_status_state *state,\n+\t\t\t int get_detached_from)\n {\n \tstruct strbuf branch = STRBUF_INIT;\n \tstruct strbuf onto = STRBUF_INIT;\n@@ -1040,6 +1103,10 @@ void wt_status_get_state(struct wt_status_state *state)\n \t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"BISECT_START\");\n \t}\n+\n+\tif (get_detached_from)\n+\t\twt_status_get_detached_from(state);\n+\n \tstrbuf_release(&branch);\n \tstrbuf_release(&onto);\n }\n@@ -1066,7 +1133,8 @@ void wt_status_print(struct wt_status *s)\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n \tstruct wt_status_state state;\n \n-\twt_status_get_state(&state);\n+\twt_status_get_state(&state,\n+\t\t\t    s->branch && !strcmp(s->branch, \"HEAD\"));\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1074,9 +1142,19 @@ void wt_status_print(struct wt_status *s)\n \t\tif (!prefixcmp(branch_name, \"refs/heads/\"))\n \t\t\tbranch_name += 11;\n \t\telse if (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_name = \"\";\n \t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\tif (state.detached_from) {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tbranch_name = state.detached_from;\n+\t\t\t\tif (!get_sha1(\"HEAD\", sha1) &&\n+\t\t\t\t    !hashcmp(sha1, state.detached_sha1))\n+\t\t\t\t\ton_what = _(\"Detached at \");\n+\t\t\t\telse\n+\t\t\t\t\ton_what = _(\"Detached from \");\n+\t\t\t} else {\n+\t\t\t\tbranch_name = \"\";\n+\t\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\t}\n \t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n@@ -1088,6 +1166,7 @@ void wt_status_print(struct wt_status *s)\n \twt_status_print_state(s, &state);\n \tfree(state.branch);\n \tfree(state.onto);\n+\tfree(state.detached_from);\n \n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 5ddcbf6..5cb7df9 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -81,12 +81,14 @@ struct wt_status_state {\n \tint bisect_in_progress;\n \tchar *branch;\n \tchar *onto;\n+\tchar *detached_from;\n+\tunsigned char detached_sha1[20];\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n-void wt_status_get_state(struct wt_status_state *state);\n+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- 8< --\n"},{"id":"210624","messageId":"vpqd2ve9gzw.fsf@grenoble-inp.fr","threadId":"33057","inReplyTo":"20130305113958.GA26236@lanh","subject":"Re: [PATCH 4/5] status: show the ref that is checked out, even if it's detached","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-03-05T12:18:59Z","receivedAt":"2013-03-05T12:18:59Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> -\t# Not currently on any branch.\n> +\t# Detached at $ONTO\n\nWithout the context, I don't think \"Detached\" alone says something to\nthe user. \"Detached HEAD at ...\" would IMHO be clearer and at least give\nthe user enough keywords to search the web/doc for an explanation.\n\nActually, in the case of \"Detached HEAD at $branch\", you may even add an\nadvice saying something like:\n\n  (run \"git checkout $branch\" to switch to branch $branch)\n\nor so. But that's not the common case, so maybe it's not that useful.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"210688","messageId":"1362572482-20570-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362303681-6585-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 0/4] nd/branch-show-rebase-bisect-state updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:21:18Z","receivedAt":"2013-03-06T12:21:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since the previous round:\n\n - reflog format change is dropped\n - almost always show \"HEAD detached from/at xxx\" instead of\n   \"currently not on any branch\", where \"xxx\" is either a ref, or an\n   abbreviated sha-1\n\nI wanted to introduce something like @{-1:to} that resolves the \"to\"\nsha-1 in reflog, in addition to @{-1} which resolves the \"from\" part.\nBut the syntax looks ugly so I dropped it. Meanwhile people could do\n\ngit log -1 -g --grep-reflog checkout --pretty=format:%h\n\nto retrieve the same info.\n\nNguyễn Thái Ngọc Duy (4):\n  wt-status: split wt_status_state parsing function out\n  wt-status: move wt_status_get_state() out to wt_status_print()\n  status: show more info than \"currently not on any branch\"\n  branch: show more information when HEAD is detached\n\n builtin/branch.c            |  25 +++++++-\n t/t3203-branch-output.sh    |   6 +-\n t/t6030-bisect-porcelain.sh |   2 +-\n t/t7406-submodule-update.sh |   6 +-\n t/t7512-status-help.sh      |  52 +++++++++------\n wt-status.c                 | 152 ++++++++++++++++++++++++++++++++++----------\n wt-status.h                 |   7 +-\n 7 files changed, 190 insertions(+), 60 deletions(-)\n\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210689","messageId":"1362572482-20570-2-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 1/4] wt-status: split wt_status_state parsing function out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:21:19Z","receivedAt":"2013-03-06T12:21:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 52 +++++++++++++++++++++++++++++++---------------------\n wt-status.h |  5 +++--\n 2 files changed, 34 insertions(+), 23 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex ef405d0..183aafe 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -970,7 +970,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n  * Extract branch information from rebase/bisect\n  */\n static void read_and_strip_branch(struct strbuf *sb,\n-\t\t\t\t  const char **branch,\n+\t\t\t\t  char **branch,\n \t\t\t\t  const char *path)\n {\n \tunsigned char sha1[20];\n@@ -994,52 +994,62 @@ static void read_and_strip_branch(struct strbuf *sb,\n \t\tstrbuf_addstr(sb, abbrev);\n \t\t*branch = sb->buf;\n \t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n-\t\t;\n+\t\t*branch = NULL;\n \telse\t\t\t/* bisect */\n \t\t*branch = sb->buf;\n+\tif (*branch)\n+\t\t*branch = xstrdup(*branch);\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+void wt_status_get_state(struct wt_status_state *state)\n {\n-\tconst char *state_color = color(WT_STATUS_HEADER, s);\n \tstruct strbuf branch = STRBUF_INIT;\n \tstruct strbuf onto = STRBUF_INIT;\n-\tstruct wt_status_state state;\n \tstruct stat st;\n \n-\tmemset(&state, 0, sizeof(state));\n+\tmemset(state, 0, sizeof(*state));\n \n \tif (!stat(git_path(\"MERGE_HEAD\"), &st)) {\n-\t\tstate.merge_in_progress = 1;\n+\t\tstate->merge_in_progress = 1;\n \t} else if (!stat(git_path(\"rebase-apply\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-apply/applying\"), &st)) {\n-\t\t\tstate.am_in_progress = 1;\n+\t\t\tstate->am_in_progress = 1;\n \t\t\tif (!stat(git_path(\"rebase-apply/patch\"), &st) && !st.st_size)\n-\t\t\t\tstate.am_empty_patch = 1;\n+\t\t\t\tstate->am_empty_patch = 1;\n \t\t} else {\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t\t      \"rebase-apply/head-name\");\n-\t\t\tread_and_strip_branch(&onto, &state.onto,\n+\t\t\tread_and_strip_branch(&onto, &state->onto,\n \t\t\t\t\t      \"rebase-apply/onto\");\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n-\t\t\tstate.rebase_interactive_in_progress = 1;\n+\t\t\tstate->rebase_interactive_in_progress = 1;\n \t\telse\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"rebase-merge/head-name\");\n-\t\tread_and_strip_branch(&onto, &state.onto,\n+\t\tread_and_strip_branch(&onto, &state->onto,\n \t\t\t\t      \"rebase-merge/onto\");\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n-\t\tstate.cherry_pick_in_progress = 1;\n+\t\tstate->cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n-\t\tstate.bisect_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n+\t\tstate->bisect_in_progress = 1;\n+\t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"BISECT_START\");\n \t}\n+\tstrbuf_release(&branch);\n+\tstrbuf_release(&onto);\n+}\n+\n+static void wt_status_print_state(struct wt_status *s)\n+{\n+\tconst char *state_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(&state);\n \n \tif (state.merge_in_progress)\n \t\tshow_merge_in_progress(s, &state, state_color);\n@@ -1051,8 +1061,8 @@ static void wt_status_print_state(struct wt_status *s)\n \t\tshow_cherry_pick_in_progress(s, &state, state_color);\n \tif (state.bisect_in_progress)\n \t\tshow_bisect_in_progress(s, &state, state_color);\n-\tstrbuf_release(&branch);\n-\tstrbuf_release(&onto);\n+\tfree(state.branch);\n+\tfree(state.onto);\n }\n \n void wt_status_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 81e1dcf..5ddcbf6 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -79,13 +79,14 @@ struct wt_status_state {\n \tint rebase_interactive_in_progress;\n \tint cherry_pick_in_progress;\n \tint bisect_in_progress;\n-\tconst char *branch;\n-\tconst char *onto;\n+\tchar *branch;\n+\tchar *onto;\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n+void wt_status_get_state(struct wt_status_state *state);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210690","messageId":"1362572482-20570-3-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 2/4] wt-status: move wt_status_get_state() out to wt_status_print()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:21:20Z","receivedAt":"2013-03-06T12:21:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 37 +++++++++++++++++++------------------\n 1 file changed, 19 insertions(+), 18 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 183aafe..6a3566b 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1044,31 +1044,29 @@ void wt_status_get_state(struct wt_status_state *state)\n \tstrbuf_release(&onto);\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+static void wt_status_print_state(struct wt_status *s,\n+\t\t\t\t  struct wt_status_state *state)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct wt_status_state state;\n-\n-\twt_status_get_state(&state);\n-\n-\tif (state.merge_in_progress)\n-\t\tshow_merge_in_progress(s, &state, state_color);\n-\telse if (state.am_in_progress)\n-\t\tshow_am_in_progress(s, &state, state_color);\n-\telse if (state.rebase_in_progress || state.rebase_interactive_in_progress)\n-\t\tshow_rebase_in_progress(s, &state, state_color);\n-\telse if (state.cherry_pick_in_progress)\n-\t\tshow_cherry_pick_in_progress(s, &state, state_color);\n-\tif (state.bisect_in_progress)\n-\t\tshow_bisect_in_progress(s, &state, state_color);\n-\tfree(state.branch);\n-\tfree(state.onto);\n+\tif (state->merge_in_progress)\n+\t\tshow_merge_in_progress(s, state, state_color);\n+\telse if (state->am_in_progress)\n+\t\tshow_am_in_progress(s, state, state_color);\n+\telse if (state->rebase_in_progress || state->rebase_interactive_in_progress)\n+\t\tshow_rebase_in_progress(s, state, state_color);\n+\telse if (state->cherry_pick_in_progress)\n+\t\tshow_cherry_pick_in_progress(s, state, state_color);\n+\tif (state->bisect_in_progress)\n+\t\tshow_bisect_in_progress(s, state, state_color);\n }\n \n void wt_status_print(struct wt_status *s)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\twt_status_get_state(&state);\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1087,7 +1085,10 @@ void wt_status_print(struct wt_status *s)\n \t\t\twt_status_print_tracking(s);\n \t}\n \n-\twt_status_print_state(s);\n+\twt_status_print_state(s, &state);\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), _(\"Initial commit\"));\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210691","messageId":"1362572482-20570-4-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 3/4] status: show more info than \"currently not on any branch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:21:21Z","receivedAt":"2013-03-06T12:21:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When a remote ref or a tag is checked out, HEAD is automatically\ndetached. There is no user-friendly way to find out what ref is\nchecked out in this case. This patch digs in reflog for this\ninformation and shows \"HEAD detached from origin/master\" or \"HEAD\ndetached at v1.8.0\" instead of \"currently not on any branch\".\n\nWhen it cannot figure out the original ref, it shows an abbreviated\nSHA-1. \"Currently not on any branch\" would never display (unless\nreflog is pruned to near empty that the last checkout entry is lost).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t7406-submodule-update.sh |  6 ++--\n t/t7512-status-help.sh      | 52 +++++++++++++++++----------\n wt-status.c                 | 85 ++++++++++++++++++++++++++++++++++++++++++---\n wt-status.h                 |  4 ++-\n 4 files changed, 121 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex 4975ec0..50ac020 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -664,8 +664,10 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'\n \n test_expect_success 'submodule update properly revives a moved submodule' '\n \t(cd super &&\n+\t H=$(git rev-parse --short HEAD) &&\n \t git commit -am \"pre move\" &&\n-\t git status >expect&&\n+\t H2=$(git rev-parse --short HEAD) &&\n+\t git status | sed \"s/$H/XXX/\" >expect &&\n \t H=$(cd submodule2; git rev-parse HEAD) &&\n \t git rm --cached submodule2 &&\n \t rm -rf submodule2 &&\n@@ -674,7 +676,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '\n \t git config -f .gitmodules submodule.submodule2.path \"moved/sub module\"\n \t git commit -am \"post move\" &&\n \t git submodule update &&\n-\t git status >actual &&\n+\t git status | sed \"s/$H2/XXX/\" >actual &&\n \t test_cmp expect actual\n \t)\n '\ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex d2da89a..da22088 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -76,7 +76,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -103,7 +103,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '\n \techo three >main.txt &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -135,7 +135,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '\n \tONTO=$(git rev-parse --short rebase_i_conflicts) &&\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -161,7 +161,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -187,9 +187,10 @@ test_expect_success 'status when rebasing -i in edit mode' '\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~2) &&\n+\tTGT=$(git rev-parse --short two_rebase_i) &&\n \tgit rebase -i HEAD~2 &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''rebase_i_edit'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -214,8 +215,9 @@ test_expect_success 'status when splitting a commit' '\n \tONTO=$(git rev-parse --short HEAD~3) &&\n \tgit rebase -i HEAD~3 &&\n \tgit reset HEAD^ &&\n+\tTGT=$(git rev-parse --short HEAD) &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $TGT\n \t# You are currently splitting a commit while rebasing branch '\\''split_commit'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -243,10 +245,11 @@ test_expect_success 'status after editing the last commit with --amend during a\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~3) &&\n+\tTGT=$(git rev-parse --short three_amend) &&\n \tgit rebase -i HEAD~3 &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''amend_last'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -276,7 +279,7 @@ test_expect_success 'status: (continue first edit) second edit' '\n \tgit rebase -i HEAD~3 &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -298,7 +301,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -325,7 +328,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -347,7 +350,7 @@ test_expect_success 'status: (amend first edit) second edit' '\n \tgit commit --amend -m \"a\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -370,7 +373,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -398,7 +401,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"d\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -422,7 +425,7 @@ test_expect_success 'status: (split first edit) second edit' '\n \tgit commit -m \"e\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -447,7 +450,7 @@ test_expect_success 'status: (split first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -477,7 +480,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"h\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -572,8 +575,9 @@ test_expect_success 'status when bisecting' '\n \tgit bisect start &&\n \tgit bisect bad &&\n \tgit bisect good one_bisect &&\n-\tcat >expected <<-\\EOF &&\n-\t# Not currently on any branch.\n+\tTGT=$(git rev-parse --short two_bisect) &&\n+\tcat >expected <<-EOF &&\n+\t# HEAD detached at $TGT\n \t# You are currently bisecting branch '\\''bisect'\\''.\n \t#   (use \"git bisect reset\" to get back to the original branch)\n \t#\n@@ -596,7 +600,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''statushints_disabled'\\'' on '\\''$ONTO'\\''.\n \t#\n \t# Unmerged paths:\n@@ -662,5 +666,15 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '\n \ttest_i18ncmp expected actual\n '\n \n+test_expect_success 'status showing detached from a tag' '\n+\ttest_commit atag tagging &&\n+\tgit checkout atag &&\n+\tcat >expected <<-\\EOF\n+\t# HEAD detached at atag\n+\tnothing to commit (use -u to show untracked files)\n+\tEOF\n+\tgit status --untracked-files=no >actual &&\n+\ttest_i18ncmp expected actual\n+'\n \n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 6a3566b..7797ccf 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1001,7 +1001,68 @@ static void read_and_strip_branch(struct strbuf *sb,\n \t\t*branch = xstrdup(*branch);\n }\n \n-void wt_status_get_state(struct wt_status_state *state)\n+struct grab_1st_switch_cbdata {\n+\tstruct strbuf buf;\n+\tunsigned char nsha1[20];\n+};\n+\n+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t   const char *email, unsigned long timestamp, int tz,\n+\t\t\t   const char *message, void *cb_data)\n+{\n+\tstruct grab_1st_switch_cbdata *cb = cb_data;\n+\tconst char *target = NULL, *end;\n+\n+\tif (prefixcmp(message, \"checkout: moving from \"))\n+\t\treturn 0;\n+\tmessage += strlen(\"checkout: moving from \");\n+\ttarget = strstr(message, \" to \");\n+\tif (!target)\n+\t\treturn 0;\n+\ttarget += strlen(\" to \");\n+\tstrbuf_reset(&cb->buf);\n+\thashcpy(cb->nsha1, nsha1);\n+\tfor (end = target; *end && *end != '\\n'; end++)\n+\t\t;\n+\tstrbuf_add(&cb->buf, target, end - target);\n+\treturn 0;\n+}\n+\n+static void wt_status_get_detached_from(struct wt_status_state *state)\n+{\n+\tstruct grab_1st_switch_cbdata cb;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\tchar *ref = NULL;\n+\n+\tstrbuf_init(&cb.buf, 0);\n+\tif (for_each_recent_reflog_ent(\"HEAD\", grab_1st_switch, 4096, &cb))\n+\t\tfor_each_reflog_ent(\"HEAD\", grab_1st_switch, &cb);\n+\tif (!cb.buf.len)\n+\t\treturn;\n+\n+\tif (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&\n+\t    (commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n+\t    !hashcmp(cb.nsha1, commit->object.sha1)) {\n+\t\tint ofs;\n+\t\tif (!prefixcmp(ref, \"refs/tags/\"))\n+\t\t\tofs = strlen(\"refs/tags/\");\n+\t\telse if (!prefixcmp(ref, \"refs/remotes/\"))\n+\t\t\tofs = strlen(\"refs/remotes/\");\n+\t\telse\n+\t\t\tofs = 0;\n+\t\tstate->detached_from = xstrdup(ref + ofs);\n+\t} else\n+\t\tstate->detached_from =\n+\t\t\txstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));\n+\thashcpy(state->detached_sha1, cb.nsha1);\n+\n+\tfree(ref);\n+\tstrbuf_release(&cb.buf);\n+}\n+\n+void wt_status_get_state(struct wt_status_state *state,\n+\t\t\t int get_detached_from)\n {\n \tstruct strbuf branch = STRBUF_INIT;\n \tstruct strbuf onto = STRBUF_INIT;\n@@ -1040,6 +1101,10 @@ void wt_status_get_state(struct wt_status_state *state)\n \t\tread_and_strip_branch(&branch, &state->branch,\n \t\t\t\t      \"BISECT_START\");\n \t}\n+\n+\tif (get_detached_from)\n+\t\twt_status_get_detached_from(state);\n+\n \tstrbuf_release(&branch);\n \tstrbuf_release(&onto);\n }\n@@ -1066,7 +1131,8 @@ void wt_status_print(struct wt_status *s)\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n \tstruct wt_status_state state;\n \n-\twt_status_get_state(&state);\n+\twt_status_get_state(&state,\n+\t\t\t    s->branch && !strcmp(s->branch, \"HEAD\"));\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1074,9 +1140,19 @@ void wt_status_print(struct wt_status *s)\n \t\tif (!prefixcmp(branch_name, \"refs/heads/\"))\n \t\t\tbranch_name += 11;\n \t\telse if (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_name = \"\";\n \t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\tif (state.detached_from) {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tbranch_name = state.detached_from;\n+\t\t\t\tif (!get_sha1(\"HEAD\", sha1) &&\n+\t\t\t\t    !hashcmp(sha1, state.detached_sha1))\n+\t\t\t\t\ton_what = _(\"HEAD detached at \");\n+\t\t\t\telse\n+\t\t\t\t\ton_what = _(\"HEAD detached from \");\n+\t\t\t} else {\n+\t\t\t\tbranch_name = \"\";\n+\t\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\t}\n \t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n@@ -1088,6 +1164,7 @@ void wt_status_print(struct wt_status *s)\n \twt_status_print_state(s, &state);\n \tfree(state.branch);\n \tfree(state.onto);\n+\tfree(state.detached_from);\n \n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 5ddcbf6..5cb7df9 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -81,12 +81,14 @@ struct wt_status_state {\n \tint bisect_in_progress;\n \tchar *branch;\n \tchar *onto;\n+\tchar *detached_from;\n+\tunsigned char detached_sha1[20];\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n-void wt_status_get_state(struct wt_status_state *state);\n+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210692","messageId":"1362572482-20570-5-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 4/4] branch: show more information when HEAD is detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:21:22Z","receivedAt":"2013-03-06T12:21:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This prints more helpful info when HEAD is detached: is it detached\nbecause of bisect or rebase? What is the original branch name in those\ncases? Is it detached because the user checks out a remote ref or a\ntag (and which one)?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/branch.c            | 25 ++++++++++++++++++++++++-\n t/t3203-branch-output.sh    |  6 +++---\n t/t6030-bisect-porcelain.sh |  2 +-\n 3 files changed, 28 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 6371bf9..02dee0d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -18,6 +18,7 @@\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"utf8.h\"\n+#include \"wt-status.h\"\n \n static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [options] [-r | -a] [--merged | --no-merged]\"),\n@@ -550,6 +551,28 @@ static int calc_maxwidth(struct ref_list *refs)\n \treturn w;\n }\n \n+static char *get_head_description()\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from)\n+\t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\n+\t\t\t    state.detached_from);\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n \n static void show_detached(struct ref_list *ref_list)\n {\n@@ -557,7 +580,7 @@ static void show_detached(struct ref_list *ref_list)\n \n \tif (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {\n \t\tstruct ref_item item;\n-\t\titem.name = xstrdup(_(\"(no branch)\"));\n+\t\titem.name = get_head_description();\n \t\titem.width = utf8_strwidth(item.name);\n \t\titem.kind = REF_LOCAL_BRANCH;\n \t\titem.dest = NULL;\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 76fe7e0..ba4f98e 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -94,13 +94,13 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '\n \ttest_must_fail git branch -v branch*\n '\n \n-cat >expect <<'EOF'\n-* (no branch)\n+test_expect_success 'git branch shows detached HEAD properly' '\n+\tcat >expect <<EOF &&\n+* (detached from $(git rev-parse --short HEAD^0))\n   branch-one\n   branch-two\n   master\n EOF\n-test_expect_success 'git branch shows detached HEAD properly' '\n \tgit checkout HEAD^0 &&\n \tgit branch >actual &&\n \ttest_i18ncmp expect actual\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 3e0e15f..9b6f0d0 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n \tcp .git/BISECT_START saved &&\n \ttest_must_fail git bisect start $HASH4 foo -- &&\n \tgit branch > branch.output &&\n-\ttest_i18ngrep \"* (no branch)\" branch.output > /dev/null &&\n+\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n \ttest_cmp saved .git/BISECT_START\n '\n test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210693","messageId":"1362572773-20809-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-5-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 0/4] nd/branch-show-rebase-bisect-state updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:26:12Z","receivedAt":"2013-03-06T12:26:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Since the previous round:\n\n - reflog format change is dropped\n - almost always show \"HEAD detached from/at xxx\" instead of\n   \"currently not on any branch\", where \"xxx\" is either a ref, or an\n   abbreviated sha-1\n\nI wanted to introduce something like @{-1:to} that resolves the \"to\"\nsha-1 in reflog, in addition to @{-1} which resolves the \"from\" part.\nBut the syntax looks ugly so I dropped it. Meanwhile people could do\n\ngit log -1 -g --grep-reflog checkout --pretty=format:%h\n\nto retrieve the same info.\n\nNguyễn Thái Ngọc Duy (4):\n  wt-status: split wt_status_state parsing function out\n  wt-status: move wt_status_get_state() out to wt_status_print()\n  status: show more info than \"currently not on any branch\"\n  branch: show more information when HEAD is detached\n\n builtin/branch.c            |  25 +++++++-\n t/t3203-branch-output.sh    |   6 +-\n t/t6030-bisect-porcelain.sh |   2 +-\n t/t7406-submodule-update.sh |   6 +-\n t/t7512-status-help.sh      |  52 +++++++++------\n wt-status.c                 | 152 ++++++++++++++++++++++++++++++++++----------\n wt-status.h                 |   7 +-\n 7 files changed, 190 insertions(+), 60 deletions(-)\n\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210694","messageId":"1362572773-20809-2-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572773-20809-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2+ 4/4] branch: show more information when HEAD is detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-06T12:26:13Z","receivedAt":"2013-03-06T12:26:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This prints more helpful info when HEAD is detached: is it detached\nbecause of bisect or rebase? What is the original branch name in those\ncases? Is it detached because the user checks out a remote ref or a\ntag (and which one)?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Oops, the previous one did not do\n\n  s/\\(get_head_description\\)()$/\\1(void)/\n \n This one does.\n\n builtin/branch.c            | 25 ++++++++++++++++++++++++-\n t/t3203-branch-output.sh    |  6 +++---\n t/t6030-bisect-porcelain.sh |  2 +-\n 3 files changed, 28 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 6371bf9..02dee0d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -18,6 +18,7 @@\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"utf8.h\"\n+#include \"wt-status.h\"\n \n static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [options] [-r | -a] [--merged | --no-merged]\"),\n@@ -550,6 +551,28 @@ static int calc_maxwidth(struct ref_list *refs)\n \treturn w;\n }\n \n+static char *get_head_description(void)\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from)\n+\t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\n+\t\t\t    state.detached_from);\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n \n static void show_detached(struct ref_list *ref_list)\n {\n@@ -557,7 +580,7 @@ static void show_detached(struct ref_list *ref_list)\n \n \tif (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {\n \t\tstruct ref_item item;\n-\t\titem.name = xstrdup(_(\"(no branch)\"));\n+\t\titem.name = get_head_description();\n \t\titem.width = utf8_strwidth(item.name);\n \t\titem.kind = REF_LOCAL_BRANCH;\n \t\titem.dest = NULL;\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 76fe7e0..ba4f98e 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -94,13 +94,13 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '\n \ttest_must_fail git branch -v branch*\n '\n \n-cat >expect <<'EOF'\n-* (no branch)\n+test_expect_success 'git branch shows detached HEAD properly' '\n+\tcat >expect <<EOF &&\n+* (detached from $(git rev-parse --short HEAD^0))\n   branch-one\n   branch-two\n   master\n EOF\n-test_expect_success 'git branch shows detached HEAD properly' '\n \tgit checkout HEAD^0 &&\n \tgit branch >actual &&\n \ttest_i18ncmp expect actual\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 3e0e15f..9b6f0d0 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n \tcp .git/BISECT_START saved &&\n \ttest_must_fail git bisect start $HASH4 foo -- &&\n \tgit branch > branch.output &&\n-\ttest_i18ngrep \"* (no branch)\" branch.output > /dev/null &&\n+\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n \ttest_cmp saved .git/BISECT_START\n '\n test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"210707","messageId":"7v4ngoqs8m.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1362572482-20570-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 1/4] wt-status: split wt_status_state parsing function out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-06T18:48:41Z","receivedAt":"2013-03-06T18:48:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  wt-status.c | 52 +++++++++++++++++++++++++++++++---------------------\n>  wt-status.h |  5 +++--\n>  2 files changed, 34 insertions(+), 23 deletions(-)\n>\n> diff --git a/wt-status.c b/wt-status.c\n> index ef405d0..183aafe 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -970,7 +970,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n>   * Extract branch information from rebase/bisect\n>   */\n>  static void read_and_strip_branch(struct strbuf *sb,\n> -\t\t\t\t  const char **branch,\n> +\t\t\t\t  char **branch,\n>  \t\t\t\t  const char *path)\n>  {\n>  \tunsigned char sha1[20];\n> @@ -994,52 +994,62 @@ static void read_and_strip_branch(struct strbuf *sb,\n>  \t\tstrbuf_addstr(sb, abbrev);\n>  \t\t*branch = sb->buf;\n>  \t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n> -\t\t;\n> +\t\t*branch = NULL;\n>  \telse\t\t\t/* bisect */\n>  \t\t*branch = sb->buf;\n> +\tif (*branch)\n> +\t\t*branch = xstrdup(*branch);\n>  }\n\nThe reason why the original print_state() kept two strbufs in it was\nbecause its use of the return value (in *branch) from this function\nwas private and it did not want to have to strdup anything.\n\nWith this change, I suspect that it is much saner to make this\nfunction *not* take any external strbuf as input, because you are\nalways returning a piece of memory that belongs to the caller, or a\nNULL.\n\nIn other words, with the new world order, wouldn't a saner function\nsignature be:\n\n\tstatic const char *read_and_strip_branch(const char **path);\n\nafter this patch?\n\nAlso I notice that the error-return cases of this function may be\nwrong even before your patch.  Shouldn't it be doing *branch = NULL\n(and 'return NULL' after the suggested change)?\n"},{"id":"210709","messageId":"7vzjygpcd5.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1362572482-20570-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 3/4] status: show more info than \"currently not on any branch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-06T19:16:54Z","receivedAt":"2013-03-06T19:16:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +static void wt_status_get_detached_from(struct wt_status_state *state)\n> +{\n> +\tstruct grab_1st_switch_cbdata cb;\n> +\tstruct commit *commit;\n> +\tunsigned char sha1[20];\n> +\tchar *ref = NULL;\n> +\n> +\tstrbuf_init(&cb.buf, 0);\n> +\tif (for_each_recent_reflog_ent(\"HEAD\", grab_1st_switch, 4096, &cb))\n> +\t\tfor_each_reflog_ent(\"HEAD\", grab_1st_switch, &cb);\n> +\tif (!cb.buf.len)\n> +\t\treturn;\n\nIs this correct?  What if the recent entries (i.e. the tail 4k) of\nthe HEAD reflog did not have *any* checkout?  Your callback never\nreturns non-zero, so as long as the HEAD reflog is sufficiently\nlong, for_each_recent_reflog_ent() will return 0 to signal success,\nand you do not dig deeper by retrying the full reflog for HEAD,\nmissing the checkout that exists before the final 4k, no?\n\nIt should be more like this, I would think:\n\n\tfor_each_recent_reflog_ent();\n        if (!found)\n        \tfor_each_reflog_ent();\n\tif (!found)\n        \treturn;\n\nUsing cb.buf.len as the \"found\" marker may be correct, but I found\nit a bit subtle to my taste, without explanation.  Adding an\nexplicit bit to \"struct grab_1st_switch_cbdata\" would be cleaner and\nmore resistant to future changes, I think.\n\n> +\n> +\tif (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&\n> +\t    (commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n> +\t    !hashcmp(cb.nsha1, commit->object.sha1)) {\n\nThat feels unnecessarily expensive.  Why not hashcmp before checking\nthe type of the object to reject the case where the ref moved since\nthe last checkout early?\n\nFor that matter, does this even need to check the type of the object\nthat currently sits at the ref?  Isn't it sufficient to reject this\ncase by seeing if sha1 is the same as cb.nsha1?\n"},{"id":"210731","messageId":"7vy5e0nl02.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"7v4ngoqs8m.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 1/4] wt-status: split wt_status_state parsing function out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-06T23:53:17Z","receivedAt":"2013-03-06T23:53:17Z","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> In other words, with the new world order, wouldn't a saner function\n> signature be:\n>\n> \tstatic const char *read_and_strip_branch(const char **path);\n\nEh, discard an asterisk from there.  \"Given a string to name the\nfile, read it to find the branch name recorded in it\".\n\n> after this patch?\n>\n> Also I notice that the error-return cases of this function may be\n> wrong even before your patch.  Shouldn't it be doing *branch = NULL\n> (and 'return NULL' after the suggested change)?\n"},{"id":"210831","messageId":"CACsJy8CnDoMRzR5ifH3+0AKRLfzrNpkc3-NOxoA85SiRUb40ng@mail.gmail.com","threadId":"33057","inReplyTo":"7vzjygpcd5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 3/4] status: show more info than \"currently not on any branch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-08T11:04:38Z","receivedAt":"2013-03-08T11:04:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Mar 7, 2013 at 2:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> +static void wt_status_get_detached_from(struct wt_status_state *state)\n>> +{\n>> +     struct grab_1st_switch_cbdata cb;\n>> +     struct commit *commit;\n>> +     unsigned char sha1[20];\n>> +     char *ref = NULL;\n>> +\n>> +     strbuf_init(&cb.buf, 0);\n>> +     if (for_each_recent_reflog_ent(\"HEAD\", grab_1st_switch, 4096, &cb))\n>> +             for_each_reflog_ent(\"HEAD\", grab_1st_switch, &cb);\n>> +     if (!cb.buf.len)\n>> +             return;\n>\n> Is this correct?  What if the recent entries (i.e. the tail 4k) of\n> the HEAD reflog did not have *any* checkout?  Your callback never\n> returns non-zero, so as long as the HEAD reflog is sufficiently\n> long, for_each_recent_reflog_ent() will return 0 to signal success,\n> and you do not dig deeper by retrying the full reflog for HEAD,\n> missing the checkout that exists before the final 4k, no?\n>\n> It should be more like this, I would think:\n>\n>         for_each_recent_reflog_ent();\n>         if (!found)\n>                 for_each_reflog_ent();\n>         if (!found)\n>                 return;\n\nYes. This \"recent\" optimization is tricky.\n\n> Using cb.buf.len as the \"found\" marker may be correct, but I found\n> it a bit subtle to my taste, without explanation.  Adding an\n> explicit bit to \"struct grab_1st_switch_cbdata\" would be cleaner and\n> more resistant to future changes, I think.\n\nOK\n\n>\n>> +\n>> +     if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&\n>> +         (commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n>> +         !hashcmp(cb.nsha1, commit->object.sha1)) {\n>\n> That feels unnecessarily expensive.  Why not hashcmp before checking\n> the type of the object to reject the case where the ref moved since\n> the last checkout early?\n>\n> For that matter, does this even need to check the type of the object\n> that currently sits at the ref?  Isn't it sufficient to reject this\n> case by seeing if sha1 is the same as cb.nsha1?\n\nnsha1 is always a commit sha-1, sha-1 could be a tag sha-1 that refers\nto the same commit. hashcmp before lookup is a good idea. Although I\ndon't think it's expensive in the big picture. When HEAD is not\ndetached, we show \"<n> commits ahead of @{u}\" which is way more\nexpensive than this. As long as \"git status\" on detached HEAD does not\nuse up all the time that \"git status\" on branches normally does, I\nthink we're fine.\n-- \nDuy\n"},{"id":"210855","messageId":"7vtxolfttl.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"CACsJy8CnDoMRzR5ifH3+0AKRLfzrNpkc3-NOxoA85SiRUb40ng@mail.gmail.com","subject":"Re: [PATCH v2 3/4] status: show more info than \"currently not on any branch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-08T21:46:46Z","receivedAt":"2013-03-08T21:46:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> It should be more like this, I would think:\n>>\n>>         for_each_recent_reflog_ent();\n>>         if (!found)\n>>                 for_each_reflog_ent();\n>>         if (!found)\n>>                 return;\n>\n> Yes. This \"recent\" optimization is tricky.\n\nNot really.  What is tricky is that reflog is an append-only file\nand we only have an API to let us read it in the oldest to newer\norder, which is natural for the file format, but unsuited for the\npurpose of finding out nth most recent anything.\n\nSee the other thread I am going to send out soon on this.\n"},{"id":"211207","messageId":"1363174973-17597-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1362572482-20570-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 0/5] nd/branch-show-rebase-bisect-state updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:48Z","receivedAt":"2013-03-13T11:42:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This round fixes the design issue of read_and_strip_branch(), makes\nuse of jc/reflog-reverse-walk and performs cheaper sha-1 check on\ndetached commits.\n\nNguyễn Thái Ngọc Duy (5):\n  wt-status: move strbuf into read_and_strip_branch()\n  wt-status: split wt_status_state parsing function out\n  wt-status: move wt_status_get_state() out to wt_status_print()\n  status: show more info than \"currently not on any branch\"\n  branch: show more information when HEAD is detached\n\n builtin/branch.c            |  26 +++++-\n t/t3203-branch-output.sh    |   6 +-\n t/t6030-bisect-porcelain.sh |   2 +-\n t/t7406-submodule-update.sh |   6 +-\n t/t7512-status-help.sh      |  52 ++++++-----\n wt-status.c                 | 204 +++++++++++++++++++++++++++++++-------------\n wt-status.h                 |   7 +-\n 7 files changed, 214 insertions(+), 89 deletions(-)\n\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211208","messageId":"1363174973-17597-2-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 1/5] wt-status: move strbuf into read_and_strip_branch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:49Z","receivedAt":"2013-03-13T11:42:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 65 ++++++++++++++++++++++++++++---------------------------------\n wt-status.h |  4 ++--\n 2 files changed, 32 insertions(+), 37 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex ef405d0..6cac27b 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -969,41 +969,41 @@ static void show_bisect_in_progress(struct wt_status *s,\n /*\n  * Extract branch information from rebase/bisect\n  */\n-static void read_and_strip_branch(struct strbuf *sb,\n-\t\t\t\t  const char **branch,\n-\t\t\t\t  const char *path)\n+static char *read_and_strip_branch(const char *path)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tunsigned char sha1[20];\n \n-\tstrbuf_reset(sb);\n-\tif (strbuf_read_file(sb, git_path(\"%s\", path), 0) <= 0)\n-\t\treturn;\n+\tif (strbuf_read_file(&sb, git_path(\"%s\", path), 0) <= 0)\n+\t\tgoto got_nothing;\n \n-\twhile (sb->len && sb->buf[sb->len - 1] == '\\n')\n-\t\tstrbuf_setlen(sb, sb->len - 1);\n-\tif (!sb->len)\n-\t\treturn;\n-\tif (!prefixcmp(sb->buf, \"refs/heads/\"))\n-\t\t*branch = sb->buf + strlen(\"refs/heads/\");\n-\telse if (!prefixcmp(sb->buf, \"refs/\"))\n-\t\t*branch = sb->buf;\n-\telse if (!get_sha1_hex(sb->buf, sha1)) {\n+\twhile (&sb.len && sb.buf[sb.len - 1] == '\\n')\n+\t\tstrbuf_setlen(&sb, sb.len - 1);\n+\tif (!sb.len)\n+\t\tgoto got_nothing;\n+\tif (!prefixcmp(sb.buf, \"refs/heads/\"))\n+\t\tstrbuf_remove(&sb,0, strlen(\"refs/heads/\"));\n+\telse if (!prefixcmp(sb.buf, \"refs/\"))\n+\t\t;\n+\telse if (!get_sha1_hex(sb.buf, sha1)) {\n \t\tconst char *abbrev;\n \t\tabbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);\n-\t\tstrbuf_reset(sb);\n-\t\tstrbuf_addstr(sb, abbrev);\n-\t\t*branch = sb->buf;\n-\t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n-\t\t;\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_addstr(&sb, abbrev);\n+\t} else if (!strcmp(sb.buf, \"detached HEAD\")) /* rebase */\n+\t\tgoto got_nothing;\n \telse\t\t\t/* bisect */\n-\t\t*branch = sb->buf;\n+\t\t;\n+\treturn strbuf_detach(&sb, NULL);\n+\n+got_nothing:\n+\tstrbuf_release(&sb);\n+\treturn NULL;\n }\n \n static void wt_status_print_state(struct wt_status *s)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct strbuf branch = STRBUF_INIT;\n-\tstruct strbuf onto = STRBUF_INIT;\n \tstruct wt_status_state state;\n \tstruct stat st;\n \n@@ -1018,27 +1018,22 @@ static void wt_status_print_state(struct wt_status *s)\n \t\t\t\tstate.am_empty_patch = 1;\n \t\t} else {\n \t\t\tstate.rebase_in_progress = 1;\n-\t\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t\t      \"rebase-apply/head-name\");\n-\t\t\tread_and_strip_branch(&onto, &state.onto,\n-\t\t\t\t\t      \"rebase-apply/onto\");\n+\t\t\tstate.branch = read_and_strip_branch(\"rebase-apply/head-name\");\n+\t\t\tstate.onto = read_and_strip_branch(\"rebase-apply/onto\");\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n \t\t\tstate.rebase_interactive_in_progress = 1;\n \t\telse\n \t\t\tstate.rebase_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t      \"rebase-merge/head-name\");\n-\t\tread_and_strip_branch(&onto, &state.onto,\n-\t\t\t\t      \"rebase-merge/onto\");\n+\t\tstate.branch = read_and_strip_branch(\"rebase-merge/head-name\");\n+\t\tstate.onto = read_and_strip_branch(\"rebase-merge/onto\");\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n \t\tstate.cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n \t\tstate.bisect_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t      \"BISECT_START\");\n+\t\tstate.branch = read_and_strip_branch(\"BISECT_START\");\n \t}\n \n \tif (state.merge_in_progress)\n@@ -1051,8 +1046,8 @@ static void wt_status_print_state(struct wt_status *s)\n \t\tshow_cherry_pick_in_progress(s, &state, state_color);\n \tif (state.bisect_in_progress)\n \t\tshow_bisect_in_progress(s, &state, state_color);\n-\tstrbuf_release(&branch);\n-\tstrbuf_release(&onto);\n+\tfree(state.branch);\n+\tfree(state.onto);\n }\n \n void wt_status_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 81e1dcf..b8c3512 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -79,8 +79,8 @@ struct wt_status_state {\n \tint rebase_interactive_in_progress;\n \tint cherry_pick_in_progress;\n \tint bisect_in_progress;\n-\tconst char *branch;\n-\tconst char *onto;\n+\tchar *branch;\n+\tchar *onto;\n };\n \n void wt_status_prepare(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211209","messageId":"1363174973-17597-3-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 2/5] wt-status: split wt_status_state parsing function out","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:50Z","receivedAt":"2013-03-13T11:42:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 41 +++++++++++++++++++++++------------------\n wt-status.h |  1 +\n 2 files changed, 24 insertions(+), 18 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 6cac27b..0d8989f 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1001,40 +1001,45 @@ got_nothing:\n \treturn NULL;\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+void wt_status_get_state(struct wt_status_state *state)\n {\n-\tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct wt_status_state state;\n \tstruct stat st;\n \n-\tmemset(&state, 0, sizeof(state));\n-\n \tif (!stat(git_path(\"MERGE_HEAD\"), &st)) {\n-\t\tstate.merge_in_progress = 1;\n+\t\tstate->merge_in_progress = 1;\n \t} else if (!stat(git_path(\"rebase-apply\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-apply/applying\"), &st)) {\n-\t\t\tstate.am_in_progress = 1;\n+\t\t\tstate->am_in_progress = 1;\n \t\t\tif (!stat(git_path(\"rebase-apply/patch\"), &st) && !st.st_size)\n-\t\t\t\tstate.am_empty_patch = 1;\n+\t\t\t\tstate->am_empty_patch = 1;\n \t\t} else {\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\t\tstate.branch = read_and_strip_branch(\"rebase-apply/head-name\");\n-\t\t\tstate.onto = read_and_strip_branch(\"rebase-apply/onto\");\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\t\tstate->branch = read_and_strip_branch(\"rebase-apply/head-name\");\n+\t\t\tstate->onto = read_and_strip_branch(\"rebase-apply/onto\");\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n-\t\t\tstate.rebase_interactive_in_progress = 1;\n+\t\t\tstate->rebase_interactive_in_progress = 1;\n \t\telse\n-\t\t\tstate.rebase_in_progress = 1;\n-\t\tstate.branch = read_and_strip_branch(\"rebase-merge/head-name\");\n-\t\tstate.onto = read_and_strip_branch(\"rebase-merge/onto\");\n+\t\t\tstate->rebase_in_progress = 1;\n+\t\tstate->branch = read_and_strip_branch(\"rebase-merge/head-name\");\n+\t\tstate->onto = read_and_strip_branch(\"rebase-merge/onto\");\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n-\t\tstate.cherry_pick_in_progress = 1;\n+\t\tstate->cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n-\t\tstate.bisect_in_progress = 1;\n-\t\tstate.branch = read_and_strip_branch(\"BISECT_START\");\n+\t\tstate->bisect_in_progress = 1;\n+\t\tstate->branch = read_and_strip_branch(\"BISECT_START\");\n \t}\n+}\n+\n+static void wt_status_print_state(struct wt_status *s)\n+{\n+\tconst char *state_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state);\n \n \tif (state.merge_in_progress)\n \t\tshow_merge_in_progress(s, &state, state_color);\ndiff --git a/wt-status.h b/wt-status.h\nindex b8c3512..5ddcbf6 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -86,6 +86,7 @@ struct wt_status_state {\n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n+void wt_status_get_state(struct wt_status_state *state);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211210","messageId":"1363174973-17597-4-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 3/5] wt-status: move wt_status_get_state() out to wt_status_print()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:51Z","receivedAt":"2013-03-13T11:42:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c | 39 ++++++++++++++++++++-------------------\n 1 file changed, 20 insertions(+), 19 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 0d8989f..17690d8 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1033,32 +1033,30 @@ void wt_status_get_state(struct wt_status_state *state)\n \t}\n }\n \n-static void wt_status_print_state(struct wt_status *s)\n+static void wt_status_print_state(struct wt_status *s,\n+\t\t\t\t  struct wt_status_state *state)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct wt_status_state state;\n-\n-\tmemset(&state, 0, sizeof(state));\n-\twt_status_get_state(&state);\n-\n-\tif (state.merge_in_progress)\n-\t\tshow_merge_in_progress(s, &state, state_color);\n-\telse if (state.am_in_progress)\n-\t\tshow_am_in_progress(s, &state, state_color);\n-\telse if (state.rebase_in_progress || state.rebase_interactive_in_progress)\n-\t\tshow_rebase_in_progress(s, &state, state_color);\n-\telse if (state.cherry_pick_in_progress)\n-\t\tshow_cherry_pick_in_progress(s, &state, state_color);\n-\tif (state.bisect_in_progress)\n-\t\tshow_bisect_in_progress(s, &state, state_color);\n-\tfree(state.branch);\n-\tfree(state.onto);\n+\tif (state->merge_in_progress)\n+\t\tshow_merge_in_progress(s, state, state_color);\n+\telse if (state->am_in_progress)\n+\t\tshow_am_in_progress(s, state, state_color);\n+\telse if (state->rebase_in_progress || state->rebase_interactive_in_progress)\n+\t\tshow_rebase_in_progress(s, state, state_color);\n+\telse if (state->cherry_pick_in_progress)\n+\t\tshow_cherry_pick_in_progress(s, state, state_color);\n+\tif (state->bisect_in_progress)\n+\t\tshow_bisect_in_progress(s, state, state_color);\n }\n \n void wt_status_print(struct wt_status *s)\n {\n \tconst char *branch_color = color(WT_STATUS_ONBRANCH, s);\n \tconst char *branch_status_color = color(WT_STATUS_HEADER, s);\n+\tstruct wt_status_state state;\n+\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state);\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1077,7 +1075,10 @@ void wt_status_print(struct wt_status *s)\n \t\t\twt_status_print_tracking(s);\n \t}\n \n-\twt_status_print_state(s);\n+\twt_status_print_state(s, &state);\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), _(\"Initial commit\"));\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211211","messageId":"1363174973-17597-5-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 4/5] status: show more info than \"currently not on any branch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:52Z","receivedAt":"2013-03-13T11:42:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When a remote ref or a tag is checked out, HEAD is automatically\ndetached. There is no user-friendly way to find out what ref is\nchecked out in this case. This patch digs in reflog for this\ninformation and shows \"HEAD detached from origin/master\" or \"HEAD\ndetached at v1.8.0\" instead of \"currently not on any branch\".\n\nWhen it cannot figure out the original ref, it shows an abbreviated\nSHA-1. \"Currently not on any branch\" would never display (unless\nreflog is pruned to near empty that the last checkout entry is lost).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t7406-submodule-update.sh |  6 ++-\n t/t7512-status-help.sh      | 52 ++++++++++++++++----------\n wt-status.c                 | 89 +++++++++++++++++++++++++++++++++++++++++++--\n wt-status.h                 |  4 +-\n 4 files changed, 125 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex ea61761..a4ffea0 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -665,8 +665,10 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'\n \n test_expect_success 'submodule update properly revives a moved submodule' '\n \t(cd super &&\n+\t H=$(git rev-parse --short HEAD) &&\n \t git commit -am \"pre move\" &&\n-\t git status >expect&&\n+\t H2=$(git rev-parse --short HEAD) &&\n+\t git status | sed \"s/$H/XXX/\" >expect &&\n \t H=$(cd submodule2; git rev-parse HEAD) &&\n \t git rm --cached submodule2 &&\n \t rm -rf submodule2 &&\n@@ -675,7 +677,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '\n \t git config -f .gitmodules submodule.submodule2.path \"moved/sub module\"\n \t git commit -am \"post move\" &&\n \t git submodule update &&\n-\t git status >actual &&\n+\t git status | sed \"s/$H2/XXX/\" >actual &&\n \t test_cmp expect actual\n \t)\n '\ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex d2da89a..da22088 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -76,7 +76,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -103,7 +103,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '\n \techo three >main.txt &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_conflicts'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -135,7 +135,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '\n \tONTO=$(git rev-parse --short rebase_i_conflicts) &&\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (fix conflicts and then run \"git rebase --continue\")\n \t#   (use \"git rebase --skip\" to skip this patch)\n@@ -161,7 +161,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '\n \ttest_must_fail git rebase -i rebase_i_conflicts &&\n \tgit add main.txt &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''rebase_i_conflicts_second'\\'' on '\\''$ONTO'\\''.\n \t#   (all conflicts fixed: run \"git rebase --continue\")\n \t#\n@@ -187,9 +187,10 @@ test_expect_success 'status when rebasing -i in edit mode' '\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~2) &&\n+\tTGT=$(git rev-parse --short two_rebase_i) &&\n \tgit rebase -i HEAD~2 &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''rebase_i_edit'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -214,8 +215,9 @@ test_expect_success 'status when splitting a commit' '\n \tONTO=$(git rev-parse --short HEAD~3) &&\n \tgit rebase -i HEAD~3 &&\n \tgit reset HEAD^ &&\n+\tTGT=$(git rev-parse --short HEAD) &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $TGT\n \t# You are currently splitting a commit while rebasing branch '\\''split_commit'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -243,10 +245,11 @@ test_expect_success 'status after editing the last commit with --amend during a\n \texport FAKE_LINES &&\n \ttest_when_finished \"git rebase --abort\" &&\n \tONTO=$(git rev-parse --short HEAD~3) &&\n+\tTGT=$(git rev-parse --short three_amend) &&\n \tgit rebase -i HEAD~3 &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $TGT\n \t# You are currently editing a commit while rebasing branch '\\''amend_last'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -276,7 +279,7 @@ test_expect_success 'status: (continue first edit) second edit' '\n \tgit rebase -i HEAD~3 &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -298,7 +301,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -325,7 +328,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"foo\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -347,7 +350,7 @@ test_expect_success 'status: (amend first edit) second edit' '\n \tgit commit --amend -m \"a\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -370,7 +373,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -398,7 +401,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"d\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -422,7 +425,7 @@ test_expect_success 'status: (split first edit) second edit' '\n \tgit commit -m \"e\" &&\n \tgit rebase --continue &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -447,7 +450,7 @@ test_expect_success 'status: (split first edit) second edit and split' '\n \tgit rebase --continue &&\n \tgit reset HEAD^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently splitting a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (Once your working directory is clean, run \"git rebase --continue\")\n \t#\n@@ -477,7 +480,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '\n \tgit rebase --continue &&\n \tgit commit --amend -m \"h\" &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached from $ONTO\n \t# You are currently editing a commit while rebasing branch '\\''several_edits'\\'' on '\\''$ONTO'\\''.\n \t#   (use \"git commit --amend\" to amend the current commit)\n \t#   (use \"git rebase --continue\" once you are satisfied with your changes)\n@@ -572,8 +575,9 @@ test_expect_success 'status when bisecting' '\n \tgit bisect start &&\n \tgit bisect bad &&\n \tgit bisect good one_bisect &&\n-\tcat >expected <<-\\EOF &&\n-\t# Not currently on any branch.\n+\tTGT=$(git rev-parse --short two_bisect) &&\n+\tcat >expected <<-EOF &&\n+\t# HEAD detached at $TGT\n \t# You are currently bisecting branch '\\''bisect'\\''.\n \t#   (use \"git bisect reset\" to get back to the original branch)\n \t#\n@@ -596,7 +600,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '\n \tONTO=$(git rev-parse --short HEAD^^) &&\n \ttest_must_fail git rebase HEAD^ --onto HEAD^^ &&\n \tcat >expected <<-EOF &&\n-\t# Not currently on any branch.\n+\t# HEAD detached at $ONTO\n \t# You are currently rebasing branch '\\''statushints_disabled'\\'' on '\\''$ONTO'\\''.\n \t#\n \t# Unmerged paths:\n@@ -662,5 +666,15 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '\n \ttest_i18ncmp expected actual\n '\n \n+test_expect_success 'status showing detached from a tag' '\n+\ttest_commit atag tagging &&\n+\tgit checkout atag &&\n+\tcat >expected <<-\\EOF\n+\t# HEAD detached at atag\n+\tnothing to commit (use -u to show untracked files)\n+\tEOF\n+\tgit status --untracked-files=no >actual &&\n+\ttest_i18ncmp expected actual\n+'\n \n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 17690d8..8c6e0c8 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1001,7 +1001,73 @@ got_nothing:\n \treturn NULL;\n }\n \n-void wt_status_get_state(struct wt_status_state *state)\n+struct grab_1st_switch_cbdata {\n+\tint found;\n+\tstruct strbuf buf;\n+\tunsigned char nsha1[20];\n+};\n+\n+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t   const char *email, unsigned long timestamp, int tz,\n+\t\t\t   const char *message, void *cb_data)\n+{\n+\tstruct grab_1st_switch_cbdata *cb = cb_data;\n+\tconst char *target = NULL, *end;\n+\n+\tif (prefixcmp(message, \"checkout: moving from \"))\n+\t\treturn 0;\n+\tmessage += strlen(\"checkout: moving from \");\n+\ttarget = strstr(message, \" to \");\n+\tif (!target)\n+\t\treturn 0;\n+\ttarget += strlen(\" to \");\n+\tstrbuf_reset(&cb->buf);\n+\thashcpy(cb->nsha1, nsha1);\n+\tfor (end = target; *end && *end != '\\n'; end++)\n+\t\t;\n+\tstrbuf_add(&cb->buf, target, end - target);\n+\tcb->found = 1;\n+\treturn 1;\n+}\n+\n+static void wt_status_get_detached_from(struct wt_status_state *state)\n+{\n+\tstruct grab_1st_switch_cbdata cb;\n+\tstruct commit *commit;\n+\tunsigned char sha1[20];\n+\tchar *ref = NULL;\n+\n+\tstrbuf_init(&cb.buf, 0);\n+\tif (for_each_reflog_ent_reverse(\"HEAD\", grab_1st_switch, &cb) <= 0) {\n+\t\tstrbuf_release(&cb.buf);\n+\t\treturn;\n+\t}\n+\n+\tif (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&\n+\t    /* sha1 is a commit? match without further lookup */\n+\t    (!hashcmp(cb.nsha1, sha1) ||\n+\t     /* perhaps sha1 is a tag, try to dereference to a commit */\n+\t     ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&\n+\t      !hashcmp(cb.nsha1, commit->object.sha1)))) {\n+\t\tint ofs;\n+\t\tif (!prefixcmp(ref, \"refs/tags/\"))\n+\t\t\tofs = strlen(\"refs/tags/\");\n+\t\telse if (!prefixcmp(ref, \"refs/remotes/\"))\n+\t\t\tofs = strlen(\"refs/remotes/\");\n+\t\telse\n+\t\t\tofs = 0;\n+\t\tstate->detached_from = xstrdup(ref + ofs);\n+\t} else\n+\t\tstate->detached_from =\n+\t\t\txstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));\n+\thashcpy(state->detached_sha1, cb.nsha1);\n+\n+\tfree(ref);\n+\tstrbuf_release(&cb.buf);\n+}\n+\n+void wt_status_get_state(struct wt_status_state *state,\n+\t\t\t int get_detached_from)\n {\n \tstruct stat st;\n \n@@ -1031,6 +1097,9 @@ void wt_status_get_state(struct wt_status_state *state)\n \t\tstate->bisect_in_progress = 1;\n \t\tstate->branch = read_and_strip_branch(\"BISECT_START\");\n \t}\n+\n+\tif (get_detached_from)\n+\t\twt_status_get_detached_from(state);\n }\n \n static void wt_status_print_state(struct wt_status *s,\n@@ -1056,7 +1125,8 @@ void wt_status_print(struct wt_status *s)\n \tstruct wt_status_state state;\n \n \tmemset(&state, 0, sizeof(state));\n-\twt_status_get_state(&state);\n+\twt_status_get_state(&state,\n+\t\t\t    s->branch && !strcmp(s->branch, \"HEAD\"));\n \n \tif (s->branch) {\n \t\tconst char *on_what = _(\"On branch \");\n@@ -1064,9 +1134,19 @@ void wt_status_print(struct wt_status *s)\n \t\tif (!prefixcmp(branch_name, \"refs/heads/\"))\n \t\t\tbranch_name += 11;\n \t\telse if (!strcmp(branch_name, \"HEAD\")) {\n-\t\t\tbranch_name = \"\";\n \t\t\tbranch_status_color = color(WT_STATUS_NOBRANCH, s);\n-\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\tif (state.detached_from) {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tbranch_name = state.detached_from;\n+\t\t\t\tif (!get_sha1(\"HEAD\", sha1) &&\n+\t\t\t\t    !hashcmp(sha1, state.detached_sha1))\n+\t\t\t\t\ton_what = _(\"HEAD detached at \");\n+\t\t\t\telse\n+\t\t\t\t\ton_what = _(\"HEAD detached from \");\n+\t\t\t} else {\n+\t\t\t\tbranch_name = \"\";\n+\t\t\t\ton_what = _(\"Not currently on any branch.\");\n+\t\t\t}\n \t\t}\n \t\tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\");\n \t\tstatus_printf_more(s, branch_status_color, \"%s\", on_what);\n@@ -1078,6 +1158,7 @@ void wt_status_print(struct wt_status *s)\n \twt_status_print_state(s, &state);\n \tfree(state.branch);\n \tfree(state.onto);\n+\tfree(state.detached_from);\n \n \tif (s->is_initial) {\n \t\tstatus_printf_ln(s, color(WT_STATUS_HEADER, s), \"\");\ndiff --git a/wt-status.h b/wt-status.h\nindex 5ddcbf6..5cb7df9 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -81,12 +81,14 @@ struct wt_status_state {\n \tint bisect_in_progress;\n \tchar *branch;\n \tchar *onto;\n+\tchar *detached_from;\n+\tunsigned char detached_sha1[20];\n };\n \n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n-void wt_status_get_state(struct wt_status_state *state);\n+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);\n \n void wt_shortstatus_print(struct wt_status *s);\n void wt_porcelain_print(struct wt_status *s);\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211212","messageId":"1363174973-17597-6-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 5/5] branch: show more information when HEAD is detached","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-13T11:42:53Z","receivedAt":"2013-03-13T11:42:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This prints more helpful info when HEAD is detached: is it detached\nbecause of bisect or rebase? What is the original branch name in those\ncases? Is it detached because the user checks out a remote ref or a\ntag (and which one)?\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/branch.c            | 26 +++++++++++++++++++++++++-\n t/t3203-branch-output.sh    |  6 +++---\n t/t6030-bisect-porcelain.sh |  2 +-\n 3 files changed, 29 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 00d17d2..2ab0e4f 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -18,6 +18,7 @@\n #include \"string-list.h\"\n #include \"column.h\"\n #include \"utf8.h\"\n+#include \"wt-status.h\"\n \n static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [options] [-r | -a] [--merged | --no-merged]\"),\n@@ -550,6 +551,29 @@ static int calc_maxwidth(struct ref_list *refs)\n \treturn w;\n }\n \n+static char *get_head_description(void)\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from)\n+\t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\n+\t\t\t    state.detached_from);\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n \n static void show_detached(struct ref_list *ref_list)\n {\n@@ -557,7 +581,7 @@ static void show_detached(struct ref_list *ref_list)\n \n \tif (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {\n \t\tstruct ref_item item;\n-\t\titem.name = xstrdup(_(\"(no branch)\"));\n+\t\titem.name = get_head_description();\n \t\titem.width = utf8_strwidth(item.name);\n \t\titem.kind = REF_LOCAL_BRANCH;\n \t\titem.dest = NULL;\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 76fe7e0..ba4f98e 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -94,13 +94,13 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '\n \ttest_must_fail git branch -v branch*\n '\n \n-cat >expect <<'EOF'\n-* (no branch)\n+test_expect_success 'git branch shows detached HEAD properly' '\n+\tcat >expect <<EOF &&\n+* (detached from $(git rev-parse --short HEAD^0))\n   branch-one\n   branch-two\n   master\n EOF\n-test_expect_success 'git branch shows detached HEAD properly' '\n \tgit checkout HEAD^0 &&\n \tgit branch >actual &&\n \ttest_i18ncmp expect actual\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 3e0e15f..9b6f0d0 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n \tcp .git/BISECT_START saved &&\n \ttest_must_fail git bisect start $HASH4 foo -- &&\n \tgit branch > branch.output &&\n-\ttest_i18ngrep \"* (no branch)\" branch.output > /dev/null &&\n+\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n \ttest_cmp saved .git/BISECT_START\n '\n test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\n-- \n1.8.1.2.536.gf441e6d\n"},{"id":"211227","messageId":"7va9q746fq.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1363174973-17597-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 1/5] wt-status: move strbuf into read_and_strip_branch()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-13T16:20:57Z","receivedAt":"2013-03-13T16:20:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nYou do not have anything to say above the S-o-b line?  The primary\neffect of this patch is to change the lifespan and ownership rule of\nthe piece of memory used for state.branch/state.onto and moving the\nstrbuf is a mere logical consequence of that change.  It is not\nexplained why this change is a good idea.\n\n> ---\n>  wt-status.c | 65 ++++++++++++++++++++++++++++---------------------------------\n>  wt-status.h |  4 ++--\n>  2 files changed, 32 insertions(+), 37 deletions(-)\n>\n> diff --git a/wt-status.c b/wt-status.c\n> index ef405d0..6cac27b 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -969,41 +969,41 @@ static void show_bisect_in_progress(struct wt_status *s,\n>  /*\n>   * Extract branch information from rebase/bisect\n>   */\n> -static void read_and_strip_branch(struct strbuf *sb,\n> -\t\t\t\t  const char **branch,\n> -\t\t\t\t  const char *path)\n> +static char *read_and_strip_branch(const char *path)\n>  {\n> +\tstruct strbuf sb = STRBUF_INIT;\n>  \tunsigned char sha1[20];\n>  \n> -\tstrbuf_reset(sb);\n> -\tif (strbuf_read_file(sb, git_path(\"%s\", path), 0) <= 0)\n> -\t\treturn;\n> +\tif (strbuf_read_file(&sb, git_path(\"%s\", path), 0) <= 0)\n> +\t\tgoto got_nothing;\n>  \n> -\twhile (sb->len && sb->buf[sb->len - 1] == '\\n')\n> -\t\tstrbuf_setlen(sb, sb->len - 1);\n> -\tif (!sb->len)\n> -\t\treturn;\n> -\tif (!prefixcmp(sb->buf, \"refs/heads/\"))\n> -\t\t*branch = sb->buf + strlen(\"refs/heads/\");\n> -\telse if (!prefixcmp(sb->buf, \"refs/\"))\n> -\t\t*branch = sb->buf;\n> -\telse if (!get_sha1_hex(sb->buf, sha1)) {\n> +\twhile (&sb.len && sb.buf[sb.len - 1] == '\\n')\n> +\t\tstrbuf_setlen(&sb, sb.len - 1);\n> +\tif (!sb.len)\n> +\t\tgoto got_nothing;\n> +\tif (!prefixcmp(sb.buf, \"refs/heads/\"))\n> +\t\tstrbuf_remove(&sb,0, strlen(\"refs/heads/\"));\n> +\telse if (!prefixcmp(sb.buf, \"refs/\"))\n> +\t\t;\n> +\telse if (!get_sha1_hex(sb.buf, sha1)) {\n>  \t\tconst char *abbrev;\n>  \t\tabbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);\n> -\t\tstrbuf_reset(sb);\n> -\t\tstrbuf_addstr(sb, abbrev);\n> -\t\t*branch = sb->buf;\n> -\t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n> -\t\t;\n> +\t\tstrbuf_reset(&sb);\n> +\t\tstrbuf_addstr(&sb, abbrev);\n> +\t} else if (!strcmp(sb.buf, \"detached HEAD\")) /* rebase */\n> +\t\tgoto got_nothing;\n>  \telse\t\t\t/* bisect */\n> -\t\t*branch = sb->buf;\n> +\t\t;\n> +\treturn strbuf_detach(&sb, NULL);\n> +\n> +got_nothing:\n> +\tstrbuf_release(&sb);\n> +\treturn NULL;\n>  }\n>  \n>  static void wt_status_print_state(struct wt_status *s)\n>  {\n>  \tconst char *state_color = color(WT_STATUS_HEADER, s);\n> -\tstruct strbuf branch = STRBUF_INIT;\n> -\tstruct strbuf onto = STRBUF_INIT;\n>  \tstruct wt_status_state state;\n>  \tstruct stat st;\n>  \n> @@ -1018,27 +1018,22 @@ static void wt_status_print_state(struct wt_status *s)\n>  \t\t\t\tstate.am_empty_patch = 1;\n>  \t\t} else {\n>  \t\t\tstate.rebase_in_progress = 1;\n> -\t\t\tread_and_strip_branch(&branch, &state.branch,\n> -\t\t\t\t\t      \"rebase-apply/head-name\");\n> -\t\t\tread_and_strip_branch(&onto, &state.onto,\n> -\t\t\t\t\t      \"rebase-apply/onto\");\n> +\t\t\tstate.branch = read_and_strip_branch(\"rebase-apply/head-name\");\n> +\t\t\tstate.onto = read_and_strip_branch(\"rebase-apply/onto\");\n>  \t\t}\n>  \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n>  \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n>  \t\t\tstate.rebase_interactive_in_progress = 1;\n>  \t\telse\n>  \t\t\tstate.rebase_in_progress = 1;\n> -\t\tread_and_strip_branch(&branch, &state.branch,\n> -\t\t\t\t      \"rebase-merge/head-name\");\n> -\t\tread_and_strip_branch(&onto, &state.onto,\n> -\t\t\t\t      \"rebase-merge/onto\");\n> +\t\tstate.branch = read_and_strip_branch(\"rebase-merge/head-name\");\n> +\t\tstate.onto = read_and_strip_branch(\"rebase-merge/onto\");\n>  \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n>  \t\tstate.cherry_pick_in_progress = 1;\n>  \t}\n>  \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n>  \t\tstate.bisect_in_progress = 1;\n> -\t\tread_and_strip_branch(&branch, &state.branch,\n> -\t\t\t\t      \"BISECT_START\");\n> +\t\tstate.branch = read_and_strip_branch(\"BISECT_START\");\n>  \t}\n>  \n>  \tif (state.merge_in_progress)\n> @@ -1051,8 +1046,8 @@ static void wt_status_print_state(struct wt_status *s)\n>  \t\tshow_cherry_pick_in_progress(s, &state, state_color);\n>  \tif (state.bisect_in_progress)\n>  \t\tshow_bisect_in_progress(s, &state, state_color);\n> -\tstrbuf_release(&branch);\n> -\tstrbuf_release(&onto);\n> +\tfree(state.branch);\n> +\tfree(state.onto);\n>  }\n>  \n>  void wt_status_print(struct wt_status *s)\n> diff --git a/wt-status.h b/wt-status.h\n> index 81e1dcf..b8c3512 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -79,8 +79,8 @@ struct wt_status_state {\n>  \tint rebase_interactive_in_progress;\n>  \tint cherry_pick_in_progress;\n>  \tint bisect_in_progress;\n> -\tconst char *branch;\n> -\tconst char *onto;\n> +\tchar *branch;\n> +\tchar *onto;\n>  };\n>  \n>  void wt_status_prepare(struct wt_status *s);\n"},{"id":"211228","messageId":"7v620v468y.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1363174973-17597-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 4/5] status: show more info than \"currently not on any branch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-13T16:25:01Z","receivedAt":"2013-03-13T16:25:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> When a remote ref or a tag is checked out, HEAD is automatically\n> detached. There is no user-friendly way to find out what ref is\n> checked out in this case. This patch digs in reflog for this\n> information and shows \"HEAD detached from origin/master\" or \"HEAD\n> detached at v1.8.0\" instead of \"currently not on any branch\".\n>\n> When it cannot figure out the original ref, it shows an abbreviated\n> SHA-1. \"Currently not on any branch\" would never display (unless\n> reflog is pruned to near empty that the last checkout entry is lost).\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nIt does look like using the for_each_in_reverse() does make the\ncaller less error prone (by the way, I'd love to see people review,\npoke holes and plug them in the implementation of reading the reflog\nfile in reverse; I am not sure if it covered all the corner cases\nright).\n\nThanks.\n"},{"id":"211427","messageId":"1363399956-13325-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"1363174973-17597-2-git-send-email-pclouds@gmail.com","subject":"[PATCH v3+ 1/5] wt-status: move strbuf into read_and_strip_branch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-16T02:12:36Z","receivedAt":"2013-03-16T02:12:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The strbufs are placed outside read_and_strip_branch as a premature\noptimization: when it reads \"refs/heads/foo\" to strbuf and wants to\nreturn just \"foo\", it could do so without memory movement. In return\nthe caller must not use the returned pointer after releasing strbufs,\nwhich own the buffers that contain the returned strings. It's a clumsy\ndesign.\n\nBy moving strbufs into read_and_strip_branch(), the returned pointer\nalways points to a malloc'd buffer or NULL. The pointer can be passed\naround and freed after use.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Fixed the commit message. No code change.\n\n wt-status.c | 65 ++++++++++++++++++++++++++++---------------------------------\n wt-status.h |  4 ++--\n 2 files changed, 32 insertions(+), 37 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex ef405d0..6cac27b 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -969,41 +969,41 @@ static void show_bisect_in_progress(struct wt_status *s,\n /*\n  * Extract branch information from rebase/bisect\n  */\n-static void read_and_strip_branch(struct strbuf *sb,\n-\t\t\t\t  const char **branch,\n-\t\t\t\t  const char *path)\n+static char *read_and_strip_branch(const char *path)\n {\n+\tstruct strbuf sb = STRBUF_INIT;\n \tunsigned char sha1[20];\n \n-\tstrbuf_reset(sb);\n-\tif (strbuf_read_file(sb, git_path(\"%s\", path), 0) <= 0)\n-\t\treturn;\n+\tif (strbuf_read_file(&sb, git_path(\"%s\", path), 0) <= 0)\n+\t\tgoto got_nothing;\n \n-\twhile (sb->len && sb->buf[sb->len - 1] == '\\n')\n-\t\tstrbuf_setlen(sb, sb->len - 1);\n-\tif (!sb->len)\n-\t\treturn;\n-\tif (!prefixcmp(sb->buf, \"refs/heads/\"))\n-\t\t*branch = sb->buf + strlen(\"refs/heads/\");\n-\telse if (!prefixcmp(sb->buf, \"refs/\"))\n-\t\t*branch = sb->buf;\n-\telse if (!get_sha1_hex(sb->buf, sha1)) {\n+\twhile (&sb.len && sb.buf[sb.len - 1] == '\\n')\n+\t\tstrbuf_setlen(&sb, sb.len - 1);\n+\tif (!sb.len)\n+\t\tgoto got_nothing;\n+\tif (!prefixcmp(sb.buf, \"refs/heads/\"))\n+\t\tstrbuf_remove(&sb,0, strlen(\"refs/heads/\"));\n+\telse if (!prefixcmp(sb.buf, \"refs/\"))\n+\t\t;\n+\telse if (!get_sha1_hex(sb.buf, sha1)) {\n \t\tconst char *abbrev;\n \t\tabbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);\n-\t\tstrbuf_reset(sb);\n-\t\tstrbuf_addstr(sb, abbrev);\n-\t\t*branch = sb->buf;\n-\t} else if (!strcmp(sb->buf, \"detached HEAD\")) /* rebase */\n-\t\t;\n+\t\tstrbuf_reset(&sb);\n+\t\tstrbuf_addstr(&sb, abbrev);\n+\t} else if (!strcmp(sb.buf, \"detached HEAD\")) /* rebase */\n+\t\tgoto got_nothing;\n \telse\t\t\t/* bisect */\n-\t\t*branch = sb->buf;\n+\t\t;\n+\treturn strbuf_detach(&sb, NULL);\n+\n+got_nothing:\n+\tstrbuf_release(&sb);\n+\treturn NULL;\n }\n \n static void wt_status_print_state(struct wt_status *s)\n {\n \tconst char *state_color = color(WT_STATUS_HEADER, s);\n-\tstruct strbuf branch = STRBUF_INIT;\n-\tstruct strbuf onto = STRBUF_INIT;\n \tstruct wt_status_state state;\n \tstruct stat st;\n \n@@ -1018,27 +1018,22 @@ static void wt_status_print_state(struct wt_status *s)\n \t\t\t\tstate.am_empty_patch = 1;\n \t\t} else {\n \t\t\tstate.rebase_in_progress = 1;\n-\t\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t\t      \"rebase-apply/head-name\");\n-\t\t\tread_and_strip_branch(&onto, &state.onto,\n-\t\t\t\t\t      \"rebase-apply/onto\");\n+\t\t\tstate.branch = read_and_strip_branch(\"rebase-apply/head-name\");\n+\t\t\tstate.onto = read_and_strip_branch(\"rebase-apply/onto\");\n \t\t}\n \t} else if (!stat(git_path(\"rebase-merge\"), &st)) {\n \t\tif (!stat(git_path(\"rebase-merge/interactive\"), &st))\n \t\t\tstate.rebase_interactive_in_progress = 1;\n \t\telse\n \t\t\tstate.rebase_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t      \"rebase-merge/head-name\");\n-\t\tread_and_strip_branch(&onto, &state.onto,\n-\t\t\t\t      \"rebase-merge/onto\");\n+\t\tstate.branch = read_and_strip_branch(\"rebase-merge/head-name\");\n+\t\tstate.onto = read_and_strip_branch(\"rebase-merge/onto\");\n \t} else if (!stat(git_path(\"CHERRY_PICK_HEAD\"), &st)) {\n \t\tstate.cherry_pick_in_progress = 1;\n \t}\n \tif (!stat(git_path(\"BISECT_LOG\"), &st)) {\n \t\tstate.bisect_in_progress = 1;\n-\t\tread_and_strip_branch(&branch, &state.branch,\n-\t\t\t\t      \"BISECT_START\");\n+\t\tstate.branch = read_and_strip_branch(\"BISECT_START\");\n \t}\n \n \tif (state.merge_in_progress)\n@@ -1051,8 +1046,8 @@ static void wt_status_print_state(struct wt_status *s)\n \t\tshow_cherry_pick_in_progress(s, &state, state_color);\n \tif (state.bisect_in_progress)\n \t\tshow_bisect_in_progress(s, &state, state_color);\n-\tstrbuf_release(&branch);\n-\tstrbuf_release(&onto);\n+\tfree(state.branch);\n+\tfree(state.onto);\n }\n \n void wt_status_print(struct wt_status *s)\ndiff --git a/wt-status.h b/wt-status.h\nindex 81e1dcf..b8c3512 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -79,8 +79,8 @@ struct wt_status_state {\n \tint rebase_interactive_in_progress;\n \tint cherry_pick_in_progress;\n \tint bisect_in_progress;\n-\tconst char *branch;\n-\tconst char *onto;\n+\tchar *branch;\n+\tchar *onto;\n };\n \n void wt_status_prepare(struct wt_status *s);\n-- \n1.8.2.83.gc99314b\n"},{"id":"211701","messageId":"7vhak7i6ch.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1363174973-17597-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v3 0/5] nd/branch-show-rebase-bisect-state updates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-19T18:37:18Z","receivedAt":"2013-03-19T18:37:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This round fixes the design issue of read_and_strip_branch(), makes\n> use of jc/reflog-reverse-walk and performs cheaper sha-1 check on\n> detached commits.\n\nI was running \"git bisect start pu jch\" (jch is a branch I do not\npush out everywhere that is somewhere in the middle of\n\"master..pu\"), and then after bisection finished, saw this:\n\n    $ git branch --with 66887099e096f0258a5ef3b1e7458748597bdffe\n    * (no branch, bisecting jch)\n      nd/magic-pathspecs\n      pu\n\nwhich is obviously bogus.  I only said \"I know the tip of pu is bad\nand the tip of jch is good\".  If anything, I was bisecting the\nbreakage in 'pu', not in 'jch'.\n\nI think the message should better say \"before you started to bisect\nyou were on 'jch'\" or something instead.\n"},{"id":"211744","messageId":"CACsJy8C93OOLWW2tdh17XA1k3Zs_kUjyZ32v4Ywk2BK83BL3wQ@mail.gmail.com","threadId":"33057","inReplyTo":"7vhak7i6ch.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 0/5] nd/branch-show-rebase-bisect-state updates","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-20T12:40:49Z","receivedAt":"2013-03-20T12:40:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Mar 20, 2013 at 1:37 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I was running \"git bisect start pu jch\" (jch is a branch I do not\n> push out everywhere that is somewhere in the middle of\n> \"master..pu\"), and then after bisection finished, saw this:\n>\n>     $ git branch --with 66887099e096f0258a5ef3b1e7458748597bdffe\n>     * (no branch, bisecting jch)\n>       nd/magic-pathspecs\n>       pu\n>\n> which is obviously bogus.  I only said \"I know the tip of pu is bad\n> and the tip of jch is good\".  If anything, I was bisecting the\n> breakage in 'pu', not in 'jch'.\n>\n> I think the message should better say \"before you started to bisect\n> you were on 'jch'\" or something instead.\n\nHow about\n\n* (no branch, bisect started on jch)\n\nthen? I don't want to make it too long because it'll waste space in\ncolumn layout.\n-- \nDuy\n"},{"id":"211757","messageId":"7vfvzqgms9.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"CACsJy8C93OOLWW2tdh17XA1k3Zs_kUjyZ32v4Ywk2BK83BL3wQ@mail.gmail.com","subject":"Re: [PATCH v3 0/5] nd/branch-show-rebase-bisect-state updates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-20T14:37:26Z","receivedAt":"2013-03-20T14:37:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> I think the message should better say \"before you started to bisect\n>> you were on 'jch'\" or something instead.\n>\n> How about\n>\n> * (no branch, bisect started on jch)\n>\n> then?\n\nSure.\n\nWhat you are showing is used only to decide where \"git bisect reset\"\ngoes to, I think.\n"},{"id":"212019","messageId":"1364010764-972-1-git-send-email-pclouds@gmail.com","threadId":"33057","inReplyTo":"7vfvzqgms9.fsf@alter.siamese.dyndns.org","subject":"[PATCH] status, branch: fix the misleading \"bisecting\" message","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-03-23T03:52:44Z","receivedAt":"2013-03-23T03:52:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The current message is \"bisecting %s\" (or \"bisecting branch %s\").\n\"%s\" is the current branch when we started bisecting. Clarify that to\navoid confusion with good and bad refs passed to \"bisect\" command.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/branch.c            | 2 +-\n t/t6030-bisect-porcelain.sh | 2 +-\n t/t7512-status-help.sh      | 2 +-\n wt-status.c                 | 2 +-\n 4 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 99105f8..8f00203 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -562,7 +562,7 @@ static char *get_head_description(void)\n \t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n \t\t\t    state.branch);\n \telse if (state.bisect_in_progress)\n-\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisect started on %s)\"),\n \t\t\t    state.branch);\n \telse if (state.detached_from)\n \t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 9b6f0d0..2fce99a 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n \tcp .git/BISECT_START saved &&\n \ttest_must_fail git bisect start $HASH4 foo -- &&\n \tgit branch > branch.output &&\n-\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n+\ttest_i18ngrep \"* (no branch, bisect started on other)\" branch.output > /dev/null &&\n \ttest_cmp saved .git/BISECT_START\n '\n test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 5adba4f..c35d01d 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -578,7 +578,7 @@ test_expect_success 'status when bisecting' '\n \tTGT=$(git rev-parse --short two_bisect) &&\n \tcat >expected <<-EOF &&\n \t# HEAD detached at $TGT\n-\t# You are currently bisecting branch '\\''bisect'\\''.\n+\t# You are currently bisecting, started from branch '\\''bisect'\\''.\n \t#   (use \"git bisect reset\" to get back to the original branch)\n \t#\n \tnothing to commit (use -u to show untracked files)\ndiff --git a/wt-status.c b/wt-status.c\nindex 32a51e1..cf3d81a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -953,7 +953,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n {\n \tif (state->branch)\n \t\tstatus_printf_ln(s, color,\n-\t\t\t\t _(\"You are currently bisecting branch '%s'.\"),\n+\t\t\t\t _(\"You are currently bisecting, started from branch '%s'.\"),\n \t\t\t\t state->branch);\n \telse\n \t\tstatus_printf_ln(s, color,\n-- \n1.8.2.83.gc99314b\n"},{"id":"212074","messageId":"7v4ng14b61.fsf@alter.siamese.dyndns.org","threadId":"33057","inReplyTo":"1364010764-972-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] status, branch: fix the misleading \"bisecting\" message","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-03-24T05:30:30Z","receivedAt":"2013-03-24T05:30:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:\n\n> The current message is \"bisecting %s\" (or \"bisecting branch %s\").\n> \"%s\" is the current branch when we started bisecting. Clarify that to\n> avoid confusion with good and bad refs passed to \"bisect\" command.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nSounds sensible.  Is this something we should squash in to\nnd/branch-show-rebase-bisect-state topic, or just apply it to the\ntop (the latter is easier for me, but you may want to keep the\nhistory free of \"oops, that was a bad idea\" changes).\n\nWill queue on top, at least for now, as the topic is not in 'next'.\n\nThanks.\n\n>  builtin/branch.c            | 2 +-\n>  t/t6030-bisect-porcelain.sh | 2 +-\n>  t/t7512-status-help.sh      | 2 +-\n>  wt-status.c                 | 2 +-\n>  4 files changed, 4 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index 99105f8..8f00203 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -562,7 +562,7 @@ static char *get_head_description(void)\n>  \t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n>  \t\t\t    state.branch);\n>  \telse if (state.bisect_in_progress)\n> -\t\tstrbuf_addf(&desc, _(\"(no branch, bisecting %s)\"),\n> +\t\tstrbuf_addf(&desc, _(\"(no branch, bisect started on %s)\"),\n>  \t\t\t    state.branch);\n>  \telse if (state.detached_from)\n>  \t\tstrbuf_addf(&desc, _(\"(detached from %s)\"),\n> diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\n> index 9b6f0d0..2fce99a 100755\n> --- a/t/t6030-bisect-porcelain.sh\n> +++ b/t/t6030-bisect-porcelain.sh\n> @@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing \".git/BISECT_START\" not modified if\n>  \tcp .git/BISECT_START saved &&\n>  \ttest_must_fail git bisect start $HASH4 foo -- &&\n>  \tgit branch > branch.output &&\n> -\ttest_i18ngrep \"* (no branch, bisecting other)\" branch.output > /dev/null &&\n> +\ttest_i18ngrep \"* (no branch, bisect started on other)\" branch.output > /dev/null &&\n>  \ttest_cmp saved .git/BISECT_START\n>  '\n>  test_expect_success 'bisect start: no \".git/BISECT_START\" if mistaken rev' '\n> diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\n> index 5adba4f..c35d01d 100755\n> --- a/t/t7512-status-help.sh\n> +++ b/t/t7512-status-help.sh\n> @@ -578,7 +578,7 @@ test_expect_success 'status when bisecting' '\n>  \tTGT=$(git rev-parse --short two_bisect) &&\n>  \tcat >expected <<-EOF &&\n>  \t# HEAD detached at $TGT\n> -\t# You are currently bisecting branch '\\''bisect'\\''.\n> +\t# You are currently bisecting, started from branch '\\''bisect'\\''.\n>  \t#   (use \"git bisect reset\" to get back to the original branch)\n>  \t#\n>  \tnothing to commit (use -u to show untracked files)\n> diff --git a/wt-status.c b/wt-status.c\n> index 32a51e1..cf3d81a 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -953,7 +953,7 @@ static void show_bisect_in_progress(struct wt_status *s,\n>  {\n>  \tif (state->branch)\n>  \t\tstatus_printf_ln(s, color,\n> -\t\t\t\t _(\"You are currently bisecting branch '%s'.\"),\n> +\t\t\t\t _(\"You are currently bisecting, started from branch '%s'.\"),\n>  \t\t\t\t state->branch);\n>  \telse\n>  \t\tstatus_printf_ln(s, color,\n"}]}