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