]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/branch.c
branch: roll show_detached HEAD into regular ref_list
[thirdparty/git.git] / builtin / branch.c
index b42e5b6dbc76016ca18e5ddd7ded2af613013eb7..a2a35f414636f69701f7d518652c2ce60213cf3d 100644 (file)
@@ -28,8 +28,9 @@ static const char * const builtin_branch_usage[] = {
        NULL
 };
 
-#define REF_LOCAL_BRANCH    0x01
-#define REF_REMOTE_BRANCH   0x02
+#define REF_DETACHED_HEAD   0x01
+#define REF_LOCAL_BRANCH    0x02
+#define REF_REMOTE_BRANCH   0x04
 
 static const char *head;
 static unsigned char head_sha1[20];
@@ -160,7 +161,7 @@ static int branch_merged(int kind, const char *name,
 }
 
 static int check_branch_commit(const char *branchname, const char *refname,
-                              unsigned char *sha1, struct commit *head_rev,
+                              const unsigned char *sha1, struct commit *head_rev,
                               int kinds, int force)
 {
        struct commit *rev = lookup_commit_reference(sha1);
@@ -253,7 +254,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        continue;
                }
 
-               if (delete_ref(name, sha1, REF_NODEREF)) {
+               if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
+                              REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
                              : _("Error deleting branch '%s'"),
@@ -281,14 +283,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 struct ref_item {
        char *name;
        char *dest;
-       unsigned int kind, width;
+       unsigned int kind;
        struct commit *commit;
        int ignore;
 };
 
 struct ref_list {
        struct rev_info revs;
-       int index, alloc, maxwidth, verbose, abbrev;
+       int index, alloc, verbose, abbrev;
        struct ref_item *list;
        struct commit_list *with_commit;
        int kinds;
@@ -351,8 +353,12 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag
                        break;
                }
        }
-       if (ARRAY_SIZE(ref_kind) <= i)
-               return 0;
+       if (ARRAY_SIZE(ref_kind) <= i) {
+               if (!strcmp(refname, "HEAD"))
+                       kind = REF_DETACHED_HEAD;
+               else
+                       return 0;
+       }
 
        /* Don't add types the caller doesn't want */
        if ((kind & ref_list->kinds) == 0)
@@ -385,15 +391,8 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag
        newitem->name = xstrdup(refname);
        newitem->kind = kind;
        newitem->commit = commit;
-       newitem->width = utf8_strwidth(refname);
        newitem->dest = resolve_symref(orig_refname, prefix);
        newitem->ignore = 0;
-       /* adjust for "remotes/" */
-       if (newitem->kind == REF_REMOTE_BRANCH &&
-           ref_list->kinds != REF_REMOTE_BRANCH)
-               newitem->width += 8;
-       if (newitem->width > ref_list->maxwidth)
-               ref_list->maxwidth = newitem->width;
 
        return 0;
 }
@@ -503,12 +502,46 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
        strbuf_release(&subject);
 }
 
+static char *get_head_description(void)
+{
+       struct strbuf desc = STRBUF_INIT;
+       struct wt_status_state state;
+       memset(&state, 0, sizeof(state));
+       wt_status_get_state(&state, 1);
+       if (state.rebase_in_progress ||
+           state.rebase_interactive_in_progress)
+               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+                           state.branch);
+       else if (state.bisect_in_progress)
+               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+                           state.branch);
+       else if (state.detached_from) {
+               /* TRANSLATORS: make sure these match _("HEAD detached at ")
+                  and _("HEAD detached from ") in wt-status.c */
+               if (state.detached_at)
+                       strbuf_addf(&desc, _("(HEAD detached at %s)"),
+                               state.detached_from);
+               else
+                       strbuf_addf(&desc, _("(HEAD detached from %s)"),
+                               state.detached_from);
+       }
+       else
+               strbuf_addstr(&desc, _("(no branch)"));
+       free(state.branch);
+       free(state.onto);
+       free(state.detached_from);
+       return strbuf_detach(&desc, NULL);
+}
+
 static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
-                          int abbrev, int current, char *prefix)
+                          int abbrev, int current, const char *remote_prefix)
 {
        char c;
        int color;
        struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
+       const char *prefix = "";
+       const char *desc = item->name;
+       char *to_free = NULL;
 
        if (item->ignore)
                return;
@@ -519,6 +552,11 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                break;
        case REF_REMOTE_BRANCH:
                color = BRANCH_COLOR_REMOTE;
+               prefix = remote_prefix;
+               break;
+       case REF_DETACHED_HEAD:
+               color = BRANCH_COLOR_CURRENT;
+               desc = to_free = get_head_description();
                break;
        default:
                color = BRANCH_COLOR_PLAIN;
@@ -531,7 +569,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                color = BRANCH_COLOR_CURRENT;
        }
 
-       strbuf_addf(&name, "%s%s", prefix, item->name);
+       strbuf_addf(&name, "%s%s", prefix, desc);
        if (verbose) {
                int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
                strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
@@ -554,68 +592,25 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
        strbuf_release(&name);
        strbuf_release(&out);
+       free(to_free);
 }
 
-static int calc_maxwidth(struct ref_list *refs)
+static int calc_maxwidth(struct ref_list *refs, int remote_bonus)
 {
-       int i, w = 0;
+       int i, max = 0;
        for (i = 0; i < refs->index; i++) {
-               if (refs->list[i].ignore)
-                       continue;
-               if (refs->list[i].width > w)
-                       w = refs->list[i].width;
-       }
-       return w;
-}
-
-static char *get_head_description(void)
-{
-       struct strbuf desc = STRBUF_INIT;
-       struct wt_status_state state;
-       memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state, 1);
-       if (state.rebase_in_progress ||
-           state.rebase_interactive_in_progress)
-               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
-                           state.branch);
-       else if (state.bisect_in_progress)
-               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
-                           state.branch);
-       else if (state.detached_from) {
-               /* TRANSLATORS: make sure these match _("HEAD detached at ")
-                  and _("HEAD detached from ") in wt-status.c */
-               if (state.detached_at)
-                       strbuf_addf(&desc, _("(HEAD detached at %s)"),
-                               state.detached_from);
-               else
-                       strbuf_addf(&desc, _("(HEAD detached from %s)"),
-                               state.detached_from);
-       }
-       else
-               strbuf_addstr(&desc, _("(no branch)"));
-       free(state.branch);
-       free(state.onto);
-       free(state.detached_from);
-       return strbuf_detach(&desc, NULL);
-}
+               struct ref_item *it = &refs->list[i];
+               int w;
 
-static void show_detached(struct ref_list *ref_list)
-{
-       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
-
-       if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
-               struct ref_item item;
-               item.name = get_head_description();
-               item.width = utf8_strwidth(item.name);
-               item.kind = REF_LOCAL_BRANCH;
-               item.dest = NULL;
-               item.commit = head_commit;
-               item.ignore = 0;
-               if (item.width > ref_list->maxwidth)
-                       ref_list->maxwidth = item.width;
-               print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
-               free(item.name);
+               if (it->ignore)
+                       continue;
+               w = utf8_strwidth(it->name);
+               if (it->kind == REF_REMOTE_BRANCH)
+                       w += remote_bonus;
+               if (w > max)
+                       max = w;
        }
+       return max;
 }
 
 static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
@@ -623,6 +618,16 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
        int i;
        struct append_ref_cb cb;
        struct ref_list ref_list;
+       int maxwidth = 0;
+       const char *remote_prefix = "";
+
+       /*
+        * If we are listing more than just remote branches,
+        * then remote branches will have a "remotes/" prefix.
+        * We need to account for this in the width.
+        */
+       if (kinds != REF_REMOTE_BRANCH)
+               remote_prefix = "remotes/";
 
        memset(&ref_list, 0, sizeof(ref_list));
        ref_list.kinds = kinds;
@@ -634,7 +639,18 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
        cb.ref_list = &ref_list;
        cb.pattern = pattern;
        cb.ret = 0;
+       /*
+        * First we obtain all regular branch refs and if the HEAD is
+        * detached then we insert that ref to the end of the ref_fist
+        * so that it can be printed and removed first.
+        */
        for_each_rawref(append_ref, &cb);
+       if (detached)
+               head_ref(append_ref, &cb);
+       /*
+        * The following implementation is currently duplicated in ref-filter. It
+        * will eventually be removed when we port branch.c to use ref-filter APIs.
+        */
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -662,26 +678,20 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
                        clear_commit_marks(item->commit, ALL_REV_FLAGS);
                }
                clear_commit_marks(filter, ALL_REV_FLAGS);
-
-               if (verbose)
-                       ref_list.maxwidth = calc_maxwidth(&ref_list);
        }
+       if (verbose)
+               maxwidth = calc_maxwidth(&ref_list, strlen(remote_prefix));
 
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
-       detached = (detached && (kinds & REF_LOCAL_BRANCH));
-       if (detached && match_patterns(pattern, "HEAD"))
-               show_detached(&ref_list);
-
        for (i = 0; i < ref_list.index; i++) {
-               int current = !detached &&
-                       (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+               int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
                        !strcmp(ref_list.list[i].name, head);
-               char *prefix = (kinds != REF_REMOTE_BRANCH &&
-                               ref_list.list[i].kind == REF_REMOTE_BRANCH)
-                               ? "remotes/" : "";
-               print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
-                              abbrev, current, prefix);
+               /*  If detached the first ref_item is the current ref */
+               if (detached && i == 0)
+                       current = 1;
+               print_ref_item(&ref_list.list[i], maxwidth, verbose,
+                              abbrev, current, remote_prefix);
        }
 
        free_ref_list(&ref_list);
@@ -745,6 +755,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        strbuf_release(&newsection);
 }
 
+/*
+ * This function is duplicated in ref-filter. It will eventually be removed
+ * when we port branch.c to use ref-filter APIs.
+ */
 static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
 {
        merge_filter = ((opt->long_name[0] == 'n')
@@ -820,18 +834,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &kinds, N_("act on remote-tracking branches"),
                        REF_REMOTE_BRANCH),
-               {
-                       OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
-                       N_("print only branches that contain the commit"),
-                       PARSE_OPT_LASTARG_DEFAULT,
-                       parse_opt_with_commit, (intptr_t)"HEAD",
-               },
-               {
-                       OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
-                       N_("print only branches that contain the commit"),
-                       PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
-                       parse_opt_with_commit, (intptr_t) "HEAD",
-               },
+               OPT_CONTAINS(&with_commit, N_("print only branches that contain the commit")),
+               OPT_WITH(&with_commit, N_("print only branches that contain the commit")),
                OPT__ABBREV(&abbrev),
 
                OPT_GROUP(N_("Specific git-branch actions:")),
@@ -911,7 +915,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        die(_("branch name required"));
                return delete_branches(argc, argv, delete > 1, kinds, quiet);
        } else if (list) {
-               int ret = print_ref_list(kinds, detached, verbose, abbrev,
+               int ret;
+               /*  git branch --local also shows HEAD when it is detached */
+               if (kinds & REF_LOCAL_BRANCH)
+                       kinds |= REF_DETACHED_HEAD;
+               ret = print_ref_list(kinds, detached, verbose, abbrev,
                                         with_commit, argv);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);