8 #define INTERESTING (1u << 1)
9 #define COUNTED (1u << 2)
10 #define SHOWN (LAST_EPOCH_FLAG << 2)
12 static const char rev_list_usage[] =
13 "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
19 " --merge-order [ --show-breaks ]";
21 static int bisect_list = 0;
22 static int tag_objects = 0;
23 static int tree_objects = 0;
24 static int blob_objects = 0;
25 static int verbose_header = 0;
26 static int show_parents = 0;
27 static int hdr_termination = 0;
28 static const char *prefix = "";
29 static unsigned long max_age = -1;
30 static unsigned long min_age = -1;
31 static int max_count = -1;
32 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
33 static int merge_order = 0;
34 static int show_breaks = 0;
35 static int stop_traversal = 0;
37 static void show_commit(struct commit *commit)
39 commit->object.flags |= SHOWN;
42 if (commit->object.flags & DISCONTINUITY) {
44 } else if (commit->object.flags & BOUNDARY) {
48 printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
50 struct commit_list *parents = commit->parents;
52 printf(" %s", sha1_to_hex(parents->item->object.sha1));
53 parents = parents->next;
58 static char pretty_header[16384];
59 pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
60 printf("%s%c", pretty_header, hdr_termination);
64 static int filter_commit(struct commit * commit)
66 if (merge_order && stop_traversal && commit->object.flags & BOUNDARY)
68 if (commit->object.flags & (UNINTERESTING|SHOWN))
70 if (min_age != -1 && (commit->date > min_age))
72 if (max_age != -1 && (commit->date < max_age)) {
80 if (max_count != -1 && !max_count--)
85 static int process_commit(struct commit * commit)
87 int action=filter_commit(commit);
93 if (action == CONTINUE) {
102 static struct object_list **add_object(struct object *obj, struct object_list **p, const char *name)
104 struct object_list *entry = xmalloc(sizeof(*entry));
112 static struct object_list **process_blob(struct blob *blob, struct object_list **p, const char *name)
114 struct object *obj = &blob->object;
118 if (obj->flags & (UNINTERESTING | SEEN))
121 return add_object(obj, p, name);
124 static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
126 struct object *obj = &tree->object;
127 struct tree_entry_list *entry;
131 if (obj->flags & (UNINTERESTING | SEEN))
133 if (parse_tree(tree) < 0)
134 die("bad tree object %s", sha1_to_hex(obj->sha1));
136 p = add_object(obj, p, name);
137 for (entry = tree->entries ; entry ; entry = entry->next) {
138 if (entry->directory)
139 p = process_tree(entry->item.tree, p, entry->name);
141 p = process_blob(entry->item.blob, p, entry->name);
146 static void show_commit_list(struct commit_list *list)
148 struct object_list *objects = NULL, **p = &objects;
150 struct commit *commit = pop_most_recent_commit(&list, SEEN);
152 p = process_tree(commit->tree, p, "");
153 if (process_commit(commit) == STOP)
157 printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
158 objects = objects->next;
162 static void mark_blob_uninteresting(struct blob *blob)
166 if (blob->object.flags & UNINTERESTING)
168 blob->object.flags |= UNINTERESTING;
171 static void mark_tree_uninteresting(struct tree *tree)
173 struct object *obj = &tree->object;
174 struct tree_entry_list *entry;
178 if (obj->flags & UNINTERESTING)
180 obj->flags |= UNINTERESTING;
181 if (parse_tree(tree) < 0)
182 die("bad tree %s", sha1_to_hex(obj->sha1));
183 entry = tree->entries;
185 if (entry->directory)
186 mark_tree_uninteresting(entry->item.tree);
188 mark_blob_uninteresting(entry->item.blob);
193 static void mark_parents_uninteresting(struct commit *commit)
195 struct commit_list *parents = commit->parents;
198 mark_tree_uninteresting(commit->tree);
200 struct commit *commit = parents->item;
201 commit->object.flags |= UNINTERESTING;
202 parents = parents->next;
206 static int everybody_uninteresting(struct commit_list *list)
209 struct commit *commit = list->item;
211 if (commit->object.flags & UNINTERESTING)
219 * This is a truly stupid algorithm, but it's only
220 * used for bisection, and we just don't care enough.
222 * We care just barely enough to avoid recursing for
225 static int count_distance(struct commit_list *entry)
230 struct commit *commit = entry->item;
231 struct commit_list *p;
233 if (commit->object.flags & (UNINTERESTING | COUNTED))
236 commit->object.flags |= COUNTED;
242 nr += count_distance(p);
250 static void clear_distance(struct commit_list *list)
253 struct commit *commit = list->item;
254 commit->object.flags &= ~COUNTED;
259 static struct commit_list *find_bisection(struct commit_list *list)
262 struct commit_list *p, *best;
275 int distance = count_distance(p);
276 clear_distance(list);
277 if (nr - distance < distance)
278 distance = nr - distance;
279 if (distance > closest) {
290 struct commit_list *limit_list(struct commit_list *list)
292 struct commit_list *newlist = NULL;
293 struct commit_list **p = &newlist;
295 struct commit *commit = pop_most_recent_commit(&list, SEEN);
296 struct object *obj = &commit->object;
298 if (obj->flags & UNINTERESTING) {
299 mark_parents_uninteresting(commit);
300 if (everybody_uninteresting(list))
304 p = &commit_list_insert(commit, p)->next;
307 newlist = find_bisection(newlist);
311 static struct commit *get_commit_reference(const char *name, unsigned int flags)
313 unsigned char sha1[20];
314 struct commit *commit;
316 if (get_sha1(name, sha1))
317 usage(rev_list_usage);
318 commit = lookup_commit_reference(sha1);
319 if (!commit || parse_commit(commit) < 0)
320 die("bad commit object %s", name);
321 commit->object.flags |= flags;
325 int main(int argc, char **argv)
327 struct commit_list *list = NULL;
330 for (i = 1 ; i < argc; i++) {
333 struct commit *commit;
335 if (!strncmp(arg, "--max-count=", 12)) {
336 max_count = atoi(arg + 12);
339 if (!strncmp(arg, "--max-age=", 10)) {
340 max_age = atoi(arg + 10);
343 if (!strncmp(arg, "--min-age=", 10)) {
344 min_age = atoi(arg + 10);
347 if (!strcmp(arg, "--header")) {
351 if (!strncmp(arg, "--pretty", 8)) {
352 commit_format = get_commit_format(arg+8);
354 hdr_termination = '\n';
358 if (!strcmp(arg, "--parents")) {
362 if (!strcmp(arg, "--bisect")) {
366 if (!strcmp(arg, "--objects")) {
372 if (!strncmp(arg, "--merge-order", 13)) {
376 if (!strncmp(arg, "--show-breaks", 13)) {
383 flags = UNINTERESTING;
387 if (show_breaks && !merge_order)
388 usage(rev_list_usage);
389 commit = get_commit_reference(arg, flags);
392 commit_list_insert(commit, &list);
396 usage(rev_list_usage);
400 list = limit_list(list);
401 show_commit_list(list);
403 if (sort_list_in_merge_order(list, &process_commit)) {
404 die("merge order sort failed\n");