Code

Add "--topo-order" flag to use new topological sort
[git.git] / rev-list.c
1 #include "cache.h"
2 #include "tag.h"
3 #include "commit.h"
4 #include "tree.h"
5 #include "blob.h"
6 #include "epoch.h"
8 #define SEEN            (1u << 0)
9 #define INTERESTING     (1u << 1)
10 #define COUNTED         (1u << 2)
11 #define SHOWN           (1u << 3)
12 #define DUPCHECK        (1u << 4)
14 static const char rev_list_usage[] =
15         "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
16                       "  --max-count=nr\n"
17                       "  --max-age=epoch\n"
18                       "  --min-age=epoch\n"
19                       "  --bisect\n"
20                       "  --objects\n"
21                       "  --unpacked\n"
22                       "  --header\n"
23                       "  --pretty\n"
24                       "  --merge-order [ --show-breaks ]";
26 static int unpacked = 0;
27 static int bisect_list = 0;
28 static int tag_objects = 0;
29 static int tree_objects = 0;
30 static int blob_objects = 0;
31 static int verbose_header = 0;
32 static int show_parents = 0;
33 static int hdr_termination = 0;
34 static const char *prefix = "";
35 static unsigned long max_age = -1;
36 static unsigned long min_age = -1;
37 static int max_count = -1;
38 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
39 static int merge_order = 0;
40 static int show_breaks = 0;
41 static int stop_traversal = 0;
42 static int topo_order = 0;
44 static void show_commit(struct commit *commit)
45 {
46         commit->object.flags |= SHOWN;
47         if (show_breaks) {
48                 prefix = "| ";
49                 if (commit->object.flags & DISCONTINUITY) {
50                         prefix = "^ ";     
51                 } else if (commit->object.flags & BOUNDARY) {
52                         prefix = "= ";
53                 } 
54         }                       
55         printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
56         if (show_parents) {
57                 struct commit_list *parents = commit->parents;
58                 while (parents) {
59                         printf(" %s", sha1_to_hex(parents->item->object.sha1));
60                         parents = parents->next;
61                 }
62         }
63         putchar('\n');
64         if (verbose_header) {
65                 static char pretty_header[16384];
66                 pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
67                 printf("%s%c", pretty_header, hdr_termination);
68         }
69         fflush(stdout);
70 }
72 static int filter_commit(struct commit * commit)
73 {
74         if (merge_order && stop_traversal && commit->object.flags & BOUNDARY)
75                 return STOP;
76         if (commit->object.flags & (UNINTERESTING|SHOWN))
77                 return CONTINUE;
78         if (min_age != -1 && (commit->date > min_age))
79                 return CONTINUE;
80         if (max_age != -1 && (commit->date < max_age)) {
81                 if (!merge_order)
82                         return STOP;
83                 else {
84                         stop_traversal = 1;
85                         return CONTINUE;
86                 }
87         }
88         if (max_count != -1 && !max_count--)
89                 return STOP;
90         return DO;
91 }
93 static int process_commit(struct commit * commit)
94 {
95         int action=filter_commit(commit);
97         if (action == STOP) {
98                 return STOP;
99         }
101         if (action == CONTINUE) {
102                 return CONTINUE;
103         }
105         show_commit(commit);
107         return CONTINUE;
110 static struct object_list **add_object(struct object *obj, struct object_list **p, const char *name)
112         struct object_list *entry = xmalloc(sizeof(*entry));
113         entry->item = obj;
114         entry->next = *p;
115         entry->name = name;
116         *p = entry;
117         return &entry->next;
120 static struct object_list **process_blob(struct blob *blob, struct object_list **p, const char *name)
122         struct object *obj = &blob->object;
124         if (!blob_objects)
125                 return p;
126         if (obj->flags & (UNINTERESTING | SEEN))
127                 return p;
128         obj->flags |= SEEN;
129         return add_object(obj, p, name);
132 static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
134         struct object *obj = &tree->object;
135         struct tree_entry_list *entry;
137         if (!tree_objects)
138                 return p;
139         if (obj->flags & (UNINTERESTING | SEEN))
140                 return p;
141         if (parse_tree(tree) < 0)
142                 die("bad tree object %s", sha1_to_hex(obj->sha1));
143         obj->flags |= SEEN;
144         p = add_object(obj, p, name);
145         for (entry = tree->entries ; entry ; entry = entry->next) {
146                 if (entry->directory)
147                         p = process_tree(entry->item.tree, p, entry->name);
148                 else
149                         p = process_blob(entry->item.blob, p, entry->name);
150         }
151         return p;
154 static struct object_list *pending_objects = NULL;
156 static void show_commit_list(struct commit_list *list)
158         struct object_list *objects = NULL, **p = &objects, *pending;
159         while (list) {
160                 struct commit *commit = pop_most_recent_commit(&list, SEEN);
162                 p = process_tree(commit->tree, p, "");
163                 if (process_commit(commit) == STOP)
164                         break;
165         }
166         for (pending = pending_objects; pending; pending = pending->next) {
167                 struct object *obj = pending->item;
168                 const char *name = pending->name;
169                 if (obj->flags & (UNINTERESTING | SEEN))
170                         continue;
171                 if (obj->type == tag_type) {
172                         obj->flags |= SEEN;
173                         p = add_object(obj, p, name);
174                         continue;
175                 }
176                 if (obj->type == tree_type) {
177                         p = process_tree((struct tree *)obj, p, name);
178                         continue;
179                 }
180                 if (obj->type == blob_type) {
181                         p = process_blob((struct blob *)obj, p, name);
182                         continue;
183                 }
184                 die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
185         }
186         while (objects) {
187                 printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
188                 objects = objects->next;
189         }
192 static void mark_blob_uninteresting(struct blob *blob)
194         if (!blob_objects)
195                 return;
196         if (blob->object.flags & UNINTERESTING)
197                 return;
198         blob->object.flags |= UNINTERESTING;
201 static void mark_tree_uninteresting(struct tree *tree)
203         struct object *obj = &tree->object;
204         struct tree_entry_list *entry;
206         if (!tree_objects)
207                 return;
208         if (obj->flags & UNINTERESTING)
209                 return;
210         obj->flags |= UNINTERESTING;
211         if (parse_tree(tree) < 0)
212                 die("bad tree %s", sha1_to_hex(obj->sha1));
213         entry = tree->entries;
214         while (entry) {
215                 if (entry->directory)
216                         mark_tree_uninteresting(entry->item.tree);
217                 else
218                         mark_blob_uninteresting(entry->item.blob);
219                 entry = entry->next;
220         }
223 static void mark_parents_uninteresting(struct commit *commit)
225         struct commit_list *parents = commit->parents;
227         if (tree_objects)
228                 mark_tree_uninteresting(commit->tree);
229         while (parents) {
230                 struct commit *commit = parents->item;
231                 commit->object.flags |= UNINTERESTING;
232                 parents = parents->next;
233         }
236 static int everybody_uninteresting(struct commit_list *list)
238         while (list) {
239                 struct commit *commit = list->item;
240                 list = list->next;
241                 if (commit->object.flags & UNINTERESTING)
242                         continue;
243                 return 0;
244         }
245         return 1;
248 /*
249  * This is a truly stupid algorithm, but it's only
250  * used for bisection, and we just don't care enough.
251  *
252  * We care just barely enough to avoid recursing for
253  * non-merge entries.
254  */
255 static int count_distance(struct commit_list *entry)
257         int nr = 0;
259         while (entry) {
260                 struct commit *commit = entry->item;
261                 struct commit_list *p;
263                 if (commit->object.flags & (UNINTERESTING | COUNTED))
264                         break;
265                 nr++;
266                 commit->object.flags |= COUNTED;
267                 p = commit->parents;
268                 entry = p;
269                 if (p) {
270                         p = p->next;
271                         while (p) {
272                                 nr += count_distance(p);
273                                 p = p->next;
274                         }
275                 }
276         }
277         return nr;
280 static void clear_distance(struct commit_list *list)
282         while (list) {
283                 struct commit *commit = list->item;
284                 commit->object.flags &= ~COUNTED;
285                 list = list->next;
286         }
289 static struct commit_list *find_bisection(struct commit_list *list)
291         int nr, closest;
292         struct commit_list *p, *best;
294         nr = 0;
295         p = list;
296         while (p) {
297                 nr++;
298                 p = p->next;
299         }
300         closest = 0;
301         best = list;
303         p = list;
304         while (p) {
305                 int distance = count_distance(p);
306                 clear_distance(list);
307                 if (nr - distance < distance)
308                         distance = nr - distance;
309                 if (distance > closest) {
310                         best = p;
311                         closest = distance;
312                 }
313                 p = p->next;
314         }
315         if (best)
316                 best->next = NULL;
317         return best;
320 static struct commit_list *limit_list(struct commit_list *list)
322         struct commit_list *newlist = NULL;
323         struct commit_list **p = &newlist;
324         while (list) {
325                 struct commit *commit = pop_most_recent_commit(&list, SEEN);
326                 struct object *obj = &commit->object;
328                 if (unpacked && has_sha1_pack(obj->sha1))
329                         obj->flags |= UNINTERESTING;
330                 if (obj->flags & UNINTERESTING) {
331                         mark_parents_uninteresting(commit);
332                         if (everybody_uninteresting(list))
333                                 break;
334                         continue;
335                 }
336                 p = &commit_list_insert(commit, p)->next;
337         }
338         if (bisect_list)
339                 newlist = find_bisection(newlist);
340         return newlist;
343 static void add_pending_object(struct object *obj, const char *name)
345         add_object(obj, &pending_objects, name);
348 static struct commit *get_commit_reference(const char *name, unsigned int flags)
350         unsigned char sha1[20];
351         struct object *object;
353         if (get_sha1(name, sha1))
354                 usage(rev_list_usage);
355         object = parse_object(sha1);
356         if (!object)
357                 die("bad object %s", name);
359         /*
360          * Tag object? Look what it points to..
361          */
362         if (object->type == tag_type) {
363                 struct tag *tag = (struct tag *) object;
364                 object->flags |= flags;
365                 if (tag_objects && !(object->flags & UNINTERESTING))
366                         add_pending_object(object, tag->tag);
367                 object = tag->tagged;
368         }
370         /*
371          * Commit object? Just return it, we'll do all the complex
372          * reachability crud.
373          */
374         if (object->type == commit_type) {
375                 struct commit *commit = (struct commit *)object;
376                 object->flags |= flags;
377                 if (parse_commit(commit) < 0)
378                         die("unable to parse commit %s", name);
379                 return commit;
380         }
382         /*
383          * Tree object? Either mark it uniniteresting, or add it
384          * to the list of objects to look at later..
385          */
386         if (object->type == tree_type) {
387                 struct tree *tree = (struct tree *)object;
388                 if (!tree_objects)
389                         return NULL;
390                 if (flags & UNINTERESTING) {
391                         mark_tree_uninteresting(tree);
392                         return NULL;
393                 }
394                 add_pending_object(object, "");
395                 return NULL;
396         }
398         /*
399          * Blob object? You know the drill by now..
400          */
401         if (object->type == blob_type) {
402                 struct blob *blob = (struct blob *)object;
403                 if (!blob_objects)
404                         return NULL;
405                 if (flags & UNINTERESTING) {
406                         mark_blob_uninteresting(blob);
407                         return NULL;
408                 }
409                 add_pending_object(object, "");
410                 return NULL;
411         }
412         die("%s is unknown object", name);
415 int main(int argc, char **argv)
417         struct commit_list *list = NULL;
418         struct commit_list *(*insert)(struct commit *, struct commit_list **);
419         int i, limited = 0;
421         insert = insert_by_date;
422         for (i = 1 ; i < argc; i++) {
423                 int flags;
424                 char *arg = argv[i];
425                 struct commit *commit;
427                 if (!strncmp(arg, "--max-count=", 12)) {
428                         max_count = atoi(arg + 12);
429                         continue;
430                 }
431                 if (!strncmp(arg, "--max-age=", 10)) {
432                         max_age = atoi(arg + 10);
433                         continue;
434                 }
435                 if (!strncmp(arg, "--min-age=", 10)) {
436                         min_age = atoi(arg + 10);
437                         continue;
438                 }
439                 if (!strcmp(arg, "--header")) {
440                         verbose_header = 1;
441                         continue;
442                 }
443                 if (!strncmp(arg, "--pretty", 8)) {
444                         commit_format = get_commit_format(arg+8);
445                         verbose_header = 1;
446                         hdr_termination = '\n';
447                         prefix = "commit ";
448                         continue;
449                 }
450                 if (!strcmp(arg, "--parents")) {
451                         show_parents = 1;
452                         continue;
453                 }
454                 if (!strcmp(arg, "--bisect")) {
455                         bisect_list = 1;
456                         continue;
457                 }
458                 if (!strcmp(arg, "--objects")) {
459                         tag_objects = 1;
460                         tree_objects = 1;
461                         blob_objects = 1;
462                         continue;
463                 }
464                 if (!strcmp(arg, "--unpacked")) {
465                         unpacked = 1;
466                         limited = 1;
467                         continue;
468                 }
469                 if (!strcmp(arg, "--merge-order")) {
470                         merge_order = 1;
471                         insert = commit_list_insert;
472                         continue;
473                 }
474                 if (!strcmp(arg, "--show-breaks")) {
475                         show_breaks = 1;
476                         continue;
477                 }
478                 if (!strcmp(arg, "--topo-order")) {
479                         topo_order = 1;
480                         continue;
481                 }
483                 flags = 0;
484                 if (*arg == '^') {
485                         flags = UNINTERESTING;
486                         arg++;
487                         limited = 1;
488                 }
489                 if (show_breaks && !merge_order)
490                         usage(rev_list_usage);
491                 commit = get_commit_reference(arg, flags);
492                 if (!commit)
493                         continue;
494                 if (commit->object.flags & DUPCHECK)
495                         continue;
496                 commit->object.flags |= DUPCHECK;
497                 insert(commit, &list);
498         }
500         if (!merge_order) {             
501                 if (limited)
502                         list = limit_list(list);
503                 if (topo_order)
504                         sort_in_topological_order(&list);
505                 show_commit_list(list);
506         } else {
507                 if (sort_list_in_merge_order(list, &process_commit)) {
508                           die("merge order sort failed\n");
509                 }
510         }
512         return 0;