ebfa4290e924d5934270c6360b4e63d8796dbffb
   1#include "cache.h"
   2#include "commit.h"
   3#include "refs.h"
   4
   5#define SEEN (1u << 0)
   6
   7static const char describe_usage[] = "git-describe [--all] <committish>*";
   8
   9static int all = 0;     /* Default to tags only */
  10
  11static int names = 0, allocs = 0;
  12static struct commit_name {
  13        const struct commit *commit;
  14        char path[];
  15} **name_array = NULL;
  16
  17static struct commit_name *match(struct commit *cmit)
  18{
  19        int i = names;
  20        struct commit_name **p = name_array;
  21
  22        while (i-- > 0) {
  23                struct commit_name *n = *p++;
  24                if (n->commit == cmit)
  25                        return n;
  26        }
  27        return NULL;
  28}
  29
  30static void add_to_known_names(const char *path, const struct commit *commit)
  31{
  32        int idx;
  33        int len = strlen(path)+1;
  34        struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
  35
  36        name->commit = commit;
  37        memcpy(name->path, path, len);
  38        idx = names;
  39        if (idx >= allocs) {
  40                allocs = (idx + 50) * 3 / 2;
  41                name_array = xrealloc(name_array, allocs*sizeof(*name_array));
  42        }
  43        name_array[idx] = name;
  44        names = ++idx;
  45}
  46
  47static int get_name(const char *path, const unsigned char *sha1)
  48{
  49        struct commit *commit = lookup_commit_reference_gently(sha1, 1);
  50        if (!commit)
  51                return 0;
  52        if (!all && strncmp(path, "refs/tags/", 10))
  53                return 0;
  54        add_to_known_names(path, commit);
  55        return 0;
  56}
  57
  58static int compare_names(const void *_a, const void *_b)
  59{
  60        struct commit_name *a = *(struct commit_name **)_a;
  61        struct commit_name *b = *(struct commit_name **)_b;
  62        unsigned long a_date = a->commit->date;
  63        unsigned long b_date = b->commit->date;
  64        return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
  65}
  66
  67static void describe(struct commit *cmit)
  68{
  69        struct commit_list *list;
  70        static int initialized = 0;
  71        struct commit_name *n;
  72
  73        if (!initialized) {
  74                initialized = 1;
  75                for_each_ref(get_name);
  76                qsort(name_array, names, sizeof(*name_array), compare_names);
  77        }
  78
  79        n = match(cmit);
  80        if (n) {
  81                printf("%s\n", n->path);
  82                return;
  83        }
  84
  85        list = NULL;
  86        commit_list_insert(cmit, &list);
  87        while (list) {
  88                struct commit *c = pop_most_recent_commit(&list, SEEN);
  89                n = match(c);
  90                if (n) {
  91                        printf("%s-g%.8s\n", n->path, sha1_to_hex(cmit->object.sha1));
  92                        return;
  93                }
  94        }
  95}
  96
  97int main(int argc, char **argv)
  98{
  99        int i;
 100
 101        for (i = 1; i < argc; i++) {
 102                const char *arg = argv[i];
 103                unsigned char sha1[20];
 104                struct commit *cmit;
 105
 106                if (!strcmp(arg, "--all")) {
 107                        all = 1;
 108                        continue;
 109                }
 110                if (get_sha1(arg, sha1) < 0)
 111                        usage(describe_usage);
 112                cmit = lookup_commit_reference(sha1);
 113                if (!cmit)
 114                        usage(describe_usage);
 115                describe(cmit);
 116        }
 117        return 0;
 118}