Merge branch 'sp/merge' (early part)
[gitweb.git] / builtin-describe.c
index ad3b469f372c6c62b3b6ba2c36c2a80874743e6d..a8c98cea163a28cafb9b23861bd976b6354c2def 100644 (file)
@@ -2,10 +2,11 @@
 #include "commit.h"
 #include "tag.h"
 #include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
 #include "builtin.h"
 
-#define SEEN (1u << 0)
-
 static const char describe_usage[] =
 "git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
 
@@ -16,7 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
 
 static int names, allocs;
 static struct commit_name {
-       const struct commit *commit;
+       struct commit *commit;
        int prio; /* annotated tag = 2, tag = 1, head = 0 */
        char path[FLEX_ARRAY]; /* more */
 } **name_array = NULL;
@@ -35,7 +36,7 @@ static struct commit_name *match(struct commit *cmit)
 }
 
 static void add_to_known_names(const char *path,
-                              const struct commit *commit,
+                              struct commit *commit,
                               int prio)
 {
        int idx;
@@ -98,6 +99,12 @@ static int compare_names(const void *_a, const void *_b)
        return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
 }
 
+struct possible_tag {
+       struct possible_tag *next;
+       struct commit_name *name;
+       unsigned long depth;
+};
+
 static void describe(const char *arg, int last_one)
 {
        unsigned char sha1[20];
@@ -105,6 +112,7 @@ static void describe(const char *arg, int last_one)
        struct commit_list *list;
        static int initialized = 0;
        struct commit_name *n;
+       struct possible_tag *all_matches, *min_match, *cur_match;
 
        if (get_sha1(arg, sha1))
                die("Not a valid object name %s", arg);
@@ -125,19 +133,68 @@ static void describe(const char *arg, int last_one)
        }
 
        list = NULL;
+       all_matches = NULL;
+       cur_match = NULL;
        commit_list_insert(cmit, &list);
        while (list) {
-               struct commit *c = pop_most_recent_commit(&list, SEEN);
+               struct commit *c = pop_commit(&list);
                n = match(c);
                if (n) {
-                       printf("%s-g%s\n", n->path,
-                              find_unique_abbrev(cmit->object.sha1, abbrev));
-                       if (!last_one)
-                               clear_commit_marks(cmit, SEEN);
-                       return;
+                       struct possible_tag *p = xmalloc(sizeof(*p));
+                       p->name = n;
+                       p->next = NULL;
+                       if (cur_match)
+                               cur_match->next = p;
+                       else
+                               all_matches = p;
+                       cur_match = p;
+               } else {
+                       struct commit_list *parents = c->parents;
+                       while (parents) {
+                               struct commit *p = parents->item;
+                               parse_commit(p);
+                               if (!(p->object.flags & SEEN)) {
+                                       p->object.flags |= SEEN;
+                                       insert_by_date(p, &list);
+                               }
+                               parents = parents->next;
+                       }
+               }
+       }
+
+       if (!all_matches)
+               die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+
+       min_match = NULL;
+       for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
+               struct rev_info revs;
+               struct commit *tagged = cur_match->name->commit;
+
+               clear_commit_marks(cmit, -1);
+               init_revisions(&revs, NULL);
+               tagged->object.flags |= UNINTERESTING;
+               add_pending_object(&revs, &tagged->object, NULL);
+               add_pending_object(&revs, &cmit->object, NULL);
+
+               prepare_revision_walk(&revs);
+               cur_match->depth = 0;
+               while ((!min_match || cur_match->depth < min_match->depth)
+                       && get_revision(&revs))
+                       cur_match->depth++;
+               if (!min_match || cur_match->depth < min_match->depth)
+                       min_match = cur_match;
+               free_commit_list(revs.commits);
+       }
+       printf("%s-g%s\n", min_match->name->path,
+                  find_unique_abbrev(cmit->object.sha1, abbrev));
+
+       if (!last_one) {
+               for (cur_match = all_matches; cur_match; cur_match = min_match) {
+                       min_match = cur_match->next;
+                       free(cur_match);
                }
+               clear_commit_marks(cmit, SEEN);
        }
-       die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
 }
 
 int cmd_describe(int argc, const char **argv, const char *prefix)