i18n: remote: add comment for translators
[git] / builtin / name-rev.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "commit.h"
4 #include "tag.h"
5 #include "refs.h"
6 #include "parse-options.h"
7 #include "sha1-lookup.h"
8
9 #define CUTOFF_DATE_SLOP 86400 /* one day */
10
11 typedef struct rev_name {
12         const char *tip_name;
13         int generation;
14         int distance;
15 } rev_name;
16
17 static long cutoff = LONG_MAX;
18
19 /* How many generations are maximally preferred over _one_ merge traversal? */
20 #define MERGE_TRAVERSAL_WEIGHT 65535
21
22 static void name_rev(struct commit *commit,
23                 const char *tip_name, int generation, int distance,
24                 int deref)
25 {
26         struct rev_name *name = (struct rev_name *)commit->util;
27         struct commit_list *parents;
28         int parent_number = 1;
29
30         parse_commit(commit);
31
32         if (commit->date < cutoff)
33                 return;
34
35         if (deref) {
36                 tip_name = xstrfmt("%s^0", tip_name);
37
38                 if (generation)
39                         die("generation: %d, but deref?", generation);
40         }
41
42         if (name == NULL) {
43                 name = xmalloc(sizeof(rev_name));
44                 commit->util = name;
45                 goto copy_data;
46         } else if (name->distance > distance) {
47 copy_data:
48                 name->tip_name = tip_name;
49                 name->generation = generation;
50                 name->distance = distance;
51         } else
52                 return;
53
54         for (parents = commit->parents;
55                         parents;
56                         parents = parents->next, parent_number++) {
57                 if (parent_number > 1) {
58                         size_t len;
59                         char *new_name;
60
61                         strip_suffix(tip_name, "^0", &len);
62                         if (generation > 0)
63                                 new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
64                                                    generation, parent_number);
65                         else
66                                 new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
67                                                    parent_number);
68
69                         name_rev(parents->item, new_name, 0,
70                                 distance + MERGE_TRAVERSAL_WEIGHT, 0);
71                 } else {
72                         name_rev(parents->item, tip_name, generation + 1,
73                                 distance + 1, 0);
74                 }
75         }
76 }
77
78 static int subpath_matches(const char *path, const char *filter)
79 {
80         const char *subpath = path;
81
82         while (subpath) {
83                 if (!wildmatch(filter, subpath, 0, NULL))
84                         return subpath - path;
85                 subpath = strchr(subpath, '/');
86                 if (subpath)
87                         subpath++;
88         }
89         return -1;
90 }
91
92 static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
93 {
94         if (shorten_unambiguous)
95                 refname = shorten_unambiguous_ref(refname, 0);
96         else if (starts_with(refname, "refs/heads/"))
97                 refname = refname + 11;
98         else if (starts_with(refname, "refs/"))
99                 refname = refname + 5;
100         return refname;
101 }
102
103 struct name_ref_data {
104         int tags_only;
105         int name_only;
106         const char *ref_filter;
107 };
108
109 static struct tip_table {
110         struct tip_table_entry {
111                 unsigned char sha1[20];
112                 const char *refname;
113         } *table;
114         int nr;
115         int alloc;
116         int sorted;
117 } tip_table;
118
119 static void add_to_tip_table(const unsigned char *sha1, const char *refname,
120                              int shorten_unambiguous)
121 {
122         refname = name_ref_abbrev(refname, shorten_unambiguous);
123
124         ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
125         hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
126         tip_table.table[tip_table.nr].refname = xstrdup(refname);
127         tip_table.nr++;
128         tip_table.sorted = 0;
129 }
130
131 static int tipcmp(const void *a_, const void *b_)
132 {
133         const struct tip_table_entry *a = a_, *b = b_;
134         return hashcmp(a->sha1, b->sha1);
135 }
136
137 static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
138 {
139         struct object *o = parse_object(oid->hash);
140         struct name_ref_data *data = cb_data;
141         int can_abbreviate_output = data->tags_only && data->name_only;
142         int deref = 0;
143
144         if (data->tags_only && !starts_with(path, "refs/tags/"))
145                 return 0;
146
147         if (data->ref_filter) {
148                 switch (subpath_matches(path, data->ref_filter)) {
149                 case -1: /* did not match */
150                         return 0;
151                 case 0:  /* matched fully */
152                         break;
153                 default: /* matched subpath */
154                         can_abbreviate_output = 1;
155                         break;
156                 }
157         }
158
159         add_to_tip_table(oid->hash, path, can_abbreviate_output);
160
161         while (o && o->type == OBJ_TAG) {
162                 struct tag *t = (struct tag *) o;
163                 if (!t->tagged)
164                         break; /* broken repository */
165                 o = parse_object(t->tagged->oid.hash);
166                 deref = 1;
167         }
168         if (o && o->type == OBJ_COMMIT) {
169                 struct commit *commit = (struct commit *)o;
170
171                 path = name_ref_abbrev(path, can_abbreviate_output);
172                 name_rev(commit, xstrdup(path), 0, 0, deref);
173         }
174         return 0;
175 }
176
177 static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
178 {
179         struct tip_table_entry *table = table_;
180         return table[ix].sha1;
181 }
182
183 static const char *get_exact_ref_match(const struct object *o)
184 {
185         int found;
186
187         if (!tip_table.table || !tip_table.nr)
188                 return NULL;
189
190         if (!tip_table.sorted) {
191                 qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
192                       tipcmp);
193                 tip_table.sorted = 1;
194         }
195
196         found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr,
197                          nth_tip_table_ent);
198         if (0 <= found)
199                 return tip_table.table[found].refname;
200         return NULL;
201 }
202
203 /* returns a static buffer */
204 static const char *get_rev_name(const struct object *o)
205 {
206         static char buffer[1024];
207         struct rev_name *n;
208         struct commit *c;
209
210         if (o->type != OBJ_COMMIT)
211                 return get_exact_ref_match(o);
212         c = (struct commit *) o;
213         n = c->util;
214         if (!n)
215                 return NULL;
216
217         if (!n->generation)
218                 return n->tip_name;
219         else {
220                 int len = strlen(n->tip_name);
221                 if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
222                         len -= 2;
223                 snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
224                                 n->generation);
225
226                 return buffer;
227         }
228 }
229
230 static void show_name(const struct object *obj,
231                       const char *caller_name,
232                       int always, int allow_undefined, int name_only)
233 {
234         const char *name;
235         const struct object_id *oid = &obj->oid;
236
237         if (!name_only)
238                 printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
239         name = get_rev_name(obj);
240         if (name)
241                 printf("%s\n", name);
242         else if (allow_undefined)
243                 printf("undefined\n");
244         else if (always)
245                 printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
246         else
247                 die("cannot describe '%s'", oid_to_hex(oid));
248 }
249
250 static char const * const name_rev_usage[] = {
251         N_("git name-rev [<options>] <commit>..."),
252         N_("git name-rev [<options>] --all"),
253         N_("git name-rev [<options>] --stdin"),
254         NULL
255 };
256
257 static void name_rev_line(char *p, struct name_ref_data *data)
258 {
259         int forty = 0;
260         char *p_start;
261         for (p_start = p; *p; p++) {
262 #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
263                 if (!ishex(*p))
264                         forty = 0;
265                 else if (++forty == 40 &&
266                          !ishex(*(p+1))) {
267                         unsigned char sha1[40];
268                         const char *name = NULL;
269                         char c = *(p+1);
270                         int p_len = p - p_start + 1;
271
272                         forty = 0;
273
274                         *(p+1) = 0;
275                         if (!get_sha1(p - 39, sha1)) {
276                                 struct object *o =
277                                         lookup_object(sha1);
278                                 if (o)
279                                         name = get_rev_name(o);
280                         }
281                         *(p+1) = c;
282
283                         if (!name)
284                                 continue;
285
286                         if (data->name_only)
287                                 printf("%.*s%s", p_len - 40, p_start, name);
288                         else
289                                 printf("%.*s (%s)", p_len, p_start, name);
290                         p_start = p + 1;
291                 }
292         }
293
294         /* flush */
295         if (p_start != p)
296                 fwrite(p_start, p - p_start, 1, stdout);
297 }
298
299 int cmd_name_rev(int argc, const char **argv, const char *prefix)
300 {
301         struct object_array revs = OBJECT_ARRAY_INIT;
302         int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
303         struct name_ref_data data = { 0, 0, NULL };
304         struct option opts[] = {
305                 OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
306                 OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
307                 OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
308                                    N_("only use refs matching <pattern>")),
309                 OPT_GROUP(""),
310                 OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
311                 OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
312                 OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
313                 OPT_BOOL(0, "always",     &always,
314                            N_("show abbreviated commit object as fallback")),
315                 {
316                         /* A Hidden OPT_BOOL */
317                         OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
318                         N_("dereference tags in the input (internal use)"),
319                         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
320                 },
321                 OPT_END(),
322         };
323
324         git_config(git_default_config, NULL);
325         argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
326         if (all + transform_stdin + !!argc > 1) {
327                 error("Specify either a list, or --all, not both!");
328                 usage_with_options(name_rev_usage, opts);
329         }
330         if (all || transform_stdin)
331                 cutoff = 0;
332
333         for (; argc; argc--, argv++) {
334                 unsigned char sha1[20];
335                 struct object *object;
336                 struct commit *commit;
337
338                 if (get_sha1(*argv, sha1)) {
339                         fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
340                                         *argv);
341                         continue;
342                 }
343
344                 commit = NULL;
345                 object = parse_object(sha1);
346                 if (object) {
347                         struct object *peeled = deref_tag(object, *argv, 0);
348                         if (peeled && peeled->type == OBJ_COMMIT)
349                                 commit = (struct commit *)peeled;
350                 }
351
352                 if (!object) {
353                         fprintf(stderr, "Could not get object for %s. Skipping.\n",
354                                         *argv);
355                         continue;
356                 }
357
358                 if (commit) {
359                         if (cutoff > commit->date)
360                                 cutoff = commit->date;
361                 }
362
363                 if (peel_tag) {
364                         if (!commit) {
365                                 fprintf(stderr, "Could not get commit for %s. Skipping.\n",
366                                         *argv);
367                                 continue;
368                         }
369                         object = (struct object *)commit;
370                 }
371                 add_object_array(object, *argv, &revs);
372         }
373
374         if (cutoff)
375                 cutoff = cutoff - CUTOFF_DATE_SLOP;
376         for_each_ref(name_ref, &data);
377
378         if (transform_stdin) {
379                 char buffer[2048];
380
381                 while (!feof(stdin)) {
382                         char *p = fgets(buffer, sizeof(buffer), stdin);
383                         if (!p)
384                                 break;
385                         name_rev_line(p, &data);
386                 }
387         } else if (all) {
388                 int i, max;
389
390                 max = get_max_object_index();
391                 for (i = 0; i < max; i++) {
392                         struct object *obj = get_indexed_object(i);
393                         if (!obj || obj->type != OBJ_COMMIT)
394                                 continue;
395                         show_name(obj, NULL,
396                                   always, allow_undefined, data.name_only);
397                 }
398         } else {
399                 int i;
400                 for (i = 0; i < revs.nr; i++)
401                         show_name(revs.objects[i].item, revs.objects[i].name,
402                                   always, allow_undefined, data.name_only);
403         }
404
405         return 0;
406 }