diff options
Diffstat (limited to 'builtin-name-rev.c')
-rw-r--r-- | builtin-name-rev.c | 222 |
1 files changed, 126 insertions, 96 deletions
diff --git a/builtin-name-rev.c b/builtin-name-rev.c index c02222436..06a38ac8c 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -3,20 +3,23 @@ #include "commit.h" #include "tag.h" #include "refs.h" +#include "parse-options.h" -static const char name_rev_usage[] = - "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n"; +#define CUTOFF_DATE_SLOP 86400 /* one day */ typedef struct rev_name { const char *tip_name; - int merge_traversals; int generation; + int distance; } rev_name; static long cutoff = LONG_MAX; +/* How many generations are maximally preferred over _one_ merge traversal? */ +#define MERGE_TRAVERSAL_WEIGHT 65535 + static void name_rev(struct commit *commit, - const char *tip_name, int merge_traversals, int generation, + const char *tip_name, int generation, int distance, int deref) { struct rev_name *name = (struct rev_name *)commit->util; @@ -43,13 +46,11 @@ static void name_rev(struct commit *commit, name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; - } else if (name->merge_traversals > merge_traversals || - (name->merge_traversals == merge_traversals && - name->generation > generation)) { + } else if (name->distance > distance) { copy_data: name->tip_name = tip_name; - name->merge_traversals = merge_traversals; name->generation = generation; + name->distance = distance; } else return; @@ -58,7 +59,10 @@ copy_data: parents = parents->next, parent_number++) { if (parent_number > 1) { int len = strlen(tip_name); - char *new_name = xmalloc(len + 8); + char *new_name = xmalloc(len + + 1 + decimal_length(generation) + /* ~<n> */ + 1 + 2 + /* ^NN */ + 1); if (len > 2 && !strcmp(tip_name + len - 2, "^0")) len -= 2; @@ -69,17 +73,18 @@ copy_data: sprintf(new_name, "%.*s^%d", len, tip_name, parent_number); - name_rev(parents->item, new_name, - merge_traversals + 1 , 0, 0); + name_rev(parents->item, new_name, 0, + distance + MERGE_TRAVERSAL_WEIGHT, 0); } else { - name_rev(parents->item, tip_name, merge_traversals, - generation + 1, 0); + name_rev(parents->item, tip_name, generation + 1, + distance + 1, 0); } } } struct name_ref_data { int tags_only; + int name_only; const char *ref_filter; }; @@ -107,6 +112,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void if (!prefixcmp(path, "refs/heads/")) path = path + 11; + else if (data->tags_only + && data->name_only + && !prefixcmp(path, "refs/tags/")) + path = path + 10; else if (!prefixcmp(path, "refs/")) path = path + 5; @@ -116,18 +125,18 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void } /* returns a static buffer */ -static const char* get_rev_name(struct object *o) +static const char *get_rev_name(const struct object *o) { static char buffer[1024]; struct rev_name *n; struct commit *c; if (o->type != OBJ_COMMIT) - return "undefined"; + return NULL; c = (struct commit *) o; n = c->util; if (!n) - return "undefined"; + return NULL; if (!n->generation) return n->tip_name; @@ -142,48 +151,106 @@ static const char* get_rev_name(struct object *o) } } -int cmd_name_rev(int argc, const char **argv, const char *prefix) +static void show_name(const struct object *obj, + const char *caller_name, + int always, int allow_undefined, int name_only) { - struct object_array revs = { 0, 0, NULL }; - int as_is = 0, all = 0, transform_stdin = 0; - struct name_ref_data data = { 0, NULL }; + const char *name; + const unsigned char *sha1 = obj->sha1; + + if (!name_only) + printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1)); + name = get_rev_name(obj); + if (name) + printf("%s\n", name); + else if (allow_undefined) + printf("undefined\n"); + else if (always) + printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV)); + else + die("cannot describe '%s'", sha1_to_hex(sha1)); +} + +static char const * const name_rev_usage[] = { + "git name-rev [options] ( --all | --stdin | <commit>... )", + NULL +}; + +static void name_rev_line(char *p, struct name_ref_data *data) +{ + int forty = 0; + char *p_start; + for (p_start = p; *p; p++) { +#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) + if (!ishex(*p)) + forty = 0; + else if (++forty == 40 && + !ishex(*(p+1))) { + unsigned char sha1[40]; + const char *name = NULL; + char c = *(p+1); + int p_len = p - p_start + 1; + + forty = 0; + + *(p+1) = 0; + if (!get_sha1(p - 39, sha1)) { + struct object *o = + lookup_object(sha1); + if (o) + name = get_rev_name(o); + } + *(p+1) = c; - git_config(git_default_config); + if (!name) + continue; - if (argc < 2) - usage(name_rev_usage); + if (data->name_only) + printf("%.*s%s", p_len - 40, p_start, name); + else + printf("%.*s (%s)", p_len, p_start, name); + p_start = p + 1; + } + } + + /* flush */ + if (p_start != p) + fwrite(p_start, p - p_start, 1, stdout); +} + +int cmd_name_rev(int argc, const char **argv, const char *prefix) +{ + struct object_array revs = { 0, 0, NULL }; + int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0; + struct name_ref_data data = { 0, 0, NULL }; + struct option opts[] = { + OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), + OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), + OPT_STRING(0, "refs", &data.ref_filter, "pattern", + "only use refs matching <pattern>"), + OPT_GROUP(""), + OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), + OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), + OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"), + OPT_BOOLEAN(0, "always", &always, + "show abbreviated commit object as fallback"), + OPT_END(), + }; + + git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); + if (!!all + !!transform_stdin + !!argc > 1) { + error("Specify either a list, or --all, not both!"); + usage_with_options(name_rev_usage, opts); + } + if (all || transform_stdin) + cutoff = 0; - for (--argc, ++argv; argc; --argc, ++argv) { + for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *o; struct commit *commit; - if (!as_is && (*argv)[0] == '-') { - if (!strcmp(*argv, "--")) { - as_is = 1; - continue; - } else if (!strcmp(*argv, "--tags")) { - data.tags_only = 1; - continue; - } else if (!prefixcmp(*argv, "--refs=")) { - data.ref_filter = *argv + 7; - continue; - } else if (!strcmp(*argv, "--all")) { - if (argc > 1) - die("Specify either a list, or --all, not both!"); - all = 1; - cutoff = 0; - continue; - } else if (!strcmp(*argv, "--stdin")) { - if (argc > 1) - die("Specify either a list, or --stdin, not both!"); - transform_stdin = 1; - cutoff = 0; - continue; - } - usage(name_rev_usage); - } - if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); @@ -198,78 +265,41 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } commit = (struct commit *)o; - if (cutoff > commit->date) cutoff = commit->date; - add_object_array((struct object *)commit, *argv, &revs); } + if (cutoff) + cutoff = cutoff - CUTOFF_DATE_SLOP; for_each_ref(name_ref, &data); if (transform_stdin) { char buffer[2048]; - char *p, *p_start; while (!feof(stdin)) { - int forty = 0; - p = fgets(buffer, sizeof(buffer), stdin); + char *p = fgets(buffer, sizeof(buffer), stdin); if (!p) break; - - for (p_start = p; *p; p++) { -#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) - if (!ishex(*p)) - forty = 0; - else if (++forty == 40 && - !ishex(*(p+1))) { - unsigned char sha1[40]; - const char *name = "undefined"; - char c = *(p+1); - - forty = 0; - - *(p+1) = 0; - if (!get_sha1(p - 39, sha1)) { - struct object *o = - lookup_object(sha1); - if (o) - name = get_rev_name(o); - } - *(p+1) = c; - - if (!strcmp(name, "undefined")) - continue; - - fwrite(p_start, p - p_start + 1, 1, - stdout); - printf(" (%s)", name); - p_start = p + 1; - } - } - - /* flush */ - if (p_start != p) - fwrite(p_start, p - p_start, 1, stdout); + name_rev_line(p, &data); } } else if (all) { int i, max; max = get_max_object_index(); for (i = 0; i < max; i++) { - struct object * obj = get_indexed_object(i); + struct object *obj = get_indexed_object(i); if (!obj) continue; - printf("%s %s\n", sha1_to_hex(obj->sha1), get_rev_name(obj)); + show_name(obj, NULL, + always, allow_undefined, data.name_only); } } else { int i; for (i = 0; i < revs.nr; i++) - printf("%s %s\n", - revs.objects[i].name, - get_rev_name(revs.objects[i].item)); + show_name(revs.objects[i].item, revs.objects[i].name, + always, allow_undefined, data.name_only); } return 0; } - |