]> git.ipfire.org Git - thirdparty/git.git/blame - rev-list.c
Add git-clean command
[thirdparty/git.git] / rev-list.c
CommitLineData
64745109 1#include "cache.h"
e091eb93 2#include "refs.h"
36f8d174 3#include "tag.h"
64745109 4#include "commit.h"
9de48752
LT
5#include "tree.h"
6#include "blob.h"
1b0c7174 7#include "tree-walk.h"
ae563542
LT
8#include "revision.h"
9
384e99a4 10/* bits #0-5 in revision.h */
64745109 11
384e99a4 12#define COUNTED (1u<<6)
8906300f 13
a6f68d47 14static const char rev_list_usage[] =
69e0c256
JH
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"
93b74bca 22" --remove-empty\n"
69e0c256
JH
23" --all\n"
24" ordering output:\n"
69e0c256 25" --topo-order\n"
4c8725f1 26" --date-order\n"
69e0c256
JH
27" formatting output:\n"
28" --parents\n"
c6496575 29" --objects | --objects-edge\n"
69e0c256
JH
30" --unpacked\n"
31" --header | --pretty\n"
9da5c2f0 32" --abbrev=nr | --no-abbrev\n"
69e0c256
JH
33" special purpose:\n"
34" --bisect"
35;
a6f68d47 36
ae563542
LT
37struct rev_info revs;
38
8b3a1e05 39static int bisect_list = 0;
81f2bb1f 40static int verbose_header = 0;
9da5c2f0 41static int abbrev = DEFAULT_ABBREV;
dc68c4ff 42static int show_timestamp = 0;
81f2bb1f 43static int hdr_termination = 0;
d998a089 44static const char *commit_prefix = "";
000182ea 45static enum cmit_fmt commit_format = CMIT_FMT_RAW;
e646de0d 46
81f2bb1f
LT
47static void show_commit(struct commit *commit)
48{
dc68c4ff
JH
49 if (show_timestamp)
50 printf("%lu ", commit->date);
51 if (commit_prefix[0])
52 fputs(commit_prefix, stdout);
384e99a4
JH
53 if (commit->object.flags & BOUNDARY)
54 putchar('-');
dc68c4ff 55 fputs(sha1_to_hex(commit->object.sha1), stdout);
7b0c9966 56 if (revs.parents) {
81f2bb1f
LT
57 struct commit_list *parents = commit->parents;
58 while (parents) {
88494423 59 struct object *o = &(parents->item->object);
81f2bb1f 60 parents = parents->next;
88494423
JH
61 if (o->flags & TMP_MARK)
62 continue;
63 printf(" %s", sha1_to_hex(o->sha1));
64 o->flags |= TMP_MARK;
81f2bb1f 65 }
88494423
JH
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.
69 */
70 for (parents = commit->parents;
71 parents;
72 parents = parents->next)
73 parents->item->object.flags &= ~TMP_MARK;
81f2bb1f 74 }
d87449c5
JH
75 if (commit_format == CMIT_FMT_ONELINE)
76 putchar(' ');
77 else
78 putchar('\n');
79
81f2bb1f 80 if (verbose_header) {
000182ea 81 static char pretty_header[16384];
9da5c2f0 82 pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
000182ea 83 printf("%s%c", pretty_header, hdr_termination);
7620d39f
LT
84 }
85 fflush(stdout);
a3437b8c
JS
86}
87
e646de0d
JH
88static struct object_list **process_blob(struct blob *blob,
89 struct object_list **p,
90 struct name_path *path,
91 const char *name)
9de48752
LT
92{
93 struct object *obj = &blob->object;
94
ae563542 95 if (!revs.blob_objects)
9de48752
LT
96 return p;
97 if (obj->flags & (UNINTERESTING | SEEN))
98 return p;
99 obj->flags |= SEEN;
e646de0d 100 return add_object(obj, p, path, name);
9de48752
LT
101}
102
e646de0d
JH
103static struct object_list **process_tree(struct tree *tree,
104 struct object_list **p,
105 struct name_path *path,
106 const char *name)
9de48752
LT
107{
108 struct object *obj = &tree->object;
109 struct tree_entry_list *entry;
e646de0d 110 struct name_path me;
9de48752 111
ae563542 112 if (!revs.tree_objects)
9de48752
LT
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;
e646de0d
JH
119 p = add_object(obj, p, path, name);
120 me.up = path;
121 me.elem = name;
122 me.elem_len = strlen(name);
b0d8923e
LT
123 entry = tree->entries;
124 tree->entries = NULL;
125 while (entry) {
126 struct tree_entry_list *next = entry->next;
9de48752 127 if (entry->directory)
e646de0d 128 p = process_tree(entry->item.tree, p, &me, entry->name);
9de48752 129 else
e646de0d 130 p = process_blob(entry->item.blob, p, &me, entry->name);
b0d8923e
LT
131 free(entry);
132 entry = next;
9de48752
LT
133 }
134 return p;
135}
136
a4a88b2b 137static void show_commit_list(struct rev_info *revs)
81f2bb1f 138{
a4a88b2b 139 struct commit *commit;
36f8d174 140 struct object_list *objects = NULL, **p = &objects, *pending;
81f2bb1f 141
a4a88b2b 142 while ((commit = get_revision(revs)) != NULL) {
e646de0d 143 p = process_tree(commit->tree, p, NULL, "");
765ac8ec 144 show_commit(commit);
81f2bb1f 145 }
a4a88b2b 146 for (pending = revs->pending_objects; pending; pending = pending->next) {
36f8d174
LT
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;
e646de0d 153 p = add_object(obj, p, NULL, name);
36f8d174
LT
154 continue;
155 }
156 if (obj->type == tree_type) {
e646de0d 157 p = process_tree((struct tree *)obj, p, NULL, name);
36f8d174
LT
158 continue;
159 }
160 if (obj->type == blob_type) {
e646de0d 161 p = process_blob((struct blob *)obj, p, NULL, name);
36f8d174
LT
162 continue;
163 }
164 die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
165 }
9de48752 166 while (objects) {
50319850
JH
167 /* An object with name "foo\n0000000..." can be used to
168 * confuse downstream git-pack-objects very badly.
c807f771
JH
169 */
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);
175 }
176 else
177 printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
9de48752
LT
178 objects = objects->next;
179 }
180}
181
8b3a1e05
LT
182/*
183 * This is a truly stupid algorithm, but it's only
184 * used for bisection, and we just don't care enough.
185 *
186 * We care just barely enough to avoid recursing for
187 * non-merge entries.
188 */
189static int count_distance(struct commit_list *entry)
190{
191 int nr = 0;
192
193 while (entry) {
194 struct commit *commit = entry->item;
195 struct commit_list *p;
196
197 if (commit->object.flags & (UNINTERESTING | COUNTED))
198 break;
8efdc326 199 if (!revs.prune_fn || (commit->object.flags & TREECHANGE))
b3cfd939 200 nr++;
8b3a1e05
LT
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;
209 }
210 }
211 }
b3cfd939 212
8b3a1e05
LT
213 return nr;
214}
215
3d958064 216static void clear_distance(struct commit_list *list)
8b3a1e05
LT
217{
218 while (list) {
219 struct commit *commit = list->item;
220 commit->object.flags &= ~COUNTED;
221 list = list->next;
222 }
223}
224
225static struct commit_list *find_bisection(struct commit_list *list)
226{
227 int nr, closest;
228 struct commit_list *p, *best;
229
230 nr = 0;
231 p = list;
232 while (p) {
8efdc326 233 if (!revs.prune_fn || (p->item->object.flags & TREECHANGE))
b3cfd939 234 nr++;
8b3a1e05
LT
235 p = p->next;
236 }
237 closest = 0;
238 best = list;
239
b3cfd939
LT
240 for (p = list; p; p = p->next) {
241 int distance;
242
8efdc326 243 if (revs.prune_fn && !(p->item->object.flags & TREECHANGE))
b3cfd939
LT
244 continue;
245
246 distance = count_distance(p);
8b3a1e05
LT
247 clear_distance(list);
248 if (nr - distance < distance)
249 distance = nr - distance;
250 if (distance > closest) {
251 best = p;
252 closest = distance;
253 }
8b3a1e05
LT
254 }
255 if (best)
256 best->next = NULL;
257 return best;
258}
259
c6496575
JH
260static void mark_edge_parents_uninteresting(struct commit *commit)
261{
262 struct commit_list *parents;
263
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);
ae563542 269 if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
eb38cc68 270 parent->object.flags |= SHOWN;
c6496575 271 printf("-%s\n", sha1_to_hex(parent->object.sha1));
eb38cc68 272 }
c6496575
JH
273 }
274}
275
5bdbaaa4
LT
276static void mark_edges_uninteresting(struct commit_list *list)
277{
278 for ( ; list; list = list->next) {
c6496575 279 struct commit *commit = list->item;
5bdbaaa4 280
c6496575
JH
281 if (commit->object.flags & UNINTERESTING) {
282 mark_tree_uninteresting(commit->tree);
283 continue;
5bdbaaa4 284 }
c6496575 285 mark_edge_parents_uninteresting(commit);
5bdbaaa4
LT
286 }
287}
288
cf484544 289int main(int argc, const char **argv)
64745109 290{
ae563542 291 struct commit_list *list;
d9a83684 292 int i;
64745109 293
a4a88b2b 294 argc = setup_revisions(argc, argv, &revs, NULL);
ae563542 295
fcfda02b 296 for (i = 1 ; i < argc; i++) {
cf484544 297 const char *arg = argv[i];
fcfda02b 298
8233340c
EW
299 /* accept -<digit>, like traditilnal "head" */
300 if ((*arg == '-') && isdigit(arg[1])) {
ae563542 301 revs.max_count = atoi(arg + 1);
8233340c
EW
302 continue;
303 }
3af06987
EW
304 if (!strcmp(arg, "-n")) {
305 if (++i >= argc)
306 die("-n requires an argument");
ae563542 307 revs.max_count = atoi(argv[i]);
3af06987
EW
308 continue;
309 }
310 if (!strncmp(arg,"-n",2)) {
ae563542 311 revs.max_count = atoi(arg + 2);
a6f68d47 312 continue;
fcfda02b 313 }
a6f68d47
LT
314 if (!strcmp(arg, "--header")) {
315 verbose_header = 1;
316 continue;
317 }
9da5c2f0
JH
318 if (!strcmp(arg, "--no-abbrev")) {
319 abbrev = 0;
320 continue;
321 }
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;
329 }
000182ea
LT
330 if (!strncmp(arg, "--pretty", 8)) {
331 commit_format = get_commit_format(arg+8);
9d97aa64 332 verbose_header = 1;
9d97aa64 333 hdr_termination = '\n';
d87449c5 334 if (commit_format == CMIT_FMT_ONELINE)
d998a089 335 commit_prefix = "";
d87449c5 336 else
d998a089 337 commit_prefix = "commit ";
9d97aa64
LT
338 continue;
339 }
dc68c4ff
JH
340 if (!strcmp(arg, "--timestamp")) {
341 show_timestamp = 1;
342 continue;
343 }
8b3a1e05
LT
344 if (!strcmp(arg, "--bisect")) {
345 bisect_list = 1;
346 continue;
347 }
ae563542 348 usage(rev_list_usage);
a6f68d47 349
fcfda02b
KS
350 }
351
ae563542 352 list = revs.commits;
ae563542 353
ef1cc2cc 354 if (!list &&
ae563542 355 (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
7b34c2fa
LT
356 usage(rev_list_usage);
357
9181ca2c
JH
358 save_commit_buffer = verbose_header;
359 track_object_refs = 0;
360
a4a88b2b
LT
361 prepare_revision_walk(&revs);
362 if (revs.tree_objects)
363 mark_edges_uninteresting(revs.commits);
364
365 if (bisect_list)
366 revs.commits = find_bisection(revs.commits);
7b34c2fa 367
765ac8ec 368 show_commit_list(&revs);
8906300f 369
64745109
LT
370 return 0;
371}