94f22dd6733c096c46a2f8ab7f423972fa8bbba5
[git.git] / rev-list.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "tag.h"
4 #include "commit.h"
5 #include "tree.h"
6 #include "blob.h"
7 #include "epoch.h"
8 #include "diff.h"
9 #include "revision.h"
10
11 /* bits #0-2 in revision.h */
12
13 #define COUNTED         (1u << 3)
14 #define SHOWN           (1u << 4)
15 #define TMP_MARK        (1u << 5) /* for isolated cases; clean after use */
16
17 static const char rev_list_usage[] =
18 "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
19 "  limiting output:\n"
20 "    --max-count=nr\n"
21 "    --max-age=epoch\n"
22 "    --min-age=epoch\n"
23 "    --sparse\n"
24 "    --no-merges\n"
25 "    --remove-empty\n"
26 "    --all\n"
27 "  ordering output:\n"
28 "    --merge-order [ --show-breaks ]\n"
29 "    --topo-order\n"
30 "    --date-order\n"
31 "  formatting output:\n"
32 "    --parents\n"
33 "    --objects | --objects-edge\n"
34 "    --unpacked\n"
35 "    --header | --pretty\n"
36 "    --abbrev=nr | --no-abbrev\n"
37 "  special purpose:\n"
38 "    --bisect"
39 ;
40
41 struct rev_info revs;
42
43 static int bisect_list = 0;
44 static int verbose_header = 0;
45 static int abbrev = DEFAULT_ABBREV;
46 static int show_parents = 0;
47 static int hdr_termination = 0;
48 static const char *commit_prefix = "";
49 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
50 static int merge_order = 0;
51 static int show_breaks = 0;
52 static int stop_traversal = 0;
53 static int no_merges = 0;
54
55 static void show_commit(struct commit *commit)
56 {
57         commit->object.flags |= SHOWN;
58         if (show_breaks) {
59                 commit_prefix = "| ";
60                 if (commit->object.flags & DISCONTINUITY) {
61                         commit_prefix = "^ ";     
62                 } else if (commit->object.flags & BOUNDARY) {
63                         commit_prefix = "= ";
64                 } 
65         }                       
66         printf("%s%s", commit_prefix, sha1_to_hex(commit->object.sha1));
67         if (show_parents) {
68                 struct commit_list *parents = commit->parents;
69                 while (parents) {
70                         struct object *o = &(parents->item->object);
71                         parents = parents->next;
72                         if (o->flags & TMP_MARK)
73                                 continue;
74                         printf(" %s", sha1_to_hex(o->sha1));
75                         o->flags |= TMP_MARK;
76                 }
77                 /* TMP_MARK is a general purpose flag that can
78                  * be used locally, but the user should clean
79                  * things up after it is done with them.
80                  */
81                 for (parents = commit->parents;
82                      parents;
83                      parents = parents->next)
84                         parents->item->object.flags &= ~TMP_MARK;
85         }
86         if (commit_format == CMIT_FMT_ONELINE)
87                 putchar(' ');
88         else
89                 putchar('\n');
90
91         if (verbose_header) {
92                 static char pretty_header[16384];
93                 pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
94                 printf("%s%c", pretty_header, hdr_termination);
95         }
96         fflush(stdout);
97 }
98
99 static int rewrite_one(struct commit **pp)
100 {
101         for (;;) {
102                 struct commit *p = *pp;
103                 if (p->object.flags & (TREECHANGE | UNINTERESTING))
104                         return 0;
105                 if (!p->parents)
106                         return -1;
107                 *pp = p->parents->item;
108         }
109 }
110
111 static void rewrite_parents(struct commit *commit)
112 {
113         struct commit_list **pp = &commit->parents;
114         while (*pp) {
115                 struct commit_list *parent = *pp;
116                 if (rewrite_one(&parent->item) < 0) {
117                         *pp = parent->next;
118                         continue;
119                 }
120                 pp = &parent->next;
121         }
122 }
123
124 static int filter_commit(struct commit * commit)
125 {
126         if (stop_traversal && (commit->object.flags & BOUNDARY))
127                 return STOP;
128         if (commit->object.flags & (UNINTERESTING|SHOWN))
129                 return CONTINUE;
130         if (revs.min_age != -1 && (commit->date > revs.min_age))
131                 return CONTINUE;
132         if (revs.max_age != -1 && (commit->date < revs.max_age)) {
133                 stop_traversal=1;
134                 return CONTINUE;
135         }
136         if (no_merges && (commit->parents && commit->parents->next))
137                 return CONTINUE;
138         if (revs.paths && revs.dense) {
139                 if (!(commit->object.flags & TREECHANGE))
140                         return CONTINUE;
141                 rewrite_parents(commit);
142         }
143         return DO;
144 }
145
146 static int process_commit(struct commit * commit)
147 {
148         int action=filter_commit(commit);
149
150         if (action == STOP) {
151                 return STOP;
152         }
153
154         if (action == CONTINUE) {
155                 return CONTINUE;
156         }
157
158         if (revs.max_count != -1 && !revs.max_count--)
159                 return STOP;
160
161         show_commit(commit);
162
163         return CONTINUE;
164 }
165
166 static struct object_list **process_blob(struct blob *blob,
167                                          struct object_list **p,
168                                          struct name_path *path,
169                                          const char *name)
170 {
171         struct object *obj = &blob->object;
172
173         if (!revs.blob_objects)
174                 return p;
175         if (obj->flags & (UNINTERESTING | SEEN))
176                 return p;
177         obj->flags |= SEEN;
178         return add_object(obj, p, path, name);
179 }
180
181 static struct object_list **process_tree(struct tree *tree,
182                                          struct object_list **p,
183                                          struct name_path *path,
184                                          const char *name)
185 {
186         struct object *obj = &tree->object;
187         struct tree_entry_list *entry;
188         struct name_path me;
189
190         if (!revs.tree_objects)
191                 return p;
192         if (obj->flags & (UNINTERESTING | SEEN))
193                 return p;
194         if (parse_tree(tree) < 0)
195                 die("bad tree object %s", sha1_to_hex(obj->sha1));
196         obj->flags |= SEEN;
197         p = add_object(obj, p, path, name);
198         me.up = path;
199         me.elem = name;
200         me.elem_len = strlen(name);
201         entry = tree->entries;
202         tree->entries = NULL;
203         while (entry) {
204                 struct tree_entry_list *next = entry->next;
205                 if (entry->directory)
206                         p = process_tree(entry->item.tree, p, &me, entry->name);
207                 else
208                         p = process_blob(entry->item.blob, p, &me, entry->name);
209                 free(entry);
210                 entry = next;
211         }
212         return p;
213 }
214
215 static void show_commit_list(struct rev_info *revs)
216 {
217         struct commit *commit;
218         struct object_list *objects = NULL, **p = &objects, *pending;
219
220         while ((commit = get_revision(revs)) != NULL) {
221                 p = process_tree(commit->tree, p, NULL, "");
222                 if (process_commit(commit) == STOP)
223                         break;
224         }
225         for (pending = revs->pending_objects; pending; pending = pending->next) {
226                 struct object *obj = pending->item;
227                 const char *name = pending->name;
228                 if (obj->flags & (UNINTERESTING | SEEN))
229                         continue;
230                 if (obj->type == tag_type) {
231                         obj->flags |= SEEN;
232                         p = add_object(obj, p, NULL, name);
233                         continue;
234                 }
235                 if (obj->type == tree_type) {
236                         p = process_tree((struct tree *)obj, p, NULL, name);
237                         continue;
238                 }
239                 if (obj->type == blob_type) {
240                         p = process_blob((struct blob *)obj, p, NULL, name);
241                         continue;
242                 }
243                 die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
244         }
245         while (objects) {
246                 /* An object with name "foo\n0000000..." can be used to
247                  * confuse downstream git-pack-objects very badly.
248                  */
249                 const char *ep = strchr(objects->name, '\n');
250                 if (ep) {
251                         printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
252                                (int) (ep - objects->name),
253                                objects->name);
254                 }
255                 else
256                         printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
257                 objects = objects->next;
258         }
259 }
260
261 /*
262  * This is a truly stupid algorithm, but it's only
263  * used for bisection, and we just don't care enough.
264  *
265  * We care just barely enough to avoid recursing for
266  * non-merge entries.
267  */
268 static int count_distance(struct commit_list *entry)
269 {
270         int nr = 0;
271
272         while (entry) {
273                 struct commit *commit = entry->item;
274                 struct commit_list *p;
275
276                 if (commit->object.flags & (UNINTERESTING | COUNTED))
277                         break;
278                 if (!revs.paths || (commit->object.flags & TREECHANGE))
279                         nr++;
280                 commit->object.flags |= COUNTED;
281                 p = commit->parents;
282                 entry = p;
283                 if (p) {
284                         p = p->next;
285                         while (p) {
286                                 nr += count_distance(p);
287                                 p = p->next;
288                         }
289                 }
290         }
291
292         return nr;
293 }
294
295 static void clear_distance(struct commit_list *list)
296 {
297         while (list) {
298                 struct commit *commit = list->item;
299                 commit->object.flags &= ~COUNTED;
300                 list = list->next;
301         }
302 }
303
304 static struct commit_list *find_bisection(struct commit_list *list)
305 {
306         int nr, closest;
307         struct commit_list *p, *best;
308
309         nr = 0;
310         p = list;
311         while (p) {
312                 if (!revs.paths || (p->item->object.flags & TREECHANGE))
313                         nr++;
314                 p = p->next;
315         }
316         closest = 0;
317         best = list;
318
319         for (p = list; p; p = p->next) {
320                 int distance;
321
322                 if (revs.paths && !(p->item->object.flags & TREECHANGE))
323                         continue;
324
325                 distance = count_distance(p);
326                 clear_distance(list);
327                 if (nr - distance < distance)
328                         distance = nr - distance;
329                 if (distance > closest) {
330                         best = p;
331                         closest = distance;
332                 }
333         }
334         if (best)
335                 best->next = NULL;
336         return best;
337 }
338
339 static void mark_edge_parents_uninteresting(struct commit *commit)
340 {
341         struct commit_list *parents;
342
343         for (parents = commit->parents; parents; parents = parents->next) {
344                 struct commit *parent = parents->item;
345                 if (!(parent->object.flags & UNINTERESTING))
346                         continue;
347                 mark_tree_uninteresting(parent->tree);
348                 if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
349                         parent->object.flags |= SHOWN;
350                         printf("-%s\n", sha1_to_hex(parent->object.sha1));
351                 }
352         }
353 }
354
355 static void mark_edges_uninteresting(struct commit_list *list)
356 {
357         for ( ; list; list = list->next) {
358                 struct commit *commit = list->item;
359
360                 if (commit->object.flags & UNINTERESTING) {
361                         mark_tree_uninteresting(commit->tree);
362                         continue;
363                 }
364                 mark_edge_parents_uninteresting(commit);
365         }
366 }
367
368 int main(int argc, const char **argv)
369 {
370         struct commit_list *list;
371         int i;
372
373         argc = setup_revisions(argc, argv, &revs, NULL);
374
375         for (i = 1 ; i < argc; i++) {
376                 const char *arg = argv[i];
377
378                 /* accept -<digit>, like traditilnal "head" */
379                 if ((*arg == '-') && isdigit(arg[1])) {
380                         revs.max_count = atoi(arg + 1);
381                         continue;
382                 }
383                 if (!strcmp(arg, "-n")) {
384                         if (++i >= argc)
385                                 die("-n requires an argument");
386                         revs.max_count = atoi(argv[i]);
387                         continue;
388                 }
389                 if (!strncmp(arg,"-n",2)) {
390                         revs.max_count = atoi(arg + 2);
391                         continue;
392                 }
393                 if (!strcmp(arg, "--header")) {
394                         verbose_header = 1;
395                         continue;
396                 }
397                 if (!strcmp(arg, "--no-abbrev")) {
398                         abbrev = 0;
399                         continue;
400                 }
401                 if (!strncmp(arg, "--abbrev=", 9)) {
402                         abbrev = strtoul(arg + 9, NULL, 10);
403                         if (abbrev && abbrev < MINIMUM_ABBREV)
404                                 abbrev = MINIMUM_ABBREV;
405                         else if (40 < abbrev)
406                                 abbrev = 40;
407                         continue;
408                 }
409                 if (!strncmp(arg, "--pretty", 8)) {
410                         commit_format = get_commit_format(arg+8);
411                         verbose_header = 1;
412                         hdr_termination = '\n';
413                         if (commit_format == CMIT_FMT_ONELINE)
414                                 commit_prefix = "";
415                         else
416                                 commit_prefix = "commit ";
417                         continue;
418                 }
419                 if (!strncmp(arg, "--no-merges", 11)) {
420                         no_merges = 1;
421                         continue;
422                 }
423                 if (!strcmp(arg, "--parents")) {
424                         show_parents = 1;
425                         continue;
426                 }
427                 if (!strcmp(arg, "--bisect")) {
428                         bisect_list = 1;
429                         continue;
430                 }
431                 if (!strcmp(arg, "--merge-order")) {
432                         merge_order = 1;
433                         continue;
434                 }
435                 if (!strcmp(arg, "--show-breaks")) {
436                         show_breaks = 1;
437                         continue;
438                 }
439                 usage(rev_list_usage);
440
441         }
442
443         list = revs.commits;
444
445         if (!list &&
446             (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
447                 usage(rev_list_usage);
448
449         prepare_revision_walk(&revs);
450         if (revs.tree_objects)
451                 mark_edges_uninteresting(revs.commits);
452
453         if (bisect_list)
454                 revs.commits = find_bisection(revs.commits);
455
456         save_commit_buffer = verbose_header;
457         track_object_refs = 0;
458
459         if (!merge_order) {
460                 show_commit_list(&revs);
461         } else {
462 #ifndef NO_OPENSSL
463                 if (sort_list_in_merge_order(list, &process_commit)) {
464                         die("merge order sort failed\n");
465                 }
466 #else
467                 die("merge order sort unsupported, OpenSSL not linked");
468 #endif
469         }
470
471         return 0;
472 }