push: change `simple` to accommodate triangular workflows
[gitweb.git] / builtin / describe.c
index 04c185b1fbdc51ba9958fdb83d1c6616bcb0c18e..4e675c3d0d7b1daeb7f12ef425514a11a92a5c3f 100644 (file)
@@ -21,6 +21,7 @@ static int debug;     /* Display lots of verbose info */
 static int all;        /* Any valid ref can be used */
 static int tags;       /* Allow lightweight tags */
 static int longformat;
+static int first_parent;
 static int abbrev = -1; /* unspecified */
 static int max_candidates = 10;
 static struct hash_table names;
@@ -42,7 +43,7 @@ struct commit_name {
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
        unsigned char sha1[20];
-       const char *path;
+       char *path;
 };
 static const char *prio_names[] = {
        "head", "lightweight", "annotated",
@@ -126,51 +127,52 @@ static void add_to_known_names(const char *path,
                        } else {
                                e->next = NULL;
                        }
+                       e->path = NULL;
                }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
                hashcpy(e->sha1, sha1);
-               e->path = path;
+               free(e->path);
+               e->path = xstrdup(path);
        }
 }
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       int might_be_tag = !prefixcmp(path, "refs/tags/");
+       int is_tag = !prefixcmp(path, "refs/tags/");
        unsigned char peeled[20];
-       int is_tag, prio;
+       int is_annotated, prio;
 
-       if (!all && !might_be_tag)
+       /* Reject anything outside refs/tags/ unless --all */
+       if (!all && !is_tag)
                return 0;
 
+       /* Accept only tags that match the pattern, if given */
+       if (pattern && (!is_tag || fnmatch(pattern, path + 10, 0)))
+               return 0;
+
+       /* Is it annotated? */
        if (!peel_ref(path, peeled)) {
-               is_tag = !!hashcmp(sha1, peeled);
+               is_annotated = !!hashcmp(sha1, peeled);
        } else {
                hashcpy(peeled, sha1);
-               is_tag = 0;
+               is_annotated = 0;
        }
 
-       /* If --all, then any refs are used.
-        * If --tags, then any tags are used.
-        * Otherwise only annotated tags are used.
+       /*
+        * By default, we only use annotated tags, but with --tags
+        * we fall back to lightweight ones (even without --tags,
+        * we still remember lightweight ones, only to give hints
+        * in an error message).  --all allows any refs to be used.
         */
-       if (might_be_tag) {
-               if (is_tag)
-                       prio = 2;
-               else
-                       prio = 1;
-
-               if (pattern && fnmatch(pattern, path + 10, 0))
-                       prio = 0;
-       }
+       if (is_annotated)
+               prio = 2;
+       else if (is_tag)
+               prio = 1;
        else
                prio = 0;
 
-       if (!all) {
-               if (!prio)
-                       return 0;
-       }
        add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
        return 0;
 }
@@ -337,6 +339,9 @@ static void describe(const char *arg, int last_one)
                                commit_list_insert_by_date(p, &list);
                        p->object.flags |= c->object.flags;
                        parents = parents->next;
+
+                       if (first_parent)
+                               break;
                }
        }
 
@@ -402,9 +407,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOLEAN(0, "contains",   &contains, N_("find the tag that comes after the commit")),
                OPT_BOOLEAN(0, "debug",      &debug, N_("debug search strategy on stderr")),
-               OPT_BOOLEAN(0, "all",        &all, N_("use any ref in .git/refs")),
-               OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag in .git/refs/tags")),
+               OPT_BOOLEAN(0, "all",        &all, N_("use any ref")),
+               OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag, even unannotated")),
                OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
+               OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
                            N_("only output exact matches"), 0),