rev-list.c 11.2 KB
Newer Older
1
#include "cache.h"
2
#include "tag.h"
3
#include "commit.h"
4 5
#include "tree.h"
#include "blob.h"
6
#include "epoch.h"
7

8 9
#define SEEN		(1u << 0)
#define INTERESTING	(1u << 1)
10
#define COUNTED		(1u << 2)
11 12
#define SHOWN		(1u << 3)
#define DUPCHECK	(1u << 4)
13

14 15 16 17 18
static const char rev_list_usage[] =
	"usage: git-rev-list [OPTION] commit-id <commit-id>\n"
		      "  --max-count=nr\n"
		      "  --max-age=epoch\n"
		      "  --min-age=epoch\n"
19 20 21
		      "  --bisect\n"
		      "  --objects\n"
		      "  --unpacked\n"
22
		      "  --header\n"
23 24
		      "  --pretty\n"
		      "  --merge-order [ --show-breaks ]";
25

26
static int unpacked = 0;
27
static int bisect_list = 0;
28
static int tag_objects = 0;
29 30
static int tree_objects = 0;
static int blob_objects = 0;
31 32 33 34 35 36 37
static int verbose_header = 0;
static int show_parents = 0;
static int hdr_termination = 0;
static const char *prefix = "";
static unsigned long max_age = -1;
static unsigned long min_age = -1;
static int max_count = -1;
38
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
39 40
static int merge_order = 0;
static int show_breaks = 0;
41
static int stop_traversal = 0;
42
static int topo_order = 0;
43 44 45

static void show_commit(struct commit *commit)
{
46
	commit->object.flags |= SHOWN;
47 48 49 50 51 52 53 54
	if (show_breaks) {
		prefix = "| ";
		if (commit->object.flags & DISCONTINUITY) {
			prefix = "^ ";     
		} else if (commit->object.flags & BOUNDARY) {
			prefix = "= ";
		} 
        }        		
55 56 57 58 59 60 61 62 63 64
	printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
	if (show_parents) {
		struct commit_list *parents = commit->parents;
		while (parents) {
			printf(" %s", sha1_to_hex(parents->item->object.sha1));
			parents = parents->next;
		}
	}
	putchar('\n');
	if (verbose_header) {
65 66 67
		static char pretty_header[16384];
		pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
		printf("%s%c", pretty_header, hdr_termination);
68 69
	}
	fflush(stdout);
70 71 72 73
}

static int filter_commit(struct commit * commit)
{
74
	if (stop_traversal && (commit->object.flags & BOUNDARY))
75
		return STOP;
76
	if (commit->object.flags & (UNINTERESTING|SHOWN))
77 78 79
		return CONTINUE;
	if (min_age != -1 && (commit->date > min_age))
		return CONTINUE;
80
	if (max_age != -1 && (commit->date < max_age)) {
81 82
		stop_traversal=1;
		return merge_order?CONTINUE:STOP;
83
	}
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	if (max_count != -1 && !max_count--)
		return STOP;
	return DO;
}

static int process_commit(struct commit * commit)
{
	int action=filter_commit(commit);

	if (action == STOP) {
		return STOP;
	}

	if (action == CONTINUE) {
		return CONTINUE;
99
	}
100 101 102 103

	show_commit(commit);

	return CONTINUE;
104 105
}

106
static struct object_list **add_object(struct object *obj, struct object_list **p, const char *name)
107 108 109
{
	struct object_list *entry = xmalloc(sizeof(*entry));
	entry->item = obj;
110
	entry->next = *p;
111
	entry->name = name;
112 113 114 115
	*p = entry;
	return &entry->next;
}

116
static struct object_list **process_blob(struct blob *blob, struct object_list **p, const char *name)
117 118 119 120 121 122 123 124
{
	struct object *obj = &blob->object;

	if (!blob_objects)
		return p;
	if (obj->flags & (UNINTERESTING | SEEN))
		return p;
	obj->flags |= SEEN;
125
	return add_object(obj, p, name);
126 127
}

128
static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
129 130 131 132 133 134 135 136 137 138 139
{
	struct object *obj = &tree->object;
	struct tree_entry_list *entry;

	if (!tree_objects)
		return p;
	if (obj->flags & (UNINTERESTING | SEEN))
		return p;
	if (parse_tree(tree) < 0)
		die("bad tree object %s", sha1_to_hex(obj->sha1));
	obj->flags |= SEEN;
140
	p = add_object(obj, p, name);
141 142
	for (entry = tree->entries ; entry ; entry = entry->next) {
		if (entry->directory)
143
			p = process_tree(entry->item.tree, p, entry->name);
144
		else
145
			p = process_blob(entry->item.blob, p, entry->name);
146 147 148 149
	}
	return p;
}

150 151
static struct object_list *pending_objects = NULL;

152 153
static void show_commit_list(struct commit_list *list)
{
154
	struct object_list *objects = NULL, **p = &objects, *pending;
155 156 157
	while (list) {
		struct commit *commit = pop_most_recent_commit(&list, SEEN);

158
		p = process_tree(commit->tree, p, "");
159
		if (process_commit(commit) == STOP)
160 161
			break;
	}
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	for (pending = pending_objects; pending; pending = pending->next) {
		struct object *obj = pending->item;
		const char *name = pending->name;
		if (obj->flags & (UNINTERESTING | SEEN))
			continue;
		if (obj->type == tag_type) {
			obj->flags |= SEEN;
			p = add_object(obj, p, name);
			continue;
		}
		if (obj->type == tree_type) {
			p = process_tree((struct tree *)obj, p, name);
			continue;
		}
		if (obj->type == blob_type) {
			p = process_blob((struct blob *)obj, p, name);
			continue;
		}
		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
	}
182
	while (objects) {
183
		printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
		objects = objects->next;
	}
}

static void mark_blob_uninteresting(struct blob *blob)
{
	if (!blob_objects)
		return;
	if (blob->object.flags & UNINTERESTING)
		return;
	blob->object.flags |= UNINTERESTING;
}

static void mark_tree_uninteresting(struct tree *tree)
{
	struct object *obj = &tree->object;
	struct tree_entry_list *entry;

	if (!tree_objects)
		return;
	if (obj->flags & UNINTERESTING)
		return;
	obj->flags |= UNINTERESTING;
	if (parse_tree(tree) < 0)
		die("bad tree %s", sha1_to_hex(obj->sha1));
	entry = tree->entries;
	while (entry) {
		if (entry->directory)
			mark_tree_uninteresting(entry->item.tree);
		else
			mark_blob_uninteresting(entry->item.blob);
		entry = entry->next;
	}
217 218
}

219 220 221 222
static void mark_parents_uninteresting(struct commit *commit)
{
	struct commit_list *parents = commit->parents;

223 224
	if (tree_objects)
		mark_tree_uninteresting(commit->tree);
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
	while (parents) {
		struct commit *commit = parents->item;
		commit->object.flags |= UNINTERESTING;
		parents = parents->next;
	}
}

static int everybody_uninteresting(struct commit_list *list)
{
	while (list) {
		struct commit *commit = list->item;
		list = list->next;
		if (commit->object.flags & UNINTERESTING)
			continue;
		return 0;
	}
	return 1;
}

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
/*
 * This is a truly stupid algorithm, but it's only
 * used for bisection, and we just don't care enough.
 *
 * We care just barely enough to avoid recursing for
 * non-merge entries.
 */
static int count_distance(struct commit_list *entry)
{
	int nr = 0;

	while (entry) {
		struct commit *commit = entry->item;
		struct commit_list *p;

		if (commit->object.flags & (UNINTERESTING | COUNTED))
			break;
		nr++;
		commit->object.flags |= COUNTED;
		p = commit->parents;
		entry = p;
		if (p) {
			p = p->next;
			while (p) {
				nr += count_distance(p);
				p = p->next;
			}
		}
	}
	return nr;
}

276
static void clear_distance(struct commit_list *list)
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
{
	while (list) {
		struct commit *commit = list->item;
		commit->object.flags &= ~COUNTED;
		list = list->next;
	}
}

static struct commit_list *find_bisection(struct commit_list *list)
{
	int nr, closest;
	struct commit_list *p, *best;

	nr = 0;
	p = list;
	while (p) {
		nr++;
		p = p->next;
	}
	closest = 0;
	best = list;

	p = list;
	while (p) {
		int distance = count_distance(p);
		clear_distance(list);
		if (nr - distance < distance)
			distance = nr - distance;
		if (distance > closest) {
			best = p;
			closest = distance;
		}
		p = p->next;
	}
	if (best)
		best->next = NULL;
	return best;
}

L
Linus Torvalds 已提交
316
static struct commit_list *limit_list(struct commit_list *list)
317 318 319
{
	struct commit_list *newlist = NULL;
	struct commit_list **p = &newlist;
320
	while (list) {
321 322 323
		struct commit *commit = pop_most_recent_commit(&list, SEEN);
		struct object *obj = &commit->object;

324 325
		if (unpacked && has_sha1_pack(obj->sha1))
			obj->flags |= UNINTERESTING;
326
		if (obj->flags & UNINTERESTING) {
327 328 329 330 331 332
			mark_parents_uninteresting(commit);
			if (everybody_uninteresting(list))
				break;
			continue;
		}
		p = &commit_list_insert(commit, p)->next;
333
	}
334 335
	if (bisect_list)
		newlist = find_bisection(newlist);
336 337 338
	return newlist;
}

339 340 341 342 343
static void add_pending_object(struct object *obj, const char *name)
{
	add_object(obj, &pending_objects, name);
}

344 345 346
static struct commit *get_commit_reference(const char *name, unsigned int flags)
{
	unsigned char sha1[20];
347
	struct object *object;
348 349 350

	if (get_sha1(name, sha1))
		usage(rev_list_usage);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
	object = parse_object(sha1);
	if (!object)
		die("bad object %s", name);

	/*
	 * Tag object? Look what it points to..
	 */
	if (object->type == tag_type) {
		struct tag *tag = (struct tag *) object;
		object->flags |= flags;
		if (tag_objects && !(object->flags & UNINTERESTING))
			add_pending_object(object, tag->tag);
		object = tag->tagged;
	}

	/*
	 * Commit object? Just return it, we'll do all the complex
	 * reachability crud.
	 */
	if (object->type == commit_type) {
		struct commit *commit = (struct commit *)object;
		object->flags |= flags;
		if (parse_commit(commit) < 0)
			die("unable to parse commit %s", name);
		return commit;
	}

	/*
	 * Tree object? Either mark it uniniteresting, or add it
	 * to the list of objects to look at later..
	 */
	if (object->type == tree_type) {
		struct tree *tree = (struct tree *)object;
		if (!tree_objects)
385
			return NULL;
386 387 388 389 390 391 392 393 394 395 396 397 398 399
		if (flags & UNINTERESTING) {
			mark_tree_uninteresting(tree);
			return NULL;
		}
		add_pending_object(object, "");
		return NULL;
	}

	/*
	 * Blob object? You know the drill by now..
	 */
	if (object->type == blob_type) {
		struct blob *blob = (struct blob *)object;
		if (!blob_objects)
400
			return NULL;
401 402 403 404 405 406 407 408
		if (flags & UNINTERESTING) {
			mark_blob_uninteresting(blob);
			return NULL;
		}
		add_pending_object(object, "");
		return NULL;
	}
	die("%s is unknown object", name);
409 410
}

411 412 413
int main(int argc, char **argv)
{
	struct commit_list *list = NULL;
414
	struct commit_list *(*insert)(struct commit *, struct commit_list **);
415
	int i, limited = 0;
416

417
	insert = insert_by_date;
418
	for (i = 1 ; i < argc; i++) {
419
		int flags;
420
		char *arg = argv[i];
421
		struct commit *commit;
422 423 424

		if (!strncmp(arg, "--max-count=", 12)) {
			max_count = atoi(arg + 12);
425 426 427
			continue;
		}
		if (!strncmp(arg, "--max-age=", 10)) {
428
			max_age = atoi(arg + 10);
429 430 431
			continue;
		}
		if (!strncmp(arg, "--min-age=", 10)) {
432
			min_age = atoi(arg + 10);
433
			continue;
434
		}
435 436 437 438
		if (!strcmp(arg, "--header")) {
			verbose_header = 1;
			continue;
		}
439 440
		if (!strncmp(arg, "--pretty", 8)) {
			commit_format = get_commit_format(arg+8);
441 442 443 444 445
			verbose_header = 1;
			hdr_termination = '\n';
			prefix = "commit ";
			continue;
		}
446 447 448 449
		if (!strcmp(arg, "--parents")) {
			show_parents = 1;
			continue;
		}
450 451 452 453
		if (!strcmp(arg, "--bisect")) {
			bisect_list = 1;
			continue;
		}
454
		if (!strcmp(arg, "--objects")) {
455
			tag_objects = 1;
456 457 458 459
			tree_objects = 1;
			blob_objects = 1;
			continue;
		}
460 461 462 463 464
		if (!strcmp(arg, "--unpacked")) {
			unpacked = 1;
			limited = 1;
			continue;
		}
465
		if (!strcmp(arg, "--merge-order")) {
466
		        merge_order = 1;
467
		        insert = commit_list_insert;
468 469
			continue;
		}
470
		if (!strcmp(arg, "--show-breaks")) {
471 472 473
			show_breaks = 1;
			continue;
		}
474 475 476 477
		if (!strcmp(arg, "--topo-order")) {
		        topo_order = 1;
			continue;
		}
478

479 480 481 482 483 484
		flags = 0;
		if (*arg == '^') {
			flags = UNINTERESTING;
			arg++;
			limited = 1;
		}
485
		if (show_breaks && !merge_order)
486
			usage(rev_list_usage);
487 488 489
		commit = get_commit_reference(arg, flags);
		if (!commit)
			continue;
490 491 492
		if (commit->object.flags & DUPCHECK)
			continue;
		commit->object.flags |= DUPCHECK;
493
		insert(commit, &list);
494 495
	}

496
	if (!merge_order) {		
497
	        if (limited)
498
			list = limit_list(list);
499 500
		if (topo_order)
			sort_in_topological_order(&list);
501 502 503 504 505 506
		show_commit_list(list);
	} else {
		if (sort_list_in_merge_order(list, &process_commit)) {
			  die("merge order sort failed\n");
		}
	}
507

508 509
	return 0;
}