rev-list.c 10.9 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
#define SHOWN		(LAST_EPOCH_FLAG << 2)
12

13 14 15 16 17
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"
18 19 20
		      "  --bisect\n"
		      "  --objects\n"
		      "  --unpacked\n"
21
		      "  --header\n"
22 23
		      "  --pretty\n"
		      "  --merge-order [ --show-breaks ]";
24

25
static int unpacked = 0;
26
static int bisect_list = 0;
27
static int tag_objects = 0;
28 29
static int tree_objects = 0;
static int blob_objects = 0;
30 31 32 33 34 35 36
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;
37
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
38 39
static int merge_order = 0;
static int show_breaks = 0;
40
static int stop_traversal = 0;
41 42 43

static void show_commit(struct commit *commit)
{
44
	commit->object.flags |= SHOWN;
45 46 47 48 49 50 51 52
	if (show_breaks) {
		prefix = "| ";
		if (commit->object.flags & DISCONTINUITY) {
			prefix = "^ ";     
		} else if (commit->object.flags & BOUNDARY) {
			prefix = "= ";
		} 
        }        		
53 54 55 56 57 58 59 60 61 62
	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) {
63 64 65
		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);
66 67
	}
	fflush(stdout);
68 69 70 71
}

static int filter_commit(struct commit * commit)
{
72 73
	if (merge_order && stop_traversal && commit->object.flags & BOUNDARY)
		return STOP;
74
	if (commit->object.flags & (UNINTERESTING|SHOWN))
75 76 77
		return CONTINUE;
	if (min_age != -1 && (commit->date > min_age))
		return CONTINUE;
78 79 80 81 82 83 84 85
	if (max_age != -1 && (commit->date < max_age)) {
		if (!merge_order)
			return STOP;
		else {
			stop_traversal = 1;
			return CONTINUE;
		}
	}
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	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;
101
	}
102 103 104 105

	show_commit(commit);

	return CONTINUE;
106 107
}

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

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

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

130
static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
131 132 133 134 135 136 137 138 139 140 141
{
	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;
142
	p = add_object(obj, p, name);
143 144
	for (entry = tree->entries ; entry ; entry = entry->next) {
		if (entry->directory)
145
			p = process_tree(entry->item.tree, p, entry->name);
146
		else
147
			p = process_blob(entry->item.blob, p, entry->name);
148 149 150 151
	}
	return p;
}

152 153
static struct object_list *pending_objects = NULL;

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

160
		p = process_tree(commit->tree, p, "");
161
		if (process_commit(commit) == STOP)
162 163
			break;
	}
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	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);
	}
184
	while (objects) {
185
		printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
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 217 218
		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;
	}
219 220
}

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

225 226
	if (tree_objects)
		mark_tree_uninteresting(commit->tree);
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
	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;
}

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 276 277
/*
 * 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;
}

278
static void clear_distance(struct commit_list *list)
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 316 317
{
	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 已提交
318
static struct commit_list *limit_list(struct commit_list *list)
319 320 321
{
	struct commit_list *newlist = NULL;
	struct commit_list **p = &newlist;
322
	while (list) {
323 324 325
		struct commit *commit = pop_most_recent_commit(&list, SEEN);
		struct object *obj = &commit->object;

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

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

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

	if (get_sha1(name, sha1))
		usage(rev_list_usage);
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 385 386
	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)
387
			return NULL;
388 389 390 391 392 393 394 395 396 397 398 399 400 401
		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)
402
			return NULL;
403 404 405 406 407 408 409 410
		if (flags & UNINTERESTING) {
			mark_blob_uninteresting(blob);
			return NULL;
		}
		add_pending_object(object, "");
		return NULL;
	}
	die("%s is unknown object", name);
411 412
}

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

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 466 467 468 469 470 471 472
		if (!strncmp(arg, "--merge-order", 13)) {
		        merge_order = 1;
			continue;
		}
		if (!strncmp(arg, "--show-breaks", 13)) {
			show_breaks = 1;
			continue;
		}
473

474 475 476 477 478 479
		flags = 0;
		if (*arg == '^') {
			flags = UNINTERESTING;
			arg++;
			limited = 1;
		}
480
		if (show_breaks && !merge_order)
481
			usage(rev_list_usage);
482 483 484
		commit = get_commit_reference(arg, flags);
		if (!commit)
			continue;
485
		insert_by_date(&list, commit);
486 487
	}

488
	if (!merge_order) {		
489
	        if (limited)
490 491 492 493 494 495 496
			list = limit_list(list);
		show_commit_list(list);
	} else {
		if (sort_list_in_merge_order(list, &process_commit)) {
			  die("merge order sort failed\n");
		}
	}
497

498 499
	return 0;
}