builtin-name-rev.con commit test format-patch -s: make sure MIME content type is shown as needed (6b945b9)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "commit.h"
   4#include "tag.h"
   5#include "refs.h"
   6
   7#define CUTOFF_DATE_SLOP 86400 /* one day */
   8
   9static const char name_rev_usage[] =
  10        "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n";
  11
  12typedef struct rev_name {
  13        const char *tip_name;
  14        int generation;
  15        int distance;
  16} rev_name;
  17
  18static long cutoff = LONG_MAX;
  19
  20/* How many generations are maximally preferred over _one_ merge traversal? */
  21#define MERGE_TRAVERSAL_WEIGHT 65535
  22
  23static void name_rev(struct commit *commit,
  24                const char *tip_name, int generation, int distance,
  25                int deref)
  26{
  27        struct rev_name *name = (struct rev_name *)commit->util;
  28        struct commit_list *parents;
  29        int parent_number = 1;
  30
  31        if (!commit->object.parsed)
  32                parse_commit(commit);
  33
  34        if (commit->date < cutoff)
  35                return;
  36
  37        if (deref) {
  38                char *new_name = xmalloc(strlen(tip_name)+3);
  39                strcpy(new_name, tip_name);
  40                strcat(new_name, "^0");
  41                tip_name = new_name;
  42
  43                if (generation)
  44                        die("generation: %d, but deref?", generation);
  45        }
  46
  47        if (name == NULL) {
  48                name = xmalloc(sizeof(rev_name));
  49                commit->util = name;
  50                goto copy_data;
  51        } else if (name->distance > distance) {
  52copy_data:
  53                name->tip_name = tip_name;
  54                name->generation = generation;
  55                name->distance = distance;
  56        } else
  57                return;
  58
  59        for (parents = commit->parents;
  60                        parents;
  61                        parents = parents->next, parent_number++) {
  62                if (parent_number > 1) {
  63                        int len = strlen(tip_name);
  64                        char *new_name = xmalloc(len +
  65                                1 + decimal_length(generation) +  /* ~<n> */
  66                                1 + 2 +                           /* ^NN */
  67                                1);
  68
  69                        if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
  70                                len -= 2;
  71                        if (generation > 0)
  72                                sprintf(new_name, "%.*s~%d^%d", len, tip_name,
  73                                                generation, parent_number);
  74                        else
  75                                sprintf(new_name, "%.*s^%d", len, tip_name,
  76                                                parent_number);
  77
  78                        name_rev(parents->item, new_name, 0,
  79                                distance + MERGE_TRAVERSAL_WEIGHT, 0);
  80                } else {
  81                        name_rev(parents->item, tip_name, generation + 1,
  82                                distance + 1, 0);
  83                }
  84        }
  85}
  86
  87struct name_ref_data {
  88        int tags_only;
  89        int name_only;
  90        const char *ref_filter;
  91};
  92
  93static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
  94{
  95        struct object *o = parse_object(sha1);
  96        struct name_ref_data *data = cb_data;
  97        int deref = 0;
  98
  99        if (data->tags_only && prefixcmp(path, "refs/tags/"))
 100                return 0;
 101
 102        if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
 103                return 0;
 104
 105        while (o && o->type == OBJ_TAG) {
 106                struct tag *t = (struct tag *) o;
 107                if (!t->tagged)
 108                        break; /* broken repository */
 109                o = parse_object(t->tagged->sha1);
 110                deref = 1;
 111        }
 112        if (o && o->type == OBJ_COMMIT) {
 113                struct commit *commit = (struct commit *)o;
 114
 115                if (!prefixcmp(path, "refs/heads/"))
 116                        path = path + 11;
 117                else if (data->tags_only
 118                    && data->name_only
 119                    && !prefixcmp(path, "refs/tags/"))
 120                        path = path + 10;
 121                else if (!prefixcmp(path, "refs/"))
 122                        path = path + 5;
 123
 124                name_rev(commit, xstrdup(path), 0, 0, deref);
 125        }
 126        return 0;
 127}
 128
 129/* returns a static buffer */
 130static const char* get_rev_name(struct object *o)
 131{
 132        static char buffer[1024];
 133        struct rev_name *n;
 134        struct commit *c;
 135
 136        if (o->type != OBJ_COMMIT)
 137                return "undefined";
 138        c = (struct commit *) o;
 139        n = c->util;
 140        if (!n)
 141                return "undefined";
 142
 143        if (!n->generation)
 144                return n->tip_name;
 145        else {
 146                int len = strlen(n->tip_name);
 147                if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
 148                        len -= 2;
 149                snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
 150                                n->generation);
 151
 152                return buffer;
 153        }
 154}
 155
 156int cmd_name_rev(int argc, const char **argv, const char *prefix)
 157{
 158        struct object_array revs = { 0, 0, NULL };
 159        int as_is = 0, all = 0, transform_stdin = 0;
 160        struct name_ref_data data = { 0, 0, NULL };
 161
 162        git_config(git_default_config);
 163
 164        if (argc < 2)
 165                usage(name_rev_usage);
 166
 167        for (--argc, ++argv; argc; --argc, ++argv) {
 168                unsigned char sha1[20];
 169                struct object *o;
 170                struct commit *commit;
 171
 172                if (!as_is && (*argv)[0] == '-') {
 173                        if (!strcmp(*argv, "--")) {
 174                                as_is = 1;
 175                                continue;
 176                        } else if (!strcmp(*argv, "--name-only")) {
 177                                data.name_only = 1;
 178                                continue;
 179                        } else if (!strcmp(*argv, "--tags")) {
 180                                data.tags_only = 1;
 181                                continue;
 182                        } else  if (!prefixcmp(*argv, "--refs=")) {
 183                                data.ref_filter = *argv + 7;
 184                                continue;
 185                        } else if (!strcmp(*argv, "--all")) {
 186                                if (argc > 1)
 187                                        die("Specify either a list, or --all, not both!");
 188                                all = 1;
 189                                cutoff = 0;
 190                                continue;
 191                        } else if (!strcmp(*argv, "--stdin")) {
 192                                if (argc > 1)
 193                                        die("Specify either a list, or --stdin, not both!");
 194                                transform_stdin = 1;
 195                                cutoff = 0;
 196                                continue;
 197                        }
 198                        usage(name_rev_usage);
 199                }
 200
 201                if (get_sha1(*argv, sha1)) {
 202                        fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
 203                                        *argv);
 204                        continue;
 205                }
 206
 207                o = deref_tag(parse_object(sha1), *argv, 0);
 208                if (!o || o->type != OBJ_COMMIT) {
 209                        fprintf(stderr, "Could not get commit for %s. Skipping.\n",
 210                                        *argv);
 211                        continue;
 212                }
 213
 214                commit = (struct commit *)o;
 215
 216                if (cutoff > commit->date)
 217                        cutoff = commit->date;
 218
 219                add_object_array((struct object *)commit, *argv, &revs);
 220        }
 221
 222        if (cutoff)
 223                cutoff = cutoff - CUTOFF_DATE_SLOP;
 224        for_each_ref(name_ref, &data);
 225
 226        if (transform_stdin) {
 227                char buffer[2048];
 228                char *p, *p_start;
 229
 230                while (!feof(stdin)) {
 231                        int forty = 0;
 232                        p = fgets(buffer, sizeof(buffer), stdin);
 233                        if (!p)
 234                                break;
 235
 236                        for (p_start = p; *p; p++) {
 237#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
 238                                if (!ishex(*p))
 239                                        forty = 0;
 240                                else if (++forty == 40 &&
 241                                                !ishex(*(p+1))) {
 242                                        unsigned char sha1[40];
 243                                        const char *name = "undefined";
 244                                        char c = *(p+1);
 245
 246                                        forty = 0;
 247
 248                                        *(p+1) = 0;
 249                                        if (!get_sha1(p - 39, sha1)) {
 250                                                struct object *o =
 251                                                        lookup_object(sha1);
 252                                                if (o)
 253                                                        name = get_rev_name(o);
 254                                        }
 255                                        *(p+1) = c;
 256
 257                                        if (!strcmp(name, "undefined"))
 258                                                continue;
 259
 260                                        fwrite(p_start, p - p_start + 1, 1,
 261                                               stdout);
 262                                        printf(" (%s)", name);
 263                                        p_start = p + 1;
 264                                }
 265                        }
 266
 267                        /* flush */
 268                        if (p_start != p)
 269                                fwrite(p_start, p - p_start, 1, stdout);
 270                }
 271        } else if (all) {
 272                int i, max;
 273
 274                max = get_max_object_index();
 275                for (i = 0; i < max; i++) {
 276                        struct object * obj = get_indexed_object(i);
 277                        if (!obj)
 278                                continue;
 279                        if (!data.name_only)
 280                                printf("%s ", sha1_to_hex(obj->sha1));
 281                        printf("%s\n", get_rev_name(obj));
 282                }
 283        } else {
 284                int i;
 285                for (i = 0; i < revs.nr; i++) {
 286                        if (!data.name_only)
 287                                printf("%s ", revs.objects[i].name);
 288                        printf("%s\n", get_rev_name(revs.objects[i].item));
 289                }
 290        }
 291
 292        return 0;
 293}