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 tree_objects = 0;
23 static int blob_objects = 0;
24 static int verbose_header = 0;
25 static int show_parents = 0;
26 static int hdr_termination = 0;
27 static const char *prefix = "";
28 static unsigned long max_age = -1;
29 static unsigned long min_age = -1;
30 static int max_count = -1;
31 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
32 static int merge_order = 0;
33 static int show_breaks = 0;
34 static int stop_traversal = 0;
36 static void show_commit(struct commit *commit)
38 commit->object.flags |= SHOWN;
41 if (commit->object.flags & DISCONTINUITY) {
43 } else if (commit->object.flags & BOUNDARY) {
47 printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
49 struct commit_list *parents = commit->parents;
51 printf(" %s", sha1_to_hex(parents->item->object.sha1));
52 parents = parents->next;
57 static char pretty_header[16384];
58 pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
59 printf("%s%c", pretty_header, hdr_termination);
63 static int filter_commit(struct commit * commit)
65 if (merge_order && stop_traversal && commit->object.flags & BOUNDARY)
67 if (commit->object.flags & (UNINTERESTING|SHOWN))
69 if (min_age != -1 && (commit->date > min_age))
71 if (max_age != -1 && (commit->date < max_age)) {
79 if (max_count != -1 && !max_count--)
84 static int process_commit(struct commit * commit)
86 int action=filter_commit(commit);
92 if (action == CONTINUE) {
101 static struct object_list **add_object(struct object *obj, struct object_list **p)
103 struct object_list *entry = xmalloc(sizeof(*entry));
110 static struct object_list **process_blob(struct blob *blob, struct object_list **p)
112 struct object *obj = &blob->object;
116 if (obj->flags & (UNINTERESTING | SEEN))
119 return add_object(obj, p);
122 static struct object_list **process_tree(struct tree *tree, struct object_list **p)
124 struct object *obj = &tree->object;
125 struct tree_entry_list *entry;
129 if (obj->flags & (UNINTERESTING | SEEN))
131 if (parse_tree(tree) < 0)
132 die("bad tree object %s", sha1_to_hex(obj->sha1));
134 p = add_object(obj, p);
135 for (entry = tree->entries ; entry ; entry = entry->next) {
136 if (entry->directory)
137 p = process_tree(entry->item.tree, p);
139 p = process_blob(entry->item.blob, p);
144 static void show_commit_list(struct commit_list *list)
146 struct object_list *objects = NULL, **p = &objects;
148 struct commit *commit = pop_most_recent_commit(&list, SEEN);
150 p = process_tree(commit->tree, p);
151 if (process_commit(commit) == STOP)
155 puts(sha1_to_hex(objects->item->sha1));
156 objects = objects->next;
160 static void mark_blob_uninteresting(struct blob *blob)
164 if (blob->object.flags & UNINTERESTING)
166 blob->object.flags |= UNINTERESTING;
169 static void mark_tree_uninteresting(struct tree *tree)
171 struct object *obj = &tree->object;
172 struct tree_entry_list *entry;
176 if (obj->flags & UNINTERESTING)
178 obj->flags |= UNINTERESTING;
179 if (parse_tree(tree) < 0)
180 die("bad tree %s", sha1_to_hex(obj->sha1));
181 entry = tree->entries;
183 if (entry->directory)
184 mark_tree_uninteresting(entry->item.tree);
186 mark_blob_uninteresting(entry->item.blob);
191 static void mark_parents_uninteresting(struct commit *commit)
193 struct commit_list *parents = commit->parents;
196 mark_tree_uninteresting(commit->tree);
198 struct commit *commit = parents->item;
199 commit->object.flags |= UNINTERESTING;
200 parents = parents->next;
204 static int everybody_uninteresting(struct commit_list *list)
207 struct commit *commit = list->item;
209 if (commit->object.flags & UNINTERESTING)
217 * This is a truly stupid algorithm, but it's only
218 * used for bisection, and we just don't care enough.
220 * We care just barely enough to avoid recursing for
223 static int count_distance(struct commit_list *entry)
228 struct commit *commit = entry->item;
229 struct commit_list *p;
231 if (commit->object.flags & (UNINTERESTING | COUNTED))
234 commit->object.flags |= COUNTED;
240 nr += count_distance(p);
248 static void clear_distance(struct commit_list *list)
251 struct commit *commit = list->item;
252 commit->object.flags &= ~COUNTED;
257 static struct commit_list *find_bisection(struct commit_list *list)
260 struct commit_list *p, *best;
273 int distance = count_distance(p);
274 clear_distance(list);
275 if (nr - distance < distance)
276 distance = nr - distance;
277 if (distance > closest) {
288 struct commit_list *limit_list(struct commit_list *list)
290 struct commit_list *newlist = NULL;
291 struct commit_list **p = &newlist;
293 struct commit *commit = pop_most_recent_commit(&list, SEEN);
294 struct object *obj = &commit->object;
296 if (obj->flags & UNINTERESTING) {
297 mark_parents_uninteresting(commit);
298 if (everybody_uninteresting(list))
302 p = &commit_list_insert(commit, p)->next;
305 newlist = find_bisection(newlist);
309 static enum cmit_fmt get_commit_format(const char *arg)
312 return CMIT_FMT_DEFAULT;
313 if (!strcmp(arg, "=raw"))
315 if (!strcmp(arg, "=medium"))
316 return CMIT_FMT_MEDIUM;
317 if (!strcmp(arg, "=short"))
318 return CMIT_FMT_SHORT;
319 usage(rev_list_usage);
323 int main(int argc, char **argv)
325 struct commit_list *list = NULL;
328 for (i = 1 ; i < argc; i++) {
331 unsigned char sha1[20];
332 struct commit *commit;
334 if (!strncmp(arg, "--max-count=", 12)) {
335 max_count = atoi(arg + 12);
338 if (!strncmp(arg, "--max-age=", 10)) {
339 max_age = atoi(arg + 10);
342 if (!strncmp(arg, "--min-age=", 10)) {
343 min_age = atoi(arg + 10);
346 if (!strcmp(arg, "--header")) {
350 if (!strncmp(arg, "--pretty", 8)) {
351 commit_format = get_commit_format(arg+8);
353 hdr_termination = '\n';
357 if (!strcmp(arg, "--parents")) {
361 if (!strcmp(arg, "--bisect")) {
365 if (!strcmp(arg, "--objects")) {
370 if (!strncmp(arg, "--merge-order", 13)) {
374 if (!strncmp(arg, "--show-breaks", 13)) {
381 flags = UNINTERESTING;
385 if (get_sha1(arg, sha1) || (show_breaks && !merge_order))
386 usage(rev_list_usage);
387 commit = lookup_commit_reference(sha1);
388 if (!commit || parse_commit(commit) < 0)
389 die("bad commit object %s", arg);
390 commit->object.flags |= flags;
391 commit_list_insert(commit, &list);
395 usage(rev_list_usage);
399 list = limit_list(list);
400 show_commit_list(list);
402 if (sort_list_in_merge_order(list, &process_commit)) {
403 die("merge order sort failed\n");