{"thread":{"id":"4260","subject":"[PATCH 7/8] Builtin git-show-branch.","startedAt":"2006-05-23T08:31:19Z","lastAt":"2006-05-23T08:31:19Z","messageCount":1,"participants":["Peter Eriksen"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"20527","messageId":"27591.817189432$1148373117@news.gmane.org","threadId":"4260","inReplyTo":"1148373081381-git-send-email-","subject":"[PATCH 7/8] Builtin git-show-branch.","fromName":"Peter Eriksen","fromEmail":"s022018@student.dtu.dk","sentAt":"2006-05-23T08:31:19Z","receivedAt":"2006-05-23T08:31:19Z","isPatch":true,"sender":{"key":"s022018@student.dtu.dk","avatar":null},"body":"From: Peter Eriksen <s022018@student.dtu.dk>\n\nSigned-off-by: Peter Eriksen <s022018@student.dtu.dk>\n\n\n---\n\nd0d1cfb7985fa90442e423a55ca23e6bc83f912d\n Makefile              |    6 \n builtin-show-branch.c |  789 +++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h             |    1 \n git.c                 |    3 \n show-branch.c         |  788 -------------------------------------------------\n 5 files changed, 795 insertions(+), 792 deletions(-)\n create mode 100644 builtin-show-branch.c\n delete mode 100644 show-branch.c\n\nd0d1cfb7985fa90442e423a55ca23e6bc83f912d\ndiff --git a/Makefile b/Makefile\nindex eeb4fdb..b438a90 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -159,7 +159,7 @@ PROGRAMS = \\\n \tgit-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \\\n \tgit-peek-remote$X git-prune-packed$X \\\n \tgit-receive-pack$X git-rev-parse$X \\\n-\tgit-send-pack$X git-show-branch$X git-shell$X \\\n+\tgit-send-pack$X git-shell$X \\\n \tgit-show-index$X git-ssh-fetch$X \\\n \tgit-ssh-upload$X git-unpack-file$X \\\n \tgit-unpack-objects$X git-update-index$X git-update-server-info$X \\\n@@ -173,7 +173,7 @@ BUILT_INS = git-log$X git-whatchanged$X \n \tgit-grep$X git-rev-list$X git-check-ref-format$X \\\n \tgit-init-db$X git-ls-files$X git-ls-tree$X \\\n \tgit-tar-tree$X git-read-tree$X git-commit-tree$X \\\n-\tgit-apply$X \n+\tgit-apply$X git-show-branch$X\n \n # what 'all' will build and 'install' will install, in gitexecdir\n ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)\n@@ -224,7 +224,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-grep.o builtin-rev-list.o builtin-check-ref-format.o \\\n \tbuiltin-init-db.o builtin-ls-files.o builtin-ls-tree.o \\\n         builtin-tar-tree.o builtin-read-tree.o builtin-commit-tree.o \\\n-\tbuiltin-apply.o\n+\tbuiltin-apply.o builtin-show-branch.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-show-branch.c b/builtin-show-branch.c\nnew file mode 100644\nindex 0000000..d1be8bb\n--- /dev/null\n+++ b/builtin-show-branch.c\n@@ -0,0 +1,789 @@\n+#include <stdlib.h>\n+#include <fnmatch.h>\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"refs.h\"\n+#include \"builtin.h\"\n+\n+static const char show_branch_usage[] =\n+\"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]\";\n+\n+static int default_num = 0;\n+static int default_alloc = 0;\n+static char **default_arg = NULL;\n+\n+#define UNINTERESTING\t01\n+\n+#define REV_SHIFT\t 2\n+#define MAX_REVS\t29 /* should not exceed bits_per_int - REV_SHIFT */\n+\n+static struct commit *interesting(struct commit_list *list)\n+{\n+\twhile (list) {\n+\t\tstruct commit *commit = list->item;\n+\t\tlist = list->next;\n+\t\tif (commit->object.flags & UNINTERESTING)\n+\t\t\tcontinue;\n+\t\treturn commit;\n+\t}\n+\treturn NULL;\n+}\n+\n+static struct commit *pop_one_commit(struct commit_list **list_p)\n+{\n+\tstruct commit *commit;\n+\tstruct commit_list *list;\n+\tlist = *list_p;\n+\tcommit = list->item;\n+\t*list_p = list->next;\n+\tfree(list);\n+\treturn commit;\n+}\n+\n+struct commit_name {\n+\tconst char *head_name; /* which head's ancestor? */\n+\tint generation; /* how many parents away from head_name */\n+};\n+\n+/* Name the commit as nth generation ancestor of head_name;\n+ * we count only the first-parent relationship for naming purposes.\n+ */\n+static void name_commit(struct commit *commit, const char *head_name, int nth)\n+{\n+\tstruct commit_name *name;\n+\tif (!commit->object.util)\n+\t\tcommit->object.util = xmalloc(sizeof(struct commit_name));\n+\tname = commit->object.util;\n+\tname->head_name = head_name;\n+\tname->generation = nth;\n+}\n+\n+/* Parent is the first parent of the commit.  We may name it\n+ * as (n+1)th generation ancestor of the same head_name as\n+ * commit is nth generation ancestor of, if that generation\n+ * number is better than the name it already has.\n+ */\n+static void name_parent(struct commit *commit, struct commit *parent)\n+{\n+\tstruct commit_name *commit_name = commit->object.util;\n+\tstruct commit_name *parent_name = parent->object.util;\n+\tif (!commit_name)\n+\t\treturn;\n+\tif (!parent_name ||\n+\t    commit_name->generation + 1 < parent_name->generation)\n+\t\tname_commit(parent, commit_name->head_name,\n+\t\t\t    commit_name->generation + 1);\n+}\n+\n+static int name_first_parent_chain(struct commit *c)\n+{\n+\tint i = 0;\n+\twhile (c) {\n+\t\tstruct commit *p;\n+\t\tif (!c->object.util)\n+\t\t\tbreak;\n+\t\tif (!c->parents)\n+\t\t\tbreak;\n+\t\tp = c->parents->item;\n+\t\tif (!p->object.util) {\n+\t\t\tname_parent(c, p);\n+\t\t\ti++;\n+\t\t}\n+\t\tc = p;\n+\t}\n+\treturn i;\n+}\n+\n+static void name_commits(struct commit_list *list,\n+\t\t\t struct commit **rev,\n+\t\t\t char **ref_name,\n+\t\t\t int num_rev)\n+{\n+\tstruct commit_list *cl;\n+\tstruct commit *c;\n+\tint i;\n+\n+\t/* First give names to the given heads */\n+\tfor (cl = list; cl; cl = cl->next) {\n+\t\tc = cl->item;\n+\t\tif (c->object.util)\n+\t\t\tcontinue;\n+\t\tfor (i = 0; i < num_rev; i++) {\n+\t\t\tif (rev[i] == c) {\n+\t\t\t\tname_commit(c, ref_name[i], 0);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Then commits on the first parent ancestry chain */\n+\tdo {\n+\t\ti = 0;\n+\t\tfor (cl = list; cl; cl = cl->next) {\n+\t\t\ti += name_first_parent_chain(cl->item);\n+\t\t}\n+\t} while (i);\n+\n+\t/* Finally, any unnamed commits */\n+\tdo {\n+\t\ti = 0;\n+\t\tfor (cl = list; cl; cl = cl->next) {\n+\t\t\tstruct commit_list *parents;\n+\t\t\tstruct commit_name *n;\n+\t\t\tint nth;\n+\t\t\tc = cl->item;\n+\t\t\tif (!c->object.util)\n+\t\t\t\tcontinue;\n+\t\t\tn = c->object.util;\n+\t\t\tparents = c->parents;\n+\t\t\tnth = 0;\n+\t\t\twhile (parents) {\n+\t\t\t\tstruct commit *p = parents->item;\n+\t\t\t\tchar newname[1000], *en;\n+\t\t\t\tparents = parents->next;\n+\t\t\t\tnth++;\n+\t\t\t\tif (p->object.util)\n+\t\t\t\t\tcontinue;\n+\t\t\t\ten = newname;\n+\t\t\t\tswitch (n->generation) {\n+\t\t\t\tcase 0:\n+\t\t\t\t\ten += sprintf(en, \"%s\", n->head_name);\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 1:\n+\t\t\t\t\ten += sprintf(en, \"%s^\", n->head_name);\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\ten += sprintf(en, \"%s~%d\",\n+\t\t\t\t\t\tn->head_name, n->generation);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (nth == 1)\n+\t\t\t\t\ten += sprintf(en, \"^\");\n+\t\t\t\telse\n+\t\t\t\t\ten += sprintf(en, \"^%d\", nth);\n+\t\t\t\tname_commit(p, strdup(newname), 0);\n+\t\t\t\ti++;\n+\t\t\t\tname_first_parent_chain(p);\n+\t\t\t}\n+\t\t}\n+\t} while (i);\n+}\n+\n+static int mark_seen(struct commit *commit, struct commit_list **seen_p)\n+{\n+\tif (!commit->object.flags) {\n+\t\tinsert_by_date(commit, seen_p);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static void join_revs(struct commit_list **list_p,\n+\t\t      struct commit_list **seen_p,\n+\t\t      int num_rev, int extra)\n+{\n+\tint all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n+\tint all_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n+\n+\twhile (*list_p) {\n+\t\tstruct commit_list *parents;\n+\t\tint still_interesting = !!interesting(*list_p);\n+\t\tstruct commit *commit = pop_one_commit(list_p);\n+\t\tint flags = commit->object.flags & all_mask;\n+\n+\t\tif (!still_interesting && extra <= 0)\n+\t\t\tbreak;\n+\n+\t\tmark_seen(commit, seen_p);\n+\t\tif ((flags & all_revs) == all_revs)\n+\t\t\tflags |= UNINTERESTING;\n+\t\tparents = commit->parents;\n+\n+\t\twhile (parents) {\n+\t\t\tstruct commit *p = parents->item;\n+\t\t\tint this_flag = p->object.flags;\n+\t\t\tparents = parents->next;\n+\t\t\tif ((this_flag & flags) == flags)\n+\t\t\t\tcontinue;\n+\t\t\tif (!p->object.parsed)\n+\t\t\t\tparse_commit(p);\n+\t\t\tif (mark_seen(p, seen_p) && !still_interesting)\n+\t\t\t\textra--;\n+\t\t\tp->object.flags |= flags;\n+\t\t\tinsert_by_date(p, list_p);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Postprocess to complete well-poisoning.\n+\t *\n+\t * At this point we have all the commits we have seen in\n+\t * seen_p list (which happens to be sorted chronologically but\n+\t * it does not really matter).  Mark anything that can be\n+\t * reached from uninteresting commits not interesting.\n+\t */\n+\tfor (;;) {\n+\t\tint changed = 0;\n+\t\tstruct commit_list *s;\n+\t\tfor (s = *seen_p; s; s = s->next) {\n+\t\t\tstruct commit *c = s->item;\n+\t\t\tstruct commit_list *parents;\n+\n+\t\t\tif (((c->object.flags & all_revs) != all_revs) &&\n+\t\t\t    !(c->object.flags & UNINTERESTING))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* The current commit is either a merge base or\n+\t\t\t * already uninteresting one.  Mark its parents\n+\t\t\t * as uninteresting commits _only_ if they are\n+\t\t\t * already parsed.  No reason to find new ones\n+\t\t\t * here.\n+\t\t\t */\n+\t\t\tparents = c->parents;\n+\t\t\twhile (parents) {\n+\t\t\t\tstruct commit *p = parents->item;\n+\t\t\t\tparents = parents->next;\n+\t\t\t\tif (!(p->object.flags & UNINTERESTING)) {\n+\t\t\t\t\tp->object.flags |= UNINTERESTING;\n+\t\t\t\t\tchanged = 1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (!changed)\n+\t\t\tbreak;\n+\t}\n+}\n+\n+static void show_one_commit(struct commit *commit, int no_name)\n+{\n+\tchar pretty[256], *cp;\n+\tstruct commit_name *name = commit->object.util;\n+\tif (commit->object.parsed)\n+\t\tpretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,\n+\t\t\t\t    pretty, sizeof(pretty), 0);\n+\telse\n+\t\tstrcpy(pretty, \"(unavailable)\");\n+\tif (!strncmp(pretty, \"[PATCH] \", 8))\n+\t\tcp = pretty + 8;\n+\telse\n+\t\tcp = pretty;\n+\n+\tif (!no_name) {\n+\t\tif (name && name->head_name) {\n+\t\t\tprintf(\"[%s\", name->head_name);\n+\t\t\tif (name->generation) {\n+\t\t\t\tif (name->generation == 1)\n+\t\t\t\t\tprintf(\"^\");\n+\t\t\t\telse\n+\t\t\t\t\tprintf(\"~%d\", name->generation);\n+\t\t\t}\n+\t\t\tprintf(\"] \");\n+\t\t}\n+\t\telse\n+\t\t\tprintf(\"[%s] \",\n+\t\t\t       find_unique_abbrev(commit->object.sha1, 7));\n+\t}\n+\tputs(cp);\n+}\n+\n+static char *ref_name[MAX_REVS + 1];\n+static int ref_name_cnt;\n+\n+static const char *find_digit_prefix(const char *s, int *v)\n+{\n+\tconst char *p;\n+\tint ver;\n+\tchar ch;\n+\n+\tfor (p = s, ver = 0;\n+\t     '0' <= (ch = *p) && ch <= '9';\n+\t     p++)\n+\t\tver = ver * 10 + ch - '0';\n+\t*v = ver;\n+\treturn p;\n+}\n+\n+\n+static int version_cmp(const char *a, const char *b)\n+{\n+\twhile (1) {\n+\t\tint va, vb;\n+\n+\t\ta = find_digit_prefix(a, &va);\n+\t\tb = find_digit_prefix(b, &vb);\n+\t\tif (va != vb)\n+\t\t\treturn va - vb;\n+\n+\t\twhile (1) {\n+\t\t\tint ca = *a;\n+\t\t\tint cb = *b;\n+\t\t\tif ('0' <= ca && ca <= '9')\n+\t\t\t\tca = 0;\n+\t\t\tif ('0' <= cb && cb <= '9')\n+\t\t\t\tcb = 0;\n+\t\t\tif (ca != cb)\n+\t\t\t\treturn ca - cb;\n+\t\t\tif (!ca)\n+\t\t\t\tbreak;\n+\t\t\ta++;\n+\t\t\tb++;\n+\t\t}\n+\t\tif (!*a && !*b)\n+\t\t\treturn 0;\n+\t}\n+}\n+\n+static int compare_ref_name(const void *a_, const void *b_)\n+{\n+\tconst char * const*a = a_, * const*b = b_;\n+\treturn version_cmp(*a, *b);\n+}\n+\n+static void sort_ref_range(int bottom, int top)\n+{\n+\tqsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),\n+\t      compare_ref_name);\n+}\n+\n+static int append_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n+\tint i;\n+\n+\tif (!commit)\n+\t\treturn 0;\n+\t/* Avoid adding the same thing twice */\n+\tfor (i = 0; i < ref_name_cnt; i++)\n+\t\tif (!strcmp(refname, ref_name[i]))\n+\t\t\treturn 0;\n+\n+\tif (MAX_REVS <= ref_name_cnt) {\n+\t\tfprintf(stderr, \"warning: ignoring %s; \"\n+\t\t\t\"cannot handle more than %d refs\\n\",\n+\t\t\trefname, MAX_REVS);\n+\t\treturn 0;\n+\t}\n+\tref_name[ref_name_cnt++] = strdup(refname);\n+\tref_name[ref_name_cnt] = NULL;\n+\treturn 0;\n+}\n+\n+static int append_head_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tunsigned char tmp[20];\n+\tint ofs = 11;\n+\tif (strncmp(refname, \"refs/heads/\", ofs))\n+\t\treturn 0;\n+\t/* If both heads/foo and tags/foo exists, get_sha1 would\n+\t * get confused.\n+\t */\n+\tif (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))\n+\t\tofs = 5;\n+\treturn append_ref(refname + ofs, sha1);\n+}\n+\n+static int append_tag_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tif (strncmp(refname, \"refs/tags/\", 10))\n+\t\treturn 0;\n+\treturn append_ref(refname + 5, sha1);\n+}\n+\n+static const char *match_ref_pattern = NULL;\n+static int match_ref_slash = 0;\n+static int count_slash(const char *s)\n+{\n+\tint cnt = 0;\n+\twhile (*s)\n+\t\tif (*s++ == '/')\n+\t\t\tcnt++;\n+\treturn cnt;\n+}\n+\n+static int append_matching_ref(const char *refname, const unsigned char *sha1)\n+{\n+\t/* we want to allow pattern hold/<asterisk> to show all\n+\t * branches under refs/heads/hold/, and v0.99.9? to show\n+\t * refs/tags/v0.99.9a and friends.\n+\t */\n+\tconst char *tail;\n+\tint slash = count_slash(refname);\n+\tfor (tail = refname; *tail && match_ref_slash < slash; )\n+\t\tif (*tail++ == '/')\n+\t\t\tslash--;\n+\tif (!*tail)\n+\t\treturn 0;\n+\tif (fnmatch(match_ref_pattern, tail, 0))\n+\t\treturn 0;\n+\tif (!strncmp(\"refs/heads/\", refname, 11))\n+\t\treturn append_head_ref(refname, sha1);\n+\tif (!strncmp(\"refs/tags/\", refname, 10))\n+\t\treturn append_tag_ref(refname, sha1);\n+\treturn append_ref(refname, sha1);\n+}\n+\n+static void snarf_refs(int head, int tag)\n+{\n+\tif (head) {\n+\t\tint orig_cnt = ref_name_cnt;\n+\t\tfor_each_ref(append_head_ref);\n+\t\tsort_ref_range(orig_cnt, ref_name_cnt);\n+\t}\n+\tif (tag) {\n+\t\tint orig_cnt = ref_name_cnt;\n+\t\tfor_each_ref(append_tag_ref);\n+\t\tsort_ref_range(orig_cnt, ref_name_cnt);\n+\t}\n+}\n+\n+static int rev_is_head(char *head_path, int headlen, char *name,\n+\t\t       unsigned char *head_sha1, unsigned char *sha1)\n+{\n+\tint namelen;\n+\tif ((!head_path[0]) ||\n+\t    (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20)))\n+\t\treturn 0;\n+\tnamelen = strlen(name);\n+\tif ((headlen < namelen) ||\n+\t    memcmp(head_path + headlen - namelen, name, namelen))\n+\t\treturn 0;\n+\tif (headlen == namelen ||\n+\t    head_path[headlen - namelen - 1] == '/')\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static int show_merge_base(struct commit_list *seen, int num_rev)\n+{\n+\tint all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n+\tint all_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n+\tint exit_status = 1;\n+\n+\twhile (seen) {\n+\t\tstruct commit *commit = pop_one_commit(&seen);\n+\t\tint flags = commit->object.flags & all_mask;\n+\t\tif (!(flags & UNINTERESTING) &&\n+\t\t    ((flags & all_revs) == all_revs)) {\n+\t\t\tputs(sha1_to_hex(commit->object.sha1));\n+\t\t\texit_status = 0;\n+\t\t\tcommit->object.flags |= UNINTERESTING;\n+\t\t}\n+\t}\n+\treturn exit_status;\n+}\n+\n+static int show_independent(struct commit **rev,\n+\t\t\t    int num_rev,\n+\t\t\t    char **ref_name,\n+\t\t\t    unsigned int *rev_mask)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < num_rev; i++) {\n+\t\tstruct commit *commit = rev[i];\n+\t\tunsigned int flag = rev_mask[i];\n+\n+\t\tif (commit->object.flags == flag)\n+\t\t\tputs(sha1_to_hex(commit->object.sha1));\n+\t\tcommit->object.flags |= UNINTERESTING;\n+\t}\n+\treturn 0;\n+}\n+\n+static void append_one_rev(const char *av)\n+{\n+\tunsigned char revkey[20];\n+\tif (!get_sha1(av, revkey)) {\n+\t\tappend_ref(av, revkey);\n+\t\treturn;\n+\t}\n+\tif (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {\n+\t\t/* glob style match */\n+\t\tint saved_matches = ref_name_cnt;\n+\t\tmatch_ref_pattern = av;\n+\t\tmatch_ref_slash = count_slash(av);\n+\t\tfor_each_ref(append_matching_ref);\n+\t\tif (saved_matches == ref_name_cnt &&\n+\t\t    ref_name_cnt < MAX_REVS)\n+\t\t\terror(\"no matching refs with %s\", av);\n+\t\tif (saved_matches + 1 < ref_name_cnt)\n+\t\t\tsort_ref_range(saved_matches, ref_name_cnt);\n+\t\treturn;\n+\t}\n+\tdie(\"bad sha1 reference %s\", av);\n+}\n+\n+static int git_show_branch_config(const char *var, const char *value)\n+{\n+\tif (!strcmp(var, \"showbranch.default\")) {\n+\t\tif (default_alloc <= default_num + 1) {\n+\t\t\tdefault_alloc = default_alloc * 3 / 2 + 20;\n+\t\t\tdefault_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);\n+\t\t}\n+\t\tdefault_arg[default_num++] = strdup(value);\n+\t\tdefault_arg[default_num] = NULL;\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value);\n+}\n+\n+static int omit_in_dense(struct commit *commit, struct commit **rev, int n)\n+{\n+\t/* If the commit is tip of the named branches, do not\n+\t * omit it.\n+\t * Otherwise, if it is a merge that is reachable from only one\n+\t * tip, it is not that interesting.\n+\t */\n+\tint i, flag, count;\n+\tfor (i = 0; i < n; i++)\n+\t\tif (rev[i] == commit)\n+\t\t\treturn 0;\n+\tflag = commit->object.flags;\n+\tfor (i = count = 0; i < n; i++) {\n+\t\tif (flag & (1u << (i + REV_SHIFT)))\n+\t\t\tcount++;\n+\t}\n+\tif (count == 1)\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+int cmd_show_branch(int ac, const char **av, char **envp)\n+{\n+\tstruct commit *rev[MAX_REVS], *commit;\n+\tstruct commit_list *list = NULL, *seen = NULL;\n+\tunsigned int rev_mask[MAX_REVS];\n+\tint num_rev, i, extra = 0;\n+\tint all_heads = 0, all_tags = 0;\n+\tint all_mask, all_revs;\n+\tint lifo = 1;\n+\tchar head_path[128];\n+\tconst char *head_path_p;\n+\tint head_path_len;\n+\tunsigned char head_sha1[20];\n+\tint merge_base = 0;\n+\tint independent = 0;\n+\tint no_name = 0;\n+\tint sha1_name = 0;\n+\tint shown_merge_point = 0;\n+\tint with_current_branch = 0;\n+\tint head_at = -1;\n+\tint topics = 0;\n+\tint dense = 1;\n+\n+\tsetup_git_directory();\n+\tgit_config(git_show_branch_config);\n+\n+\t/* If nothing is specified, try the default first */\n+\tif (ac == 1 && default_num) {\n+\t\tac = default_num + 1;\n+\t\tav = default_arg - 1; /* ick; we would not address av[0] */\n+\t}\n+\n+\twhile (1 < ac && av[1][0] == '-') {\n+\t\tchar *arg = av[1];\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\tac--; av++;\n+\t\t\tbreak;\n+\t\t}\n+\t\telse if (!strcmp(arg, \"--all\"))\n+\t\t\tall_heads = all_tags = 1;\n+\t\telse if (!strcmp(arg, \"--heads\"))\n+\t\t\tall_heads = 1;\n+\t\telse if (!strcmp(arg, \"--tags\"))\n+\t\t\tall_tags = 1;\n+\t\telse if (!strcmp(arg, \"--more\"))\n+\t\t\textra = 1;\n+\t\telse if (!strcmp(arg, \"--list\"))\n+\t\t\textra = -1;\n+\t\telse if (!strcmp(arg, \"--no-name\"))\n+\t\t\tno_name = 1;\n+\t\telse if (!strcmp(arg, \"--current\"))\n+\t\t\twith_current_branch = 1;\n+\t\telse if (!strcmp(arg, \"--sha1-name\"))\n+\t\t\tsha1_name = 1;\n+\t\telse if (!strncmp(arg, \"--more=\", 7))\n+\t\t\textra = atoi(arg + 7);\n+\t\telse if (!strcmp(arg, \"--merge-base\"))\n+\t\t\tmerge_base = 1;\n+\t\telse if (!strcmp(arg, \"--independent\"))\n+\t\t\tindependent = 1;\n+\t\telse if (!strcmp(arg, \"--topo-order\"))\n+\t\t\tlifo = 1;\n+\t\telse if (!strcmp(arg, \"--topics\"))\n+\t\t\ttopics = 1;\n+\t\telse if (!strcmp(arg, \"--sparse\"))\n+\t\t\tdense = 0;\n+\t\telse if (!strcmp(arg, \"--date-order\"))\n+\t\t\tlifo = 0;\n+\t\telse\n+\t\t\tusage(show_branch_usage);\n+\t\tac--; av++;\n+\t}\n+\tac--; av++;\n+\n+\t/* Only one of these is allowed */\n+\tif (1 < independent + merge_base + (extra != 0))\n+\t\tusage(show_branch_usage);\n+\n+\t/* If nothing is specified, show all branches by default */\n+\tif (ac + all_heads + all_tags == 0)\n+\t\tall_heads = 1;\n+\n+\tif (all_heads + all_tags)\n+\t\tsnarf_refs(all_heads, all_tags);\n+\twhile (0 < ac) {\n+\t\tappend_one_rev(*av);\n+\t\tac--; av++;\n+\t}\n+\n+\thead_path_p = resolve_ref(git_path(\"HEAD\"), head_sha1, 1);\n+\tif (head_path_p) {\n+\t\thead_path_len = strlen(head_path_p);\n+\t\tmemcpy(head_path, head_path_p, head_path_len + 1);\n+\t}\n+\telse {\n+\t\thead_path_len = 0;\n+\t\thead_path[0] = 0;\n+\t}\n+\n+\tif (with_current_branch && head_path_p) {\n+\t\tint has_head = 0;\n+\t\tfor (i = 0; !has_head && i < ref_name_cnt; i++) {\n+\t\t\t/* We are only interested in adding the branch\n+\t\t\t * HEAD points at.\n+\t\t\t */\n+\t\t\tif (rev_is_head(head_path,\n+\t\t\t\t\thead_path_len,\n+\t\t\t\t\tref_name[i],\n+\t\t\t\t\thead_sha1, NULL))\n+\t\t\t\thas_head++;\n+\t\t}\n+\t\tif (!has_head) {\n+\t\t\tint pfxlen = strlen(git_path(\"refs/heads/\"));\n+\t\t\tappend_one_rev(head_path + pfxlen);\n+\t\t}\n+\t}\n+\n+\tif (!ref_name_cnt) {\n+\t\tfprintf(stderr, \"No revs to be shown.\\n\");\n+\t\texit(0);\n+\t}\n+\n+\tfor (num_rev = 0; ref_name[num_rev]; num_rev++) {\n+\t\tunsigned char revkey[20];\n+\t\tunsigned int flag = 1u << (num_rev + REV_SHIFT);\n+\n+\t\tif (MAX_REVS <= num_rev)\n+\t\t\tdie(\"cannot handle more than %d revs.\", MAX_REVS);\n+\t\tif (get_sha1(ref_name[num_rev], revkey))\n+\t\t\tdie(\"'%s' is not a valid ref.\", ref_name[num_rev]);\n+\t\tcommit = lookup_commit_reference(revkey);\n+\t\tif (!commit)\n+\t\t\tdie(\"cannot find commit %s (%s)\",\n+\t\t\t    ref_name[num_rev], revkey);\n+\t\tparse_commit(commit);\n+\t\tmark_seen(commit, &seen);\n+\n+\t\t/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,\n+\t\t * and so on.  REV_SHIFT bits from bit 0 are used for\n+\t\t * internal bookkeeping.\n+\t\t */\n+\t\tcommit->object.flags |= flag;\n+\t\tif (commit->object.flags == flag)\n+\t\t\tinsert_by_date(commit, &list);\n+\t\trev[num_rev] = commit;\n+\t}\n+\tfor (i = 0; i < num_rev; i++)\n+\t\trev_mask[i] = rev[i]->object.flags;\n+\n+\tif (0 <= extra)\n+\t\tjoin_revs(&list, &seen, num_rev, extra);\n+\n+\tif (merge_base)\n+\t\treturn show_merge_base(seen, num_rev);\n+\n+\tif (independent)\n+\t\treturn show_independent(rev, num_rev, ref_name, rev_mask);\n+\n+\t/* Show list; --more=-1 means list-only */\n+\tif (1 < num_rev || extra < 0) {\n+\t\tfor (i = 0; i < num_rev; i++) {\n+\t\t\tint j;\n+\t\t\tint is_head = rev_is_head(head_path,\n+\t\t\t\t\t\t  head_path_len,\n+\t\t\t\t\t\t  ref_name[i],\n+\t\t\t\t\t\t  head_sha1,\n+\t\t\t\t\t\t  rev[i]->object.sha1);\n+\t\t\tif (extra < 0)\n+\t\t\t\tprintf(\"%c [%s] \",\n+\t\t\t\t       is_head ? '*' : ' ', ref_name[i]);\n+\t\t\telse {\n+\t\t\t\tfor (j = 0; j < i; j++)\n+\t\t\t\t\tputchar(' ');\n+\t\t\t\tprintf(\"%c [%s] \",\n+\t\t\t\t       is_head ? '*' : '!', ref_name[i]);\n+\t\t\t}\n+\t\t\t/* header lines never need name */\n+\t\t\tshow_one_commit(rev[i], 1);\n+\t\t\tif (is_head)\n+\t\t\t\thead_at = i;\n+\t\t}\n+\t\tif (0 <= extra) {\n+\t\t\tfor (i = 0; i < num_rev; i++)\n+\t\t\t\tputchar('-');\n+\t\t\tputchar('\\n');\n+\t\t}\n+\t}\n+\tif (extra < 0)\n+\t\texit(0);\n+\n+\t/* Sort topologically */\n+\tsort_in_topological_order(&seen, lifo);\n+\n+\t/* Give names to commits */\n+\tif (!sha1_name && !no_name)\n+\t\tname_commits(seen, rev, ref_name, num_rev);\n+\n+\tall_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n+\tall_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n+\n+\twhile (seen) {\n+\t\tstruct commit *commit = pop_one_commit(&seen);\n+\t\tint this_flag = commit->object.flags;\n+\t\tint is_merge_point = ((this_flag & all_revs) == all_revs);\n+\n+\t\tshown_merge_point |= is_merge_point;\n+\n+\t\tif (1 < num_rev) {\n+\t\t\tint is_merge = !!(commit->parents &&\n+\t\t\t\t\t  commit->parents->next);\n+\t\t\tif (topics &&\n+\t\t\t    !is_merge_point &&\n+\t\t\t    (this_flag & (1u << REV_SHIFT)))\n+\t\t\t\tcontinue;\n+\t\t\tif (dense && is_merge &&\n+\t\t\t    omit_in_dense(commit, rev, num_rev))\n+\t\t\t\tcontinue;\n+\t\t\tfor (i = 0; i < num_rev; i++) {\n+\t\t\t\tint mark;\n+\t\t\t\tif (!(this_flag & (1u << (i + REV_SHIFT))))\n+\t\t\t\t\tmark = ' ';\n+\t\t\t\telse if (is_merge)\n+\t\t\t\t\tmark = '-';\n+\t\t\t\telse if (i == head_at)\n+\t\t\t\t\tmark = '*';\n+\t\t\t\telse\n+\t\t\t\t\tmark = '+';\n+\t\t\t\tputchar(mark);\n+\t\t\t}\n+\t\t\tputchar(' ');\n+\t\t}\n+\t\tshow_one_commit(commit, no_name);\n+\n+\t\tif (shown_merge_point && --extra < 0)\n+\t\t\tbreak;\n+\t}\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex d6ff88e..01882ec 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -33,5 +33,6 @@ extern int cmd_tar_tree(int argc, const \n extern int cmd_read_tree(int argc, const char **argv, char **envp);\n extern int cmd_commit_tree(int argc, const char **argv, char **envp);\n extern int cmd_apply(int argc, const char **argv, char **envp);\n+extern int cmd_show_branch(int argc, const char **argv, char **envp);\n \n #endif\ndiff --git a/git.c b/git.c\nindex f44e08b..d29505c 100644\n--- a/git.c\n+++ b/git.c\n@@ -58,7 +58,8 @@ static void handle_internal_command(int \n \t\t{ \"tar-tree\", cmd_tar_tree },\n \t\t{ \"read-tree\", cmd_read_tree },\n \t\t{ \"commit-tree\", cmd_commit_tree },\n-\t\t{ \"apply\", cmd_apply }\n+\t\t{ \"apply\", cmd_apply },\n+\t\t{ \"show-branch\", cmd_show_branch }\n \t};\n \tint i;\n \ndiff --git a/show-branch.c b/show-branch.c\ndeleted file mode 100644\nindex 268c57b..0000000\n--- a/show-branch.c\n+++ /dev/null\n@@ -1,788 +0,0 @@\n-#include <stdlib.h>\n-#include <fnmatch.h>\n-#include \"cache.h\"\n-#include \"commit.h\"\n-#include \"refs.h\"\n-\n-static const char show_branch_usage[] =\n-\"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]\";\n-\n-static int default_num = 0;\n-static int default_alloc = 0;\n-static char **default_arg = NULL;\n-\n-#define UNINTERESTING\t01\n-\n-#define REV_SHIFT\t 2\n-#define MAX_REVS\t29 /* should not exceed bits_per_int - REV_SHIFT */\n-\n-static struct commit *interesting(struct commit_list *list)\n-{\n-\twhile (list) {\n-\t\tstruct commit *commit = list->item;\n-\t\tlist = list->next;\n-\t\tif (commit->object.flags & UNINTERESTING)\n-\t\t\tcontinue;\n-\t\treturn commit;\n-\t}\n-\treturn NULL;\n-}\n-\n-static struct commit *pop_one_commit(struct commit_list **list_p)\n-{\n-\tstruct commit *commit;\n-\tstruct commit_list *list;\n-\tlist = *list_p;\n-\tcommit = list->item;\n-\t*list_p = list->next;\n-\tfree(list);\n-\treturn commit;\n-}\n-\n-struct commit_name {\n-\tconst char *head_name; /* which head's ancestor? */\n-\tint generation; /* how many parents away from head_name */\n-};\n-\n-/* Name the commit as nth generation ancestor of head_name;\n- * we count only the first-parent relationship for naming purposes.\n- */\n-static void name_commit(struct commit *commit, const char *head_name, int nth)\n-{\n-\tstruct commit_name *name;\n-\tif (!commit->object.util)\n-\t\tcommit->object.util = xmalloc(sizeof(struct commit_name));\n-\tname = commit->object.util;\n-\tname->head_name = head_name;\n-\tname->generation = nth;\n-}\n-\n-/* Parent is the first parent of the commit.  We may name it\n- * as (n+1)th generation ancestor of the same head_name as\n- * commit is nth generation ancestor of, if that generation\n- * number is better than the name it already has.\n- */\n-static void name_parent(struct commit *commit, struct commit *parent)\n-{\n-\tstruct commit_name *commit_name = commit->object.util;\n-\tstruct commit_name *parent_name = parent->object.util;\n-\tif (!commit_name)\n-\t\treturn;\n-\tif (!parent_name ||\n-\t    commit_name->generation + 1 < parent_name->generation)\n-\t\tname_commit(parent, commit_name->head_name,\n-\t\t\t    commit_name->generation + 1);\n-}\n-\n-static int name_first_parent_chain(struct commit *c)\n-{\n-\tint i = 0;\n-\twhile (c) {\n-\t\tstruct commit *p;\n-\t\tif (!c->object.util)\n-\t\t\tbreak;\n-\t\tif (!c->parents)\n-\t\t\tbreak;\n-\t\tp = c->parents->item;\n-\t\tif (!p->object.util) {\n-\t\t\tname_parent(c, p);\n-\t\t\ti++;\n-\t\t}\n-\t\tc = p;\n-\t}\n-\treturn i;\n-}\n-\n-static void name_commits(struct commit_list *list,\n-\t\t\t struct commit **rev,\n-\t\t\t char **ref_name,\n-\t\t\t int num_rev)\n-{\n-\tstruct commit_list *cl;\n-\tstruct commit *c;\n-\tint i;\n-\n-\t/* First give names to the given heads */\n-\tfor (cl = list; cl; cl = cl->next) {\n-\t\tc = cl->item;\n-\t\tif (c->object.util)\n-\t\t\tcontinue;\n-\t\tfor (i = 0; i < num_rev; i++) {\n-\t\t\tif (rev[i] == c) {\n-\t\t\t\tname_commit(c, ref_name[i], 0);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t/* Then commits on the first parent ancestry chain */\n-\tdo {\n-\t\ti = 0;\n-\t\tfor (cl = list; cl; cl = cl->next) {\n-\t\t\ti += name_first_parent_chain(cl->item);\n-\t\t}\n-\t} while (i);\n-\n-\t/* Finally, any unnamed commits */\n-\tdo {\n-\t\ti = 0;\n-\t\tfor (cl = list; cl; cl = cl->next) {\n-\t\t\tstruct commit_list *parents;\n-\t\t\tstruct commit_name *n;\n-\t\t\tint nth;\n-\t\t\tc = cl->item;\n-\t\t\tif (!c->object.util)\n-\t\t\t\tcontinue;\n-\t\t\tn = c->object.util;\n-\t\t\tparents = c->parents;\n-\t\t\tnth = 0;\n-\t\t\twhile (parents) {\n-\t\t\t\tstruct commit *p = parents->item;\n-\t\t\t\tchar newname[1000], *en;\n-\t\t\t\tparents = parents->next;\n-\t\t\t\tnth++;\n-\t\t\t\tif (p->object.util)\n-\t\t\t\t\tcontinue;\n-\t\t\t\ten = newname;\n-\t\t\t\tswitch (n->generation) {\n-\t\t\t\tcase 0:\n-\t\t\t\t\ten += sprintf(en, \"%s\", n->head_name);\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase 1:\n-\t\t\t\t\ten += sprintf(en, \"%s^\", n->head_name);\n-\t\t\t\t\tbreak;\n-\t\t\t\tdefault:\n-\t\t\t\t\ten += sprintf(en, \"%s~%d\",\n-\t\t\t\t\t\tn->head_name, n->generation);\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t\tif (nth == 1)\n-\t\t\t\t\ten += sprintf(en, \"^\");\n-\t\t\t\telse\n-\t\t\t\t\ten += sprintf(en, \"^%d\", nth);\n-\t\t\t\tname_commit(p, strdup(newname), 0);\n-\t\t\t\ti++;\n-\t\t\t\tname_first_parent_chain(p);\n-\t\t\t}\n-\t\t}\n-\t} while (i);\n-}\n-\n-static int mark_seen(struct commit *commit, struct commit_list **seen_p)\n-{\n-\tif (!commit->object.flags) {\n-\t\tinsert_by_date(commit, seen_p);\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n-static void join_revs(struct commit_list **list_p,\n-\t\t      struct commit_list **seen_p,\n-\t\t      int num_rev, int extra)\n-{\n-\tint all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n-\tint all_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n-\n-\twhile (*list_p) {\n-\t\tstruct commit_list *parents;\n-\t\tint still_interesting = !!interesting(*list_p);\n-\t\tstruct commit *commit = pop_one_commit(list_p);\n-\t\tint flags = commit->object.flags & all_mask;\n-\n-\t\tif (!still_interesting && extra <= 0)\n-\t\t\tbreak;\n-\n-\t\tmark_seen(commit, seen_p);\n-\t\tif ((flags & all_revs) == all_revs)\n-\t\t\tflags |= UNINTERESTING;\n-\t\tparents = commit->parents;\n-\n-\t\twhile (parents) {\n-\t\t\tstruct commit *p = parents->item;\n-\t\t\tint this_flag = p->object.flags;\n-\t\t\tparents = parents->next;\n-\t\t\tif ((this_flag & flags) == flags)\n-\t\t\t\tcontinue;\n-\t\t\tif (!p->object.parsed)\n-\t\t\t\tparse_commit(p);\n-\t\t\tif (mark_seen(p, seen_p) && !still_interesting)\n-\t\t\t\textra--;\n-\t\t\tp->object.flags |= flags;\n-\t\t\tinsert_by_date(p, list_p);\n-\t\t}\n-\t}\n-\n-\t/*\n-\t * Postprocess to complete well-poisoning.\n-\t *\n-\t * At this point we have all the commits we have seen in\n-\t * seen_p list (which happens to be sorted chronologically but\n-\t * it does not really matter).  Mark anything that can be\n-\t * reached from uninteresting commits not interesting.\n-\t */\n-\tfor (;;) {\n-\t\tint changed = 0;\n-\t\tstruct commit_list *s;\n-\t\tfor (s = *seen_p; s; s = s->next) {\n-\t\t\tstruct commit *c = s->item;\n-\t\t\tstruct commit_list *parents;\n-\n-\t\t\tif (((c->object.flags & all_revs) != all_revs) &&\n-\t\t\t    !(c->object.flags & UNINTERESTING))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/* The current commit is either a merge base or\n-\t\t\t * already uninteresting one.  Mark its parents\n-\t\t\t * as uninteresting commits _only_ if they are\n-\t\t\t * already parsed.  No reason to find new ones\n-\t\t\t * here.\n-\t\t\t */\n-\t\t\tparents = c->parents;\n-\t\t\twhile (parents) {\n-\t\t\t\tstruct commit *p = parents->item;\n-\t\t\t\tparents = parents->next;\n-\t\t\t\tif (!(p->object.flags & UNINTERESTING)) {\n-\t\t\t\t\tp->object.flags |= UNINTERESTING;\n-\t\t\t\t\tchanged = 1;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\tif (!changed)\n-\t\t\tbreak;\n-\t}\n-}\n-\n-static void show_one_commit(struct commit *commit, int no_name)\n-{\n-\tchar pretty[256], *cp;\n-\tstruct commit_name *name = commit->object.util;\n-\tif (commit->object.parsed)\n-\t\tpretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,\n-\t\t\t\t    pretty, sizeof(pretty), 0);\n-\telse\n-\t\tstrcpy(pretty, \"(unavailable)\");\n-\tif (!strncmp(pretty, \"[PATCH] \", 8))\n-\t\tcp = pretty + 8;\n-\telse\n-\t\tcp = pretty;\n-\n-\tif (!no_name) {\n-\t\tif (name && name->head_name) {\n-\t\t\tprintf(\"[%s\", name->head_name);\n-\t\t\tif (name->generation) {\n-\t\t\t\tif (name->generation == 1)\n-\t\t\t\t\tprintf(\"^\");\n-\t\t\t\telse\n-\t\t\t\t\tprintf(\"~%d\", name->generation);\n-\t\t\t}\n-\t\t\tprintf(\"] \");\n-\t\t}\n-\t\telse\n-\t\t\tprintf(\"[%s] \",\n-\t\t\t       find_unique_abbrev(commit->object.sha1, 7));\n-\t}\n-\tputs(cp);\n-}\n-\n-static char *ref_name[MAX_REVS + 1];\n-static int ref_name_cnt;\n-\n-static const char *find_digit_prefix(const char *s, int *v)\n-{\n-\tconst char *p;\n-\tint ver;\n-\tchar ch;\n-\n-\tfor (p = s, ver = 0;\n-\t     '0' <= (ch = *p) && ch <= '9';\n-\t     p++)\n-\t\tver = ver * 10 + ch - '0';\n-\t*v = ver;\n-\treturn p;\n-}\n-\n-\n-static int version_cmp(const char *a, const char *b)\n-{\n-\twhile (1) {\n-\t\tint va, vb;\n-\n-\t\ta = find_digit_prefix(a, &va);\n-\t\tb = find_digit_prefix(b, &vb);\n-\t\tif (va != vb)\n-\t\t\treturn va - vb;\n-\n-\t\twhile (1) {\n-\t\t\tint ca = *a;\n-\t\t\tint cb = *b;\n-\t\t\tif ('0' <= ca && ca <= '9')\n-\t\t\t\tca = 0;\n-\t\t\tif ('0' <= cb && cb <= '9')\n-\t\t\t\tcb = 0;\n-\t\t\tif (ca != cb)\n-\t\t\t\treturn ca - cb;\n-\t\t\tif (!ca)\n-\t\t\t\tbreak;\n-\t\t\ta++;\n-\t\t\tb++;\n-\t\t}\n-\t\tif (!*a && !*b)\n-\t\t\treturn 0;\n-\t}\n-}\n-\n-static int compare_ref_name(const void *a_, const void *b_)\n-{\n-\tconst char * const*a = a_, * const*b = b_;\n-\treturn version_cmp(*a, *b);\n-}\n-\n-static void sort_ref_range(int bottom, int top)\n-{\n-\tqsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),\n-\t      compare_ref_name);\n-}\n-\n-static int append_ref(const char *refname, const unsigned char *sha1)\n-{\n-\tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n-\tint i;\n-\n-\tif (!commit)\n-\t\treturn 0;\n-\t/* Avoid adding the same thing twice */\n-\tfor (i = 0; i < ref_name_cnt; i++)\n-\t\tif (!strcmp(refname, ref_name[i]))\n-\t\t\treturn 0;\n-\n-\tif (MAX_REVS <= ref_name_cnt) {\n-\t\tfprintf(stderr, \"warning: ignoring %s; \"\n-\t\t\t\"cannot handle more than %d refs\\n\",\n-\t\t\trefname, MAX_REVS);\n-\t\treturn 0;\n-\t}\n-\tref_name[ref_name_cnt++] = strdup(refname);\n-\tref_name[ref_name_cnt] = NULL;\n-\treturn 0;\n-}\n-\n-static int append_head_ref(const char *refname, const unsigned char *sha1)\n-{\n-\tunsigned char tmp[20];\n-\tint ofs = 11;\n-\tif (strncmp(refname, \"refs/heads/\", ofs))\n-\t\treturn 0;\n-\t/* If both heads/foo and tags/foo exists, get_sha1 would\n-\t * get confused.\n-\t */\n-\tif (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))\n-\t\tofs = 5;\n-\treturn append_ref(refname + ofs, sha1);\n-}\n-\n-static int append_tag_ref(const char *refname, const unsigned char *sha1)\n-{\n-\tif (strncmp(refname, \"refs/tags/\", 10))\n-\t\treturn 0;\n-\treturn append_ref(refname + 5, sha1);\n-}\n-\n-static const char *match_ref_pattern = NULL;\n-static int match_ref_slash = 0;\n-static int count_slash(const char *s)\n-{\n-\tint cnt = 0;\n-\twhile (*s)\n-\t\tif (*s++ == '/')\n-\t\t\tcnt++;\n-\treturn cnt;\n-}\n-\n-static int append_matching_ref(const char *refname, const unsigned char *sha1)\n-{\n-\t/* we want to allow pattern hold/<asterisk> to show all\n-\t * branches under refs/heads/hold/, and v0.99.9? to show\n-\t * refs/tags/v0.99.9a and friends.\n-\t */\n-\tconst char *tail;\n-\tint slash = count_slash(refname);\n-\tfor (tail = refname; *tail && match_ref_slash < slash; )\n-\t\tif (*tail++ == '/')\n-\t\t\tslash--;\n-\tif (!*tail)\n-\t\treturn 0;\n-\tif (fnmatch(match_ref_pattern, tail, 0))\n-\t\treturn 0;\n-\tif (!strncmp(\"refs/heads/\", refname, 11))\n-\t\treturn append_head_ref(refname, sha1);\n-\tif (!strncmp(\"refs/tags/\", refname, 10))\n-\t\treturn append_tag_ref(refname, sha1);\n-\treturn append_ref(refname, sha1);\n-}\n-\n-static void snarf_refs(int head, int tag)\n-{\n-\tif (head) {\n-\t\tint orig_cnt = ref_name_cnt;\n-\t\tfor_each_ref(append_head_ref);\n-\t\tsort_ref_range(orig_cnt, ref_name_cnt);\n-\t}\n-\tif (tag) {\n-\t\tint orig_cnt = ref_name_cnt;\n-\t\tfor_each_ref(append_tag_ref);\n-\t\tsort_ref_range(orig_cnt, ref_name_cnt);\n-\t}\n-}\n-\n-static int rev_is_head(char *head_path, int headlen, char *name,\n-\t\t       unsigned char *head_sha1, unsigned char *sha1)\n-{\n-\tint namelen;\n-\tif ((!head_path[0]) ||\n-\t    (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20)))\n-\t\treturn 0;\n-\tnamelen = strlen(name);\n-\tif ((headlen < namelen) ||\n-\t    memcmp(head_path + headlen - namelen, name, namelen))\n-\t\treturn 0;\n-\tif (headlen == namelen ||\n-\t    head_path[headlen - namelen - 1] == '/')\n-\t\treturn 1;\n-\treturn 0;\n-}\n-\n-static int show_merge_base(struct commit_list *seen, int num_rev)\n-{\n-\tint all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n-\tint all_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n-\tint exit_status = 1;\n-\n-\twhile (seen) {\n-\t\tstruct commit *commit = pop_one_commit(&seen);\n-\t\tint flags = commit->object.flags & all_mask;\n-\t\tif (!(flags & UNINTERESTING) &&\n-\t\t    ((flags & all_revs) == all_revs)) {\n-\t\t\tputs(sha1_to_hex(commit->object.sha1));\n-\t\t\texit_status = 0;\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\t\t}\n-\t}\n-\treturn exit_status;\n-}\n-\n-static int show_independent(struct commit **rev,\n-\t\t\t    int num_rev,\n-\t\t\t    char **ref_name,\n-\t\t\t    unsigned int *rev_mask)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < num_rev; i++) {\n-\t\tstruct commit *commit = rev[i];\n-\t\tunsigned int flag = rev_mask[i];\n-\n-\t\tif (commit->object.flags == flag)\n-\t\t\tputs(sha1_to_hex(commit->object.sha1));\n-\t\tcommit->object.flags |= UNINTERESTING;\n-\t}\n-\treturn 0;\n-}\n-\n-static void append_one_rev(const char *av)\n-{\n-\tunsigned char revkey[20];\n-\tif (!get_sha1(av, revkey)) {\n-\t\tappend_ref(av, revkey);\n-\t\treturn;\n-\t}\n-\tif (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {\n-\t\t/* glob style match */\n-\t\tint saved_matches = ref_name_cnt;\n-\t\tmatch_ref_pattern = av;\n-\t\tmatch_ref_slash = count_slash(av);\n-\t\tfor_each_ref(append_matching_ref);\n-\t\tif (saved_matches == ref_name_cnt &&\n-\t\t    ref_name_cnt < MAX_REVS)\n-\t\t\terror(\"no matching refs with %s\", av);\n-\t\tif (saved_matches + 1 < ref_name_cnt)\n-\t\t\tsort_ref_range(saved_matches, ref_name_cnt);\n-\t\treturn;\n-\t}\n-\tdie(\"bad sha1 reference %s\", av);\n-}\n-\n-static int git_show_branch_config(const char *var, const char *value)\n-{\n-\tif (!strcmp(var, \"showbranch.default\")) {\n-\t\tif (default_alloc <= default_num + 1) {\n-\t\t\tdefault_alloc = default_alloc * 3 / 2 + 20;\n-\t\t\tdefault_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);\n-\t\t}\n-\t\tdefault_arg[default_num++] = strdup(value);\n-\t\tdefault_arg[default_num] = NULL;\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_default_config(var, value);\n-}\n-\n-static int omit_in_dense(struct commit *commit, struct commit **rev, int n)\n-{\n-\t/* If the commit is tip of the named branches, do not\n-\t * omit it.\n-\t * Otherwise, if it is a merge that is reachable from only one\n-\t * tip, it is not that interesting.\n-\t */\n-\tint i, flag, count;\n-\tfor (i = 0; i < n; i++)\n-\t\tif (rev[i] == commit)\n-\t\t\treturn 0;\n-\tflag = commit->object.flags;\n-\tfor (i = count = 0; i < n; i++) {\n-\t\tif (flag & (1u << (i + REV_SHIFT)))\n-\t\t\tcount++;\n-\t}\n-\tif (count == 1)\n-\t\treturn 1;\n-\treturn 0;\n-}\n-\n-int main(int ac, char **av)\n-{\n-\tstruct commit *rev[MAX_REVS], *commit;\n-\tstruct commit_list *list = NULL, *seen = NULL;\n-\tunsigned int rev_mask[MAX_REVS];\n-\tint num_rev, i, extra = 0;\n-\tint all_heads = 0, all_tags = 0;\n-\tint all_mask, all_revs;\n-\tint lifo = 1;\n-\tchar head_path[128];\n-\tconst char *head_path_p;\n-\tint head_path_len;\n-\tunsigned char head_sha1[20];\n-\tint merge_base = 0;\n-\tint independent = 0;\n-\tint no_name = 0;\n-\tint sha1_name = 0;\n-\tint shown_merge_point = 0;\n-\tint with_current_branch = 0;\n-\tint head_at = -1;\n-\tint topics = 0;\n-\tint dense = 1;\n-\n-\tsetup_git_directory();\n-\tgit_config(git_show_branch_config);\n-\n-\t/* If nothing is specified, try the default first */\n-\tif (ac == 1 && default_num) {\n-\t\tac = default_num + 1;\n-\t\tav = default_arg - 1; /* ick; we would not address av[0] */\n-\t}\n-\n-\twhile (1 < ac && av[1][0] == '-') {\n-\t\tchar *arg = av[1];\n-\t\tif (!strcmp(arg, \"--\")) {\n-\t\t\tac--; av++;\n-\t\t\tbreak;\n-\t\t}\n-\t\telse if (!strcmp(arg, \"--all\"))\n-\t\t\tall_heads = all_tags = 1;\n-\t\telse if (!strcmp(arg, \"--heads\"))\n-\t\t\tall_heads = 1;\n-\t\telse if (!strcmp(arg, \"--tags\"))\n-\t\t\tall_tags = 1;\n-\t\telse if (!strcmp(arg, \"--more\"))\n-\t\t\textra = 1;\n-\t\telse if (!strcmp(arg, \"--list\"))\n-\t\t\textra = -1;\n-\t\telse if (!strcmp(arg, \"--no-name\"))\n-\t\t\tno_name = 1;\n-\t\telse if (!strcmp(arg, \"--current\"))\n-\t\t\twith_current_branch = 1;\n-\t\telse if (!strcmp(arg, \"--sha1-name\"))\n-\t\t\tsha1_name = 1;\n-\t\telse if (!strncmp(arg, \"--more=\", 7))\n-\t\t\textra = atoi(arg + 7);\n-\t\telse if (!strcmp(arg, \"--merge-base\"))\n-\t\t\tmerge_base = 1;\n-\t\telse if (!strcmp(arg, \"--independent\"))\n-\t\t\tindependent = 1;\n-\t\telse if (!strcmp(arg, \"--topo-order\"))\n-\t\t\tlifo = 1;\n-\t\telse if (!strcmp(arg, \"--topics\"))\n-\t\t\ttopics = 1;\n-\t\telse if (!strcmp(arg, \"--sparse\"))\n-\t\t\tdense = 0;\n-\t\telse if (!strcmp(arg, \"--date-order\"))\n-\t\t\tlifo = 0;\n-\t\telse\n-\t\t\tusage(show_branch_usage);\n-\t\tac--; av++;\n-\t}\n-\tac--; av++;\n-\n-\t/* Only one of these is allowed */\n-\tif (1 < independent + merge_base + (extra != 0))\n-\t\tusage(show_branch_usage);\n-\n-\t/* If nothing is specified, show all branches by default */\n-\tif (ac + all_heads + all_tags == 0)\n-\t\tall_heads = 1;\n-\n-\tif (all_heads + all_tags)\n-\t\tsnarf_refs(all_heads, all_tags);\n-\twhile (0 < ac) {\n-\t\tappend_one_rev(*av);\n-\t\tac--; av++;\n-\t}\n-\n-\thead_path_p = resolve_ref(git_path(\"HEAD\"), head_sha1, 1);\n-\tif (head_path_p) {\n-\t\thead_path_len = strlen(head_path_p);\n-\t\tmemcpy(head_path, head_path_p, head_path_len + 1);\n-\t}\n-\telse {\n-\t\thead_path_len = 0;\n-\t\thead_path[0] = 0;\n-\t}\n-\n-\tif (with_current_branch && head_path_p) {\n-\t\tint has_head = 0;\n-\t\tfor (i = 0; !has_head && i < ref_name_cnt; i++) {\n-\t\t\t/* We are only interested in adding the branch\n-\t\t\t * HEAD points at.\n-\t\t\t */\n-\t\t\tif (rev_is_head(head_path,\n-\t\t\t\t\thead_path_len,\n-\t\t\t\t\tref_name[i],\n-\t\t\t\t\thead_sha1, NULL))\n-\t\t\t\thas_head++;\n-\t\t}\n-\t\tif (!has_head) {\n-\t\t\tint pfxlen = strlen(git_path(\"refs/heads/\"));\n-\t\t\tappend_one_rev(head_path + pfxlen);\n-\t\t}\n-\t}\n-\n-\tif (!ref_name_cnt) {\n-\t\tfprintf(stderr, \"No revs to be shown.\\n\");\n-\t\texit(0);\n-\t}\n-\n-\tfor (num_rev = 0; ref_name[num_rev]; num_rev++) {\n-\t\tunsigned char revkey[20];\n-\t\tunsigned int flag = 1u << (num_rev + REV_SHIFT);\n-\n-\t\tif (MAX_REVS <= num_rev)\n-\t\t\tdie(\"cannot handle more than %d revs.\", MAX_REVS);\n-\t\tif (get_sha1(ref_name[num_rev], revkey))\n-\t\t\tdie(\"'%s' is not a valid ref.\", ref_name[num_rev]);\n-\t\tcommit = lookup_commit_reference(revkey);\n-\t\tif (!commit)\n-\t\t\tdie(\"cannot find commit %s (%s)\",\n-\t\t\t    ref_name[num_rev], revkey);\n-\t\tparse_commit(commit);\n-\t\tmark_seen(commit, &seen);\n-\n-\t\t/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,\n-\t\t * and so on.  REV_SHIFT bits from bit 0 are used for\n-\t\t * internal bookkeeping.\n-\t\t */\n-\t\tcommit->object.flags |= flag;\n-\t\tif (commit->object.flags == flag)\n-\t\t\tinsert_by_date(commit, &list);\n-\t\trev[num_rev] = commit;\n-\t}\n-\tfor (i = 0; i < num_rev; i++)\n-\t\trev_mask[i] = rev[i]->object.flags;\n-\n-\tif (0 <= extra)\n-\t\tjoin_revs(&list, &seen, num_rev, extra);\n-\n-\tif (merge_base)\n-\t\treturn show_merge_base(seen, num_rev);\n-\n-\tif (independent)\n-\t\treturn show_independent(rev, num_rev, ref_name, rev_mask);\n-\n-\t/* Show list; --more=-1 means list-only */\n-\tif (1 < num_rev || extra < 0) {\n-\t\tfor (i = 0; i < num_rev; i++) {\n-\t\t\tint j;\n-\t\t\tint is_head = rev_is_head(head_path,\n-\t\t\t\t\t\t  head_path_len,\n-\t\t\t\t\t\t  ref_name[i],\n-\t\t\t\t\t\t  head_sha1,\n-\t\t\t\t\t\t  rev[i]->object.sha1);\n-\t\t\tif (extra < 0)\n-\t\t\t\tprintf(\"%c [%s] \",\n-\t\t\t\t       is_head ? '*' : ' ', ref_name[i]);\n-\t\t\telse {\n-\t\t\t\tfor (j = 0; j < i; j++)\n-\t\t\t\t\tputchar(' ');\n-\t\t\t\tprintf(\"%c [%s] \",\n-\t\t\t\t       is_head ? '*' : '!', ref_name[i]);\n-\t\t\t}\n-\t\t\t/* header lines never need name */\n-\t\t\tshow_one_commit(rev[i], 1);\n-\t\t\tif (is_head)\n-\t\t\t\thead_at = i;\n-\t\t}\n-\t\tif (0 <= extra) {\n-\t\t\tfor (i = 0; i < num_rev; i++)\n-\t\t\t\tputchar('-');\n-\t\t\tputchar('\\n');\n-\t\t}\n-\t}\n-\tif (extra < 0)\n-\t\texit(0);\n-\n-\t/* Sort topologically */\n-\tsort_in_topological_order(&seen, lifo);\n-\n-\t/* Give names to commits */\n-\tif (!sha1_name && !no_name)\n-\t\tname_commits(seen, rev, ref_name, num_rev);\n-\n-\tall_mask = ((1u << (REV_SHIFT + num_rev)) - 1);\n-\tall_revs = all_mask & ~((1u << REV_SHIFT) - 1);\n-\n-\twhile (seen) {\n-\t\tstruct commit *commit = pop_one_commit(&seen);\n-\t\tint this_flag = commit->object.flags;\n-\t\tint is_merge_point = ((this_flag & all_revs) == all_revs);\n-\n-\t\tshown_merge_point |= is_merge_point;\n-\n-\t\tif (1 < num_rev) {\n-\t\t\tint is_merge = !!(commit->parents &&\n-\t\t\t\t\t  commit->parents->next);\n-\t\t\tif (topics &&\n-\t\t\t    !is_merge_point &&\n-\t\t\t    (this_flag & (1u << REV_SHIFT)))\n-\t\t\t\tcontinue;\n-\t\t\tif (dense && is_merge &&\n-\t\t\t    omit_in_dense(commit, rev, num_rev))\n-\t\t\t\tcontinue;\n-\t\t\tfor (i = 0; i < num_rev; i++) {\n-\t\t\t\tint mark;\n-\t\t\t\tif (!(this_flag & (1u << (i + REV_SHIFT))))\n-\t\t\t\t\tmark = ' ';\n-\t\t\t\telse if (is_merge)\n-\t\t\t\t\tmark = '-';\n-\t\t\t\telse if (i == head_at)\n-\t\t\t\t\tmark = '*';\n-\t\t\t\telse\n-\t\t\t\t\tmark = '+';\n-\t\t\t\tputchar(mark);\n-\t\t\t}\n-\t\t\tputchar(' ');\n-\t\t}\n-\t\tshow_one_commit(commit, no_name);\n-\n-\t\tif (shown_merge_point && --extra < 0)\n-\t\t\tbreak;\n-\t}\n-\treturn 0;\n-}\n-- \n1.3.3.g288c\n"}]}