Merge branch 'js/branch-symref'
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2009 23:41:35 +0000 (15:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2009 23:41:35 +0000 (15:41 -0800)
* js/branch-symref:
add basic branch display tests
branch: clean up repeated strlen
Avoid segfault with 'git branch' when the HEAD is detached
builtin-branch: improve output when displaying remote branches

Conflicts:
builtin-branch.c

1  2 
builtin-branch.c
diff --combined builtin-branch.c
index 60f70acd144f95c9502f5d8c370fea20d1d6b010,b15d3517f338477e6a7014cf4373914e2c962d64..14d4b917e5bb929f40efd3fc5e010cec5d3507de
@@@ -32,18 -32,18 +32,18 @@@ static unsigned char head_sha1[20]
  
  static int branch_use_color = -1;
  static char branch_colors[][COLOR_MAXLEN] = {
 -      "\033[m",       /* reset */
 -      "",             /* PLAIN (normal) */
 -      "\033[31m",     /* REMOTE (red) */
 -      "",             /* LOCAL (normal) */
 -      "\033[32m",     /* CURRENT (green) */
 +      GIT_COLOR_RESET,
 +      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_RED,          /* REMOTE */
 +      GIT_COLOR_NORMAL,       /* LOCAL */
 +      GIT_COLOR_GREEN,        /* CURRENT */
  };
  enum color_branch {
 -      COLOR_BRANCH_RESET = 0,
 -      COLOR_BRANCH_PLAIN = 1,
 -      COLOR_BRANCH_REMOTE = 2,
 -      COLOR_BRANCH_LOCAL = 3,
 -      COLOR_BRANCH_CURRENT = 4,
 +      BRANCH_COLOR_RESET = 0,
 +      BRANCH_COLOR_PLAIN = 1,
 +      BRANCH_COLOR_REMOTE = 2,
 +      BRANCH_COLOR_LOCAL = 3,
 +      BRANCH_COLOR_CURRENT = 4,
  };
  
  static enum merge_filter {
@@@ -56,15 -56,15 +56,15 @@@ static unsigned char merge_filter_ref[2
  static int parse_branch_color_slot(const char *var, int ofs)
  {
        if (!strcasecmp(var+ofs, "plain"))
 -              return COLOR_BRANCH_PLAIN;
 +              return BRANCH_COLOR_PLAIN;
        if (!strcasecmp(var+ofs, "reset"))
 -              return COLOR_BRANCH_RESET;
 +              return BRANCH_COLOR_RESET;
        if (!strcasecmp(var+ofs, "remote"))
 -              return COLOR_BRANCH_REMOTE;
 +              return BRANCH_COLOR_REMOTE;
        if (!strcasecmp(var+ofs, "local"))
 -              return COLOR_BRANCH_LOCAL;
 +              return BRANCH_COLOR_LOCAL;
        if (!strcasecmp(var+ofs, "current"))
 -              return COLOR_BRANCH_CURRENT;
 +              return BRANCH_COLOR_CURRENT;
        die("bad config variable '%s'", var);
  }
  
@@@ -99,7 -99,6 +99,7 @@@ static int delete_branches(int argc, co
        const char *fmt, *remote;
        int i;
        int ret = 0;
 +      struct strbuf bname = STRBUF_INIT;
  
        switch (kinds) {
        case REF_REMOTE_BRANCH:
                if (!head_rev)
                        die("Couldn't look up commit object for HEAD");
        }
 -      for (i = 0; i < argc; i++) {
 -              if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) {
 +      for (i = 0; i < argc; i++, strbuf_release(&bname)) {
 +              int len = strlen(argv[i]);
 +
 +              if (interpret_nth_last_branch(argv[i], &bname) != len)
 +                      strbuf_add(&bname, argv[i], len);
 +
 +              if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
                        error("Cannot delete the branch '%s' "
 -                              "which you are currently on.", argv[i]);
 +                            "which you are currently on.", bname.buf);
                        ret = 1;
                        continue;
                }
  
                free(name);
  
 -              name = xstrdup(mkpath(fmt, argv[i]));
 +              name = xstrdup(mkpath(fmt, bname.buf));
                if (!resolve_ref(name, sha1, 1, NULL)) {
                        error("%sbranch '%s' not found.",
 -                                      remote, argv[i]);
 +                                      remote, bname.buf);
                        ret = 1;
                        continue;
                }
                if (!force &&
                    !in_merge_bases(rev, &head_rev, 1)) {
                        error("The branch '%s' is not an ancestor of "
 -                              "your current HEAD.\n"
 -                              "If you are sure you want to delete it, "
 -                              "run 'git branch -D %s'.", argv[i], argv[i]);
 +                            "your current HEAD.\n"
 +                            "If you are sure you want to delete it, "
 +                            "run 'git branch -D %s'.", bname.buf, bname.buf);
                        ret = 1;
                        continue;
                }
  
                if (delete_ref(name, sha1, 0)) {
                        error("Error deleting %sbranch '%s'", remote,
 -                             argv[i]);
 +                            bname.buf);
                        ret = 1;
                } else {
                        struct strbuf buf = STRBUF_INIT;
 -                      printf("Deleted %sbranch %s (%s).\n", remote, argv[i],
 -                              find_unique_abbrev(sha1, DEFAULT_ABBREV));
 -                      strbuf_addf(&buf, "branch.%s", argv[i]);
 +                      printf("Deleted %sbranch %s (%s).\n", remote,
 +                             bname.buf,
 +                             find_unique_abbrev(sha1, DEFAULT_ABBREV));
 +                      strbuf_addf(&buf, "branch.%s", bname.buf);
                        if (git_config_rename_section(buf.buf, NULL) < 0)
                                warning("Update of config-file failed");
                        strbuf_release(&buf);
  
  struct ref_item {
        char *name;
-       unsigned int kind;
+       char *dest;
+       unsigned int kind, len;
        struct commit *commit;
  };
  
@@@ -200,22 -194,47 +201,47 @@@ struct ref_list 
        int kinds;
  };
  
+ static char *resolve_symref(const char *src, const char *prefix)
+ {
+       unsigned char sha1[20];
+       int flag;
+       const char *dst, *cp;
+       dst = resolve_ref(src, sha1, 0, &flag);
+       if (!(dst && (flag & REF_ISSYMREF)))
+               return NULL;
+       if (prefix && (cp = skip_prefix(dst, prefix)))
+               dst = cp;
+       return xstrdup(dst);
+ }
  static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
  {
        struct ref_list *ref_list = (struct ref_list*)(cb_data);
        struct ref_item *newitem;
        struct commit *commit;
-       int kind;
-       int len;
+       int kind, i;
+       const char *prefix, *orig_refname = refname;
+       static struct {
+               int kind;
+               const char *prefix;
+               int pfxlen;
+       } ref_kind[] = {
+               { REF_LOCAL_BRANCH, "refs/heads/", 11 },
+               { REF_REMOTE_BRANCH, "refs/remotes/", 13 },
+       };
  
        /* Detect kind */
-       if (!prefixcmp(refname, "refs/heads/")) {
-               kind = REF_LOCAL_BRANCH;
-               refname += 11;
-       } else if (!prefixcmp(refname, "refs/remotes/")) {
-               kind = REF_REMOTE_BRANCH;
-               refname += 13;
-       } else
+       for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+               prefix = ref_kind[i].prefix;
+               if (strncmp(refname, prefix, ref_kind[i].pfxlen))
+                       continue;
+               kind = ref_kind[i].kind;
+               refname += ref_kind[i].pfxlen;
+               break;
+       }
+       if (ARRAY_SIZE(ref_kind) <= i)
                return 0;
  
        commit = lookup_commit_reference_gently(sha1, 1);
        newitem->name = xstrdup(refname);
        newitem->kind = kind;
        newitem->commit = commit;
-       len = strlen(newitem->name);
-       if (len > ref_list->maxwidth)
-               ref_list->maxwidth = len;
+       newitem->len = strlen(refname);
+       newitem->dest = resolve_symref(orig_refname, prefix);
+       /* adjust for "remotes/" */
+       if (newitem->kind == REF_REMOTE_BRANCH &&
+           ref_list->kinds != REF_REMOTE_BRANCH)
+               newitem->len += 8;
+       if (newitem->len > ref_list->maxwidth)
+               ref_list->maxwidth = newitem->len;
  
        return 0;
  }
@@@ -257,8 -281,10 +288,10 @@@ static void free_ref_list(struct ref_li
  {
        int i;
  
-       for (i = 0; i < ref_list->index; i++)
+       for (i = 0; i < ref_list->index; i++) {
                free(ref_list->list[i].name);
+               free(ref_list->list[i].dest);
+       }
        free(ref_list->list);
  }
  
@@@ -299,34 -325,46 +332,46 @@@ static int matches_merge_filter(struct 
  }
  
  static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
-                          int abbrev, int current)
+                          int abbrev, int current, char *prefix)
  {
        char c;
        int color;
        struct commit *commit = item->commit;
+       struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
  
        if (!matches_merge_filter(commit))
                return;
  
        switch (item->kind) {
        case REF_LOCAL_BRANCH:
 -              color = COLOR_BRANCH_LOCAL;
 +              color = BRANCH_COLOR_LOCAL;
                break;
        case REF_REMOTE_BRANCH:
 -              color = COLOR_BRANCH_REMOTE;
 +              color = BRANCH_COLOR_REMOTE;
                break;
        default:
 -              color = COLOR_BRANCH_PLAIN;
 +              color = BRANCH_COLOR_PLAIN;
                break;
        }
  
        c = ' ';
        if (current) {
                c = '*';
 -              color = COLOR_BRANCH_CURRENT;
 +              color = BRANCH_COLOR_CURRENT;
        }
  
-       if (verbose) {
+       strbuf_addf(&name, "%s%s", prefix, item->name);
+       if (verbose)
+               strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
+                           maxwidth, name.buf,
 -                          branch_get_color(COLOR_BRANCH_RESET));
++                          branch_get_color(BRANCH_COLOR_RESET));
+       else
+               strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
 -                          name.buf, branch_get_color(COLOR_BRANCH_RESET));
++                          name.buf, branch_get_color(BRANCH_COLOR_RESET));
+       if (item->dest)
+               strbuf_addf(&out, " -> %s", item->dest);
+       else if (verbose) {
                struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
                const char *sub = " **** invalid ref ****";
  
                if (item->kind == REF_LOCAL_BRANCH)
                        fill_tracking_info(&stat, item->name);
  
-               printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
-                      maxwidth, item->name,
-                      branch_get_color(BRANCH_COLOR_RESET),
-                      find_unique_abbrev(item->commit->object.sha1, abbrev),
-                      stat.buf, sub);
+               strbuf_addf(&out, " %s %s%s",
+                       find_unique_abbrev(item->commit->object.sha1, abbrev),
+                       stat.buf, sub);
                strbuf_release(&stat);
                strbuf_release(&subject);
-       } else {
-               printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
-                      branch_get_color(BRANCH_COLOR_RESET));
        }
+       printf("%s\n", out.buf);
+       strbuf_release(&name);
+       strbuf_release(&out);
  }
  
  static int calc_maxwidth(struct ref_list *refs)
  {
-       int i, l, w = 0;
+       int i, w = 0;
        for (i = 0; i < refs->index; i++) {
                if (!matches_merge_filter(refs->list[i].commit))
                        continue;
-               l = strlen(refs->list[i].name);
-               if (l > w)
-                       w = l;
+               if (refs->list[i].len > w)
+                       w = refs->list[i].len;
        }
        return w;
  }
@@@ -397,11 -432,13 +439,13 @@@ static void print_ref_list(int kinds, i
            is_descendant_of(head_commit, with_commit)) {
                struct ref_item item;
                item.name = xstrdup("(no branch)");
+               item.len = strlen(item.name);
                item.kind = REF_LOCAL_BRANCH;
+               item.dest = NULL;
                item.commit = head_commit;
-               if (strlen(item.name) > ref_list.maxwidth)
-                       ref_list.maxwidth = strlen(item.name);
-               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+               if (item.len > ref_list.maxwidth)
+                       ref_list.maxwidth = item.len;
+               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
                free(item.name);
        }
  
                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);
+                              abbrev, current, prefix);
        }
  
        free_ref_list(&ref_list);