Merge branch 'lt/fix-sol-pack'
[git/trast.git] / rev-list.c
blob22141e2b045bb4e54709ec16c83942fcfdd44ed2
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 "tree-walk.h"
8 #include "revision.h"
10 /* bits #0-5 in revision.h */
12 #define COUNTED (1u<<6)
14 static const char rev_list_usage[] =
15 "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
16 " limiting output:\n"
17 " --max-count=nr\n"
18 " --max-age=epoch\n"
19 " --min-age=epoch\n"
20 " --sparse\n"
21 " --no-merges\n"
22 " --remove-empty\n"
23 " --all\n"
24 " ordering output:\n"
25 " --topo-order\n"
26 " --date-order\n"
27 " formatting output:\n"
28 " --parents\n"
29 " --objects | --objects-edge\n"
30 " --unpacked\n"
31 " --header | --pretty\n"
32 " --abbrev=nr | --no-abbrev\n"
33 " special purpose:\n"
34 " --bisect"
37 struct rev_info revs;
39 static int bisect_list = 0;
40 static int verbose_header = 0;
41 static int abbrev = DEFAULT_ABBREV;
42 static int show_timestamp = 0;
43 static int hdr_termination = 0;
44 static const char *commit_prefix = "";
45 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
47 static void show_commit(struct commit *commit)
49 if (show_timestamp)
50 printf("%lu ", commit->date);
51 if (commit_prefix[0])
52 fputs(commit_prefix, stdout);
53 if (commit->object.flags & BOUNDARY)
54 putchar('-');
55 fputs(sha1_to_hex(commit->object.sha1), stdout);
56 if (revs.parents) {
57 struct commit_list *parents = commit->parents;
58 while (parents) {
59 struct object *o = &(parents->item->object);
60 parents = parents->next;
61 if (o->flags & TMP_MARK)
62 continue;
63 printf(" %s", sha1_to_hex(o->sha1));
64 o->flags |= TMP_MARK;
66 /* TMP_MARK is a general purpose flag that can
67 * be used locally, but the user should clean
68 * things up after it is done with them.
70 for (parents = commit->parents;
71 parents;
72 parents = parents->next)
73 parents->item->object.flags &= ~TMP_MARK;
75 if (commit_format == CMIT_FMT_ONELINE)
76 putchar(' ');
77 else
78 putchar('\n');
80 if (verbose_header) {
81 static char pretty_header[16384];
82 pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
83 printf("%s%c", pretty_header, hdr_termination);
85 fflush(stdout);
88 static struct object_list **process_blob(struct blob *blob,
89 struct object_list **p,
90 struct name_path *path,
91 const char *name)
93 struct object *obj = &blob->object;
95 if (!revs.blob_objects)
96 return p;
97 if (obj->flags & (UNINTERESTING | SEEN))
98 return p;
99 obj->flags |= SEEN;
100 return add_object(obj, p, path, name);
103 static struct object_list **process_tree(struct tree *tree,
104 struct object_list **p,
105 struct name_path *path,
106 const char *name)
108 struct object *obj = &tree->object;
109 struct tree_entry_list *entry;
110 struct name_path me;
112 if (!revs.tree_objects)
113 return p;
114 if (obj->flags & (UNINTERESTING | SEEN))
115 return p;
116 if (parse_tree(tree) < 0)
117 die("bad tree object %s", sha1_to_hex(obj->sha1));
118 obj->flags |= SEEN;
119 p = add_object(obj, p, path, name);
120 me.up = path;
121 me.elem = name;
122 me.elem_len = strlen(name);
123 entry = tree->entries;
124 tree->entries = NULL;
125 while (entry) {
126 struct tree_entry_list *next = entry->next;
127 if (entry->directory)
128 p = process_tree(entry->item.tree, p, &me, entry->name);
129 else
130 p = process_blob(entry->item.blob, p, &me, entry->name);
131 free(entry);
132 entry = next;
134 return p;
137 static void show_commit_list(struct rev_info *revs)
139 struct commit *commit;
140 struct object_list *objects = NULL, **p = &objects, *pending;
142 while ((commit = get_revision(revs)) != NULL) {
143 p = process_tree(commit->tree, p, NULL, "");
144 show_commit(commit);
146 for (pending = revs->pending_objects; pending; pending = pending->next) {
147 struct object *obj = pending->item;
148 const char *name = pending->name;
149 if (obj->flags & (UNINTERESTING | SEEN))
150 continue;
151 if (obj->type == tag_type) {
152 obj->flags |= SEEN;
153 p = add_object(obj, p, NULL, name);
154 continue;
156 if (obj->type == tree_type) {
157 p = process_tree((struct tree *)obj, p, NULL, name);
158 continue;
160 if (obj->type == blob_type) {
161 p = process_blob((struct blob *)obj, p, NULL, name);
162 continue;
164 die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
166 while (objects) {
167 /* An object with name "foo\n0000000..." can be used to
168 * confuse downstream git-pack-objects very badly.
170 const char *ep = strchr(objects->name, '\n');
171 if (ep) {
172 printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
173 (int) (ep - objects->name),
174 objects->name);
176 else
177 printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
178 objects = objects->next;
183 * This is a truly stupid algorithm, but it's only
184 * used for bisection, and we just don't care enough.
186 * We care just barely enough to avoid recursing for
187 * non-merge entries.
189 static int count_distance(struct commit_list *entry)
191 int nr = 0;
193 while (entry) {
194 struct commit *commit = entry->item;
195 struct commit_list *p;
197 if (commit->object.flags & (UNINTERESTING | COUNTED))
198 break;
199 if (!revs.prune_fn || (commit->object.flags & TREECHANGE))
200 nr++;
201 commit->object.flags |= COUNTED;
202 p = commit->parents;
203 entry = p;
204 if (p) {
205 p = p->next;
206 while (p) {
207 nr += count_distance(p);
208 p = p->next;
213 return nr;
216 static void clear_distance(struct commit_list *list)
218 while (list) {
219 struct commit *commit = list->item;
220 commit->object.flags &= ~COUNTED;
221 list = list->next;
225 static struct commit_list *find_bisection(struct commit_list *list)
227 int nr, closest;
228 struct commit_list *p, *best;
230 nr = 0;
231 p = list;
232 while (p) {
233 if (!revs.prune_fn || (p->item->object.flags & TREECHANGE))
234 nr++;
235 p = p->next;
237 closest = 0;
238 best = list;
240 for (p = list; p; p = p->next) {
241 int distance;
243 if (revs.prune_fn && !(p->item->object.flags & TREECHANGE))
244 continue;
246 distance = count_distance(p);
247 clear_distance(list);
248 if (nr - distance < distance)
249 distance = nr - distance;
250 if (distance > closest) {
251 best = p;
252 closest = distance;
255 if (best)
256 best->next = NULL;
257 return best;
260 static void mark_edge_parents_uninteresting(struct commit *commit)
262 struct commit_list *parents;
264 for (parents = commit->parents; parents; parents = parents->next) {
265 struct commit *parent = parents->item;
266 if (!(parent->object.flags & UNINTERESTING))
267 continue;
268 mark_tree_uninteresting(parent->tree);
269 if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
270 parent->object.flags |= SHOWN;
271 printf("-%s\n", sha1_to_hex(parent->object.sha1));
276 static void mark_edges_uninteresting(struct commit_list *list)
278 for ( ; list; list = list->next) {
279 struct commit *commit = list->item;
281 if (commit->object.flags & UNINTERESTING) {
282 mark_tree_uninteresting(commit->tree);
283 continue;
285 mark_edge_parents_uninteresting(commit);
289 int main(int argc, const char **argv)
291 struct commit_list *list;
292 int i;
294 argc = setup_revisions(argc, argv, &revs, NULL);
296 for (i = 1 ; i < argc; i++) {
297 const char *arg = argv[i];
299 /* accept -<digit>, like traditilnal "head" */
300 if ((*arg == '-') && isdigit(arg[1])) {
301 revs.max_count = atoi(arg + 1);
302 continue;
304 if (!strcmp(arg, "-n")) {
305 if (++i >= argc)
306 die("-n requires an argument");
307 revs.max_count = atoi(argv[i]);
308 continue;
310 if (!strncmp(arg,"-n",2)) {
311 revs.max_count = atoi(arg + 2);
312 continue;
314 if (!strcmp(arg, "--header")) {
315 verbose_header = 1;
316 continue;
318 if (!strcmp(arg, "--no-abbrev")) {
319 abbrev = 0;
320 continue;
322 if (!strncmp(arg, "--abbrev=", 9)) {
323 abbrev = strtoul(arg + 9, NULL, 10);
324 if (abbrev && abbrev < MINIMUM_ABBREV)
325 abbrev = MINIMUM_ABBREV;
326 else if (40 < abbrev)
327 abbrev = 40;
328 continue;
330 if (!strncmp(arg, "--pretty", 8)) {
331 commit_format = get_commit_format(arg+8);
332 verbose_header = 1;
333 hdr_termination = '\n';
334 if (commit_format == CMIT_FMT_ONELINE)
335 commit_prefix = "";
336 else
337 commit_prefix = "commit ";
338 continue;
340 if (!strcmp(arg, "--timestamp")) {
341 show_timestamp = 1;
342 continue;
344 if (!strcmp(arg, "--bisect")) {
345 bisect_list = 1;
346 continue;
348 usage(rev_list_usage);
352 list = revs.commits;
354 if (!list &&
355 (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
356 usage(rev_list_usage);
358 save_commit_buffer = verbose_header;
359 track_object_refs = 0;
361 prepare_revision_walk(&revs);
362 if (revs.tree_objects)
363 mark_edges_uninteresting(revs.commits);
365 if (bisect_list)
366 revs.commits = find_bisection(revs.commits);
368 show_commit_list(&revs);
370 return 0;