Documentation/git-cherry-pick: Add --replay and --no-commit.
[gitweb.git] / show-branch.c
index 631336cd9debb7f97b0e644c9ad51853c220aef4..3cb335d68b40e54f1688e70730cd095be224d1e5 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include <fnmatch.h>
 #include "cache.h"
 #include "commit.h"
 #include "refs.h"
@@ -54,7 +55,7 @@ static void name_commit(struct commit *commit, const char *head_name, int nth)
 
 /* 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)
@@ -313,9 +314,16 @@ static int append_ref(const char *refname, const unsigned char *sha1)
 
 static int append_head_ref(const char *refname, const unsigned char *sha1)
 {
-       if (strncmp(refname, "refs/heads/", 11))
+       unsigned char tmp[20];
+       int ofs = 11;
+       if (strncmp(refname, "refs/heads/", ofs))
                return 0;
-       return append_ref(refname + 11, sha1);
+       /* If both heads/foo and tags/foo exists, get_sha1 would
+        * get confused.
+        */
+       if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))
+               ofs = 5;
+       return append_ref(refname + ofs, sha1);
 }
 
 static int append_tag_ref(const char *refname, const unsigned char *sha1)
@@ -325,6 +333,39 @@ static int append_tag_ref(const char *refname, const unsigned char *sha1)
        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) {
@@ -393,6 +434,27 @@ static int show_independent(struct commit **rev,
        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;
@@ -451,17 +513,20 @@ int main(int ac, char **av)
        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];
@@ -470,7 +535,7 @@ int main(int ac, char **av)
                if (MAX_REVS <= num_rev)
                        die("cannot handle more than %d revs.", MAX_REVS);
                if (get_sha1(ref_name[num_rev], revkey))
-                       usage(show_branch_usage);
+                       die("'%s' is not a valid ref.\n", ref_name[num_rev]);
                commit = lookup_commit_reference(revkey);
                if (!commit)
                        die("cannot find commit %s (%s)",