SYNOPSIS
--------
-'git-show-branch [--all] [--heads] [--tags] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] <reference>...'
+'git-show-branch [--all] [--heads] [--tags] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [<rev> | <glob>]...'
DESCRIPTION
-----------
-Shows the head commits from the named <reference> (or all refs under
-$GIT_DIR/refs/heads), and displays concise list of commit logs
-to show their relationship semi-visually.
+
+Shows the commit ancestry graph starting from the commits named
+with <rev>s or <globs>s (or all refs under $GIT_DIR/refs/heads
+and/or $GIT_DIR/refs/tags) semi-visually.
+
+It cannot show more than 29 branches and commits at a time.
+
OPTIONS
-------
-<reference>::
- Name of the reference under $GIT_DIR/refs/.
+<rev>::
+ Arbitrary extended SHA1 expression (see `git-rev-parse`)
+ that typically names a branch HEAD or a tag.
+
+<glob>::
+ A glob pattern that matches branch or tag names under
+ $GIT_DIR/refs. For example, if you have many topic
+ branches under $GIT_DIR/refs/heads/topic, giving
+ `topic/*` would show all of them.
--all --heads --tags::
Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads,
#include <stdlib.h>
+#include <fnmatch.h>
#include "cache.h"
#include "commit.h"
#include "refs.h"
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);
+ return;
+ }
+ die("bad sha1 reference %s", av);
+}
+
int main(int ac, char **av)
{
struct commit *rev[MAX_REVS], *commit;
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);
- ac--; av++;
+ if (ac) {
+ while (0 < ac) {
+ append_one_rev(*av);
+ ac--; av++;
+ }
}
-
- /* If still no revs, then add heads */
- if (!ref_name_cnt)
+ else {
+ /* If no revs given, then add heads */
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];