#include <stdlib.h>
+#include <fnmatch.h>
#include "cache.h"
#include "commit.h"
#include "refs.h"
static const char show_branch_usage[] =
-"git-show-branch [--all] [--heads] [--tags] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
+"git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
#define UNINTERESTING 01
/* Parent is the first parent of the commit. We may name it
* as (n+1)th generation ancestor of the same head_name as
- * commit is nth generation ancestore of, if that generation
+ * commit is nth generation ancestor of, if that generation
* number is better than the name it already has.
*/
static void name_parent(struct commit *commit, struct commit *parent)
static char *ref_name[MAX_REVS + 1];
static int ref_name_cnt;
+static const char *find_digit_prefix(const char *s, int *v)
+{
+ const char *p;
+ int ver;
+ char ch;
+
+ for (p = s, ver = 0;
+ '0' <= (ch = *p) && ch <= '9';
+ p++)
+ ver = ver * 10 + ch - '0';
+ *v = ver;
+ return p;
+}
+
+
+static int version_cmp(const char *a, const char *b)
+{
+ while (1) {
+ int va, vb;
+
+ a = find_digit_prefix(a, &va);
+ b = find_digit_prefix(b, &vb);
+ if (va != vb)
+ return va - vb;
+
+ while (1) {
+ int ca = *a;
+ int cb = *b;
+ if ('0' <= ca && ca <= '9')
+ ca = 0;
+ if ('0' <= cb && cb <= '9')
+ cb = 0;
+ if (ca != cb)
+ return ca - cb;
+ if (!ca)
+ break;
+ a++;
+ b++;
+ }
+ if (!*a && !*b)
+ return 0;
+ }
+}
+
static int compare_ref_name(const void *a_, const void *b_)
{
const char * const*a = a_, * const*b = b_;
- return strcmp(*a, *b);
+ return version_cmp(*a, *b);
}
static void sort_ref_range(int bottom, int top)
static int append_ref(const char *refname, const unsigned char *sha1)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ int i;
+
if (!commit)
return 0;
+ /* Avoid adding the same thing twice */
+ for (i = 0; i < ref_name_cnt; i++)
+ if (!strcmp(refname, ref_name[i]))
+ return 0;
+
if (MAX_REVS <= ref_name_cnt) {
fprintf(stderr, "warning: ignoring %s; "
- "cannot handle more than %d refs",
+ "cannot handle more than %d refs\n",
refname, MAX_REVS);
return 0;
}
return append_ref(refname + 5, sha1);
}
+static const char *match_ref_pattern = NULL;
+static int match_ref_slash = 0;
+static int count_slash(const char *s)
+{
+ int cnt = 0;
+ while (*s)
+ if (*s++ == '/')
+ cnt++;
+ return cnt;
+}
+
+static int append_matching_ref(const char *refname, const unsigned char *sha1)
+{
+ /* we want to allow pattern hold/<asterisk> to show all
+ * branches under refs/heads/hold/, and v0.99.9? to show
+ * refs/tags/v0.99.9a and friends.
+ */
+ const char *tail;
+ int slash = count_slash(refname);
+ for (tail = refname; *tail && match_ref_slash < slash; )
+ if (*tail++ == '/')
+ slash--;
+ if (!*tail)
+ return 0;
+ if (fnmatch(match_ref_pattern, tail, 0))
+ return 0;
+ if (!strncmp("refs/heads/", refname, 11))
+ return append_head_ref(refname, sha1);
+ if (!strncmp("refs/tags/", refname, 10))
+ return append_tag_ref(refname, sha1);
+ return append_ref(refname, sha1);
+}
+
static void snarf_refs(int head, int tag)
{
if (head) {
return 0;
}
+static void append_one_rev(const char *av)
+{
+ unsigned char revkey[20];
+ if (!get_sha1(av, revkey)) {
+ append_ref(av, revkey);
+ return;
+ }
+ if (strchr(av, '*') || strchr(av, '?')) {
+ /* glob style match */
+ int saved_matches = ref_name_cnt;
+ match_ref_pattern = av;
+ match_ref_slash = count_slash(av);
+ for_each_ref(append_matching_ref);
+ if (saved_matches == ref_name_cnt &&
+ ref_name_cnt < MAX_REVS)
+ error("no matching refs with %s", av);
+ if (saved_matches + 1 < ref_name_cnt)
+ sort_ref_range(saved_matches, ref_name_cnt);
+ return;
+ }
+ die("bad sha1 reference %s", av);
+}
+
int main(int ac, char **av)
{
struct commit *rev[MAX_REVS], *commit;
if (1 < independent + merge_base + (extra != 0))
usage(show_branch_usage);
+ /* If nothing is specified, show all branches by default */
+ if (ac + all_heads + all_tags == 0)
+ all_heads = 1;
+
if (all_heads + all_tags)
snarf_refs(all_heads, all_tags);
-
while (0 < ac) {
- unsigned char revkey[20];
- if (get_sha1(*av, revkey))
- die("bad sha1 reference %s", *av);
- append_ref(*av, revkey);
+ append_one_rev(*av);
ac--; av++;
}
- /* If still no revs, then add heads */
- if (!ref_name_cnt)
- snarf_refs(1, 0);
+ if (!ref_name_cnt) {
+ fprintf(stderr, "No revs to be shown.\n");
+ exit(0);
+ }
for (num_rev = 0; ref_name[num_rev]; num_rev++) {
unsigned char revkey[20];
if (MAX_REVS <= num_rev)
die("cannot handle more than %d revs.", MAX_REVS);
if (get_sha1(ref_name[num_rev], revkey))
- die("'%s' is not a valid ref.\n", ref_name[num_rev]);
+ die("'%s' is not a valid ref.", ref_name[num_rev]);
commit = lookup_commit_reference(revkey);
if (!commit)
die("cannot find commit %s (%s)",