status: show more info than "currently not on any branch"
[git/git.git] / wt-status.c
index 40dced3..32a51e1 100644 (file)
@@ -999,7 +999,73 @@ got_nothing:
        return NULL;
 }
 
        return NULL;
 }
 
-void wt_status_get_state(struct wt_status_state *state)
+struct grab_1st_switch_cbdata {
+       int found;
+       struct strbuf buf;
+       unsigned char nsha1[20];
+};
+
+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
+                          const char *email, unsigned long timestamp, int tz,
+                          const char *message, void *cb_data)
+{
+       struct grab_1st_switch_cbdata *cb = cb_data;
+       const char *target = NULL, *end;
+
+       if (prefixcmp(message, "checkout: moving from "))
+               return 0;
+       message += strlen("checkout: moving from ");
+       target = strstr(message, " to ");
+       if (!target)
+               return 0;
+       target += strlen(" to ");
+       strbuf_reset(&cb->buf);
+       hashcpy(cb->nsha1, nsha1);
+       for (end = target; *end && *end != '\n'; end++)
+               ;
+       strbuf_add(&cb->buf, target, end - target);
+       cb->found = 1;
+       return 1;
+}
+
+static void wt_status_get_detached_from(struct wt_status_state *state)
+{
+       struct grab_1st_switch_cbdata cb;
+       struct commit *commit;
+       unsigned char sha1[20];
+       char *ref = NULL;
+
+       strbuf_init(&cb.buf, 0);
+       if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
+               strbuf_release(&cb.buf);
+               return;
+       }
+
+       if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
+           /* sha1 is a commit? match without further lookup */
+           (!hashcmp(cb.nsha1, sha1) ||
+            /* perhaps sha1 is a tag, try to dereference to a commit */
+            ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
+             !hashcmp(cb.nsha1, commit->object.sha1)))) {
+               int ofs;
+               if (!prefixcmp(ref, "refs/tags/"))
+                       ofs = strlen("refs/tags/");
+               else if (!prefixcmp(ref, "refs/remotes/"))
+                       ofs = strlen("refs/remotes/");
+               else
+                       ofs = 0;
+               state->detached_from = xstrdup(ref + ofs);
+       } else
+               state->detached_from =
+                       xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
+       hashcpy(state->detached_sha1, cb.nsha1);
+
+       free(ref);
+       strbuf_release(&cb.buf);
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+                        int get_detached_from)
 {
        struct stat st;
 
 {
        struct stat st;
 
@@ -1029,6 +1095,9 @@ void wt_status_get_state(struct wt_status_state *state)
                state->bisect_in_progress = 1;
                state->branch = read_and_strip_branch("BISECT_START");
        }
                state->bisect_in_progress = 1;
                state->branch = read_and_strip_branch("BISECT_START");
        }
+
+       if (get_detached_from)
+               wt_status_get_detached_from(state);
 }
 
 static void wt_status_print_state(struct wt_status *s,
 }
 
 static void wt_status_print_state(struct wt_status *s,
@@ -1054,7 +1123,8 @@ void wt_status_print(struct wt_status *s)
        struct wt_status_state state;
 
        memset(&state, 0, sizeof(state));
        struct wt_status_state state;
 
        memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state);
+       wt_status_get_state(&state,
+                           s->branch && !strcmp(s->branch, "HEAD"));
 
        if (s->branch) {
                const char *on_what = _("On branch ");
 
        if (s->branch) {
                const char *on_what = _("On branch ");
@@ -1062,9 +1132,19 @@ void wt_status_print(struct wt_status *s)
                if (!prefixcmp(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                if (!prefixcmp(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
-                       branch_name = "";
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
-                       on_what = _("Not currently on any branch.");
+                       if (state.detached_from) {
+                               unsigned char sha1[20];
+                               branch_name = state.detached_from;
+                               if (!get_sha1("HEAD", sha1) &&
+                                   !hashcmp(sha1, state.detached_sha1))
+                                       on_what = _("HEAD detached at ");
+                               else
+                                       on_what = _("HEAD detached from ");
+                       } else {
+                               branch_name = "";
+                               on_what = _("Not currently on any branch.");
+                       }
                }
                status_printf(s, color(WT_STATUS_HEADER, s), "");
                status_printf_more(s, branch_status_color, "%s", on_what);
                }
                status_printf(s, color(WT_STATUS_HEADER, s), "");
                status_printf_more(s, branch_status_color, "%s", on_what);
@@ -1076,6 +1156,7 @@ void wt_status_print(struct wt_status *s)
        wt_status_print_state(s, &state);
        free(state.branch);
        free(state.onto);
        wt_status_print_state(s, &state);
        free(state.branch);
        free(state.onto);
+       free(state.detached_from);
 
        if (s->is_initial) {
                status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
 
        if (s->is_initial) {
                status_printf_ln(s, color(WT_STATUS_HEADER, s), "");