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