rev-list.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. #include "cache.h"
  2. #include "config.h"
  3. #include "commit.h"
  4. #include "diff.h"
  5. #include "revision.h"
  6. #include "list-objects.h"
  7. #include "list-objects-filter.h"
  8. #include "list-objects-filter-options.h"
  9. #include "object.h"
  10. #include "object-store.h"
  11. #include "pack.h"
  12. #include "pack-bitmap.h"
  13. #include "builtin.h"
  14. #include "log-tree.h"
  15. #include "graph.h"
  16. #include "bisect.h"
  17. #include "progress.h"
  18. #include "reflog-walk.h"
  19. #include "oidset.h"
  20. #include "packfile.h"
  21. static const char rev_list_usage[] =
  22. "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
  23. " limiting output:\n"
  24. " --max-count=<n>\n"
  25. " --max-age=<epoch>\n"
  26. " --min-age=<epoch>\n"
  27. " --sparse\n"
  28. " --no-merges\n"
  29. " --min-parents=<n>\n"
  30. " --no-min-parents\n"
  31. " --max-parents=<n>\n"
  32. " --no-max-parents\n"
  33. " --remove-empty\n"
  34. " --all\n"
  35. " --branches\n"
  36. " --tags\n"
  37. " --remotes\n"
  38. " --stdin\n"
  39. " --quiet\n"
  40. " ordering output:\n"
  41. " --topo-order\n"
  42. " --date-order\n"
  43. " --reverse\n"
  44. " formatting output:\n"
  45. " --parents\n"
  46. " --children\n"
  47. " --objects | --objects-edge\n"
  48. " --unpacked\n"
  49. " --header | --pretty\n"
  50. " --[no-]object-names\n"
  51. " --abbrev=<n> | --no-abbrev\n"
  52. " --abbrev-commit\n"
  53. " --left-right\n"
  54. " --count\n"
  55. " special purpose:\n"
  56. " --bisect\n"
  57. " --bisect-vars\n"
  58. " --bisect-all"
  59. ;
  60. static struct progress *progress;
  61. static unsigned progress_counter;
  62. static struct list_objects_filter_options filter_options;
  63. static struct oidset omitted_objects;
  64. static int arg_print_omitted; /* print objects omitted by filter */
  65. static struct oidset missing_objects;
  66. enum missing_action {
  67. MA_ERROR = 0, /* fail if any missing objects are encountered */
  68. MA_ALLOW_ANY, /* silently allow ALL missing objects */
  69. MA_PRINT, /* print ALL missing objects in special section */
  70. MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
  71. };
  72. static enum missing_action arg_missing_action;
  73. /* display only the oid of each object encountered */
  74. static int arg_show_object_names = 1;
  75. #define DEFAULT_OIDSET_SIZE (16*1024)
  76. static void finish_commit(struct commit *commit);
  77. static void show_commit(struct commit *commit, void *data)
  78. {
  79. struct rev_list_info *info = data;
  80. struct rev_info *revs = info->revs;
  81. display_progress(progress, ++progress_counter);
  82. if (info->flags & REV_LIST_QUIET) {
  83. finish_commit(commit);
  84. return;
  85. }
  86. graph_show_commit(revs->graph);
  87. if (revs->count) {
  88. if (commit->object.flags & PATCHSAME)
  89. revs->count_same++;
  90. else if (commit->object.flags & SYMMETRIC_LEFT)
  91. revs->count_left++;
  92. else
  93. revs->count_right++;
  94. finish_commit(commit);
  95. return;
  96. }
  97. if (info->show_timestamp)
  98. printf("%"PRItime" ", commit->date);
  99. if (info->header_prefix)
  100. fputs(info->header_prefix, stdout);
  101. if (!revs->graph)
  102. fputs(get_revision_mark(revs, commit), stdout);
  103. if (revs->abbrev_commit && revs->abbrev)
  104. fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
  105. stdout);
  106. else
  107. fputs(oid_to_hex(&commit->object.oid), stdout);
  108. if (revs->print_parents) {
  109. struct commit_list *parents = commit->parents;
  110. while (parents) {
  111. printf(" %s", oid_to_hex(&parents->item->object.oid));
  112. parents = parents->next;
  113. }
  114. }
  115. if (revs->children.name) {
  116. struct commit_list *children;
  117. children = lookup_decoration(&revs->children, &commit->object);
  118. while (children) {
  119. printf(" %s", oid_to_hex(&children->item->object.oid));
  120. children = children->next;
  121. }
  122. }
  123. show_decorations(revs, commit);
  124. if (revs->commit_format == CMIT_FMT_ONELINE)
  125. putchar(' ');
  126. else
  127. putchar('\n');
  128. if (revs->verbose_header) {
  129. struct strbuf buf = STRBUF_INIT;
  130. struct pretty_print_context ctx = {0};
  131. ctx.abbrev = revs->abbrev;
  132. ctx.date_mode = revs->date_mode;
  133. ctx.date_mode_explicit = revs->date_mode_explicit;
  134. ctx.fmt = revs->commit_format;
  135. ctx.output_encoding = get_log_output_encoding();
  136. ctx.color = revs->diffopt.use_color;
  137. pretty_print_commit(&ctx, commit, &buf);
  138. if (buf.len) {
  139. if (revs->commit_format != CMIT_FMT_ONELINE)
  140. graph_show_oneline(revs->graph);
  141. graph_show_commit_msg(revs->graph, stdout, &buf);
  142. /*
  143. * Add a newline after the commit message.
  144. *
  145. * Usually, this newline produces a blank
  146. * padding line between entries, in which case
  147. * we need to add graph padding on this line.
  148. *
  149. * However, the commit message may not end in a
  150. * newline. In this case the newline simply
  151. * ends the last line of the commit message,
  152. * and we don't need any graph output. (This
  153. * always happens with CMIT_FMT_ONELINE, and it
  154. * happens with CMIT_FMT_USERFORMAT when the
  155. * format doesn't explicitly end in a newline.)
  156. */
  157. if (buf.len && buf.buf[buf.len - 1] == '\n')
  158. graph_show_padding(revs->graph);
  159. putchar(info->hdr_termination);
  160. } else {
  161. /*
  162. * If the message buffer is empty, just show
  163. * the rest of the graph output for this
  164. * commit.
  165. */
  166. if (graph_show_remainder(revs->graph))
  167. putchar('\n');
  168. if (revs->commit_format == CMIT_FMT_ONELINE)
  169. putchar('\n');
  170. }
  171. strbuf_release(&buf);
  172. } else {
  173. if (graph_show_remainder(revs->graph))
  174. putchar('\n');
  175. }
  176. maybe_flush_or_die(stdout, "stdout");
  177. finish_commit(commit);
  178. }
  179. static void finish_commit(struct commit *commit)
  180. {
  181. if (commit->parents) {
  182. free_commit_list(commit->parents);
  183. commit->parents = NULL;
  184. }
  185. free_commit_buffer(the_repository->parsed_objects,
  186. commit);
  187. }
  188. static inline void finish_object__ma(struct object *obj)
  189. {
  190. /*
  191. * Whether or not we try to dynamically fetch missing objects
  192. * from the server, we currently DO NOT have the object. We
  193. * can either print, allow (ignore), or conditionally allow
  194. * (ignore) them.
  195. */
  196. switch (arg_missing_action) {
  197. case MA_ERROR:
  198. die("missing %s object '%s'",
  199. type_name(obj->type), oid_to_hex(&obj->oid));
  200. return;
  201. case MA_ALLOW_ANY:
  202. return;
  203. case MA_PRINT:
  204. oidset_insert(&missing_objects, &obj->oid);
  205. return;
  206. case MA_ALLOW_PROMISOR:
  207. if (is_promisor_object(&obj->oid))
  208. return;
  209. die("unexpected missing %s object '%s'",
  210. type_name(obj->type), oid_to_hex(&obj->oid));
  211. return;
  212. default:
  213. BUG("unhandled missing_action");
  214. return;
  215. }
  216. }
  217. static int finish_object(struct object *obj, const char *name, void *cb_data)
  218. {
  219. struct rev_list_info *info = cb_data;
  220. if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
  221. finish_object__ma(obj);
  222. return 1;
  223. }
  224. if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
  225. parse_object(the_repository, &obj->oid);
  226. return 0;
  227. }
  228. static void show_object(struct object *obj, const char *name, void *cb_data)
  229. {
  230. struct rev_list_info *info = cb_data;
  231. struct rev_info *revs = info->revs;
  232. if (finish_object(obj, name, cb_data))
  233. return;
  234. display_progress(progress, ++progress_counter);
  235. if (info->flags & REV_LIST_QUIET)
  236. return;
  237. if (revs->count) {
  238. /*
  239. * The object count is always accumulated in the .count_right
  240. * field for traversal that is not a left-right traversal,
  241. * and cmd_rev_list() made sure that a .count request that
  242. * wants to count non-commit objects, which is handled by
  243. * the show_object() callback, does not ask for .left_right.
  244. */
  245. revs->count_right++;
  246. return;
  247. }
  248. if (arg_show_object_names)
  249. show_object_with_name(stdout, obj, name);
  250. else
  251. printf("%s\n", oid_to_hex(&obj->oid));
  252. }
  253. static void show_edge(struct commit *commit)
  254. {
  255. printf("-%s\n", oid_to_hex(&commit->object.oid));
  256. }
  257. static void print_var_str(const char *var, const char *val)
  258. {
  259. printf("%s='%s'\n", var, val);
  260. }
  261. static void print_var_int(const char *var, int val)
  262. {
  263. printf("%s=%d\n", var, val);
  264. }
  265. static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
  266. {
  267. int cnt, flags = info->flags;
  268. char hex[GIT_MAX_HEXSZ + 1] = "";
  269. struct commit_list *tried;
  270. struct rev_info *revs = info->revs;
  271. if (!revs->commits)
  272. return 1;
  273. revs->commits = filter_skipped(revs->commits, &tried,
  274. flags & BISECT_SHOW_ALL,
  275. NULL, NULL);
  276. /*
  277. * revs->commits can reach "reaches" commits among
  278. * "all" commits. If it is good, then there are
  279. * (all-reaches) commits left to be bisected.
  280. * On the other hand, if it is bad, then the set
  281. * to bisect is "reaches".
  282. * A bisect set of size N has (N-1) commits further
  283. * to test, as we already know one bad one.
  284. */
  285. cnt = all - reaches;
  286. if (cnt < reaches)
  287. cnt = reaches;
  288. if (revs->commits)
  289. oid_to_hex_r(hex, &revs->commits->item->object.oid);
  290. if (flags & BISECT_SHOW_ALL) {
  291. traverse_commit_list(revs, show_commit, show_object, info);
  292. printf("------\n");
  293. }
  294. print_var_str("bisect_rev", hex);
  295. print_var_int("bisect_nr", cnt - 1);
  296. print_var_int("bisect_good", all - reaches - 1);
  297. print_var_int("bisect_bad", reaches - 1);
  298. print_var_int("bisect_all", all);
  299. print_var_int("bisect_steps", estimate_bisect_steps(all));
  300. return 0;
  301. }
  302. static int show_object_fast(
  303. const struct object_id *oid,
  304. enum object_type type,
  305. int exclude,
  306. uint32_t name_hash,
  307. struct packed_git *found_pack,
  308. off_t found_offset)
  309. {
  310. fprintf(stdout, "%s\n", oid_to_hex(oid));
  311. return 1;
  312. }
  313. static inline int parse_missing_action_value(const char *value)
  314. {
  315. if (!strcmp(value, "error")) {
  316. arg_missing_action = MA_ERROR;
  317. return 1;
  318. }
  319. if (!strcmp(value, "allow-any")) {
  320. arg_missing_action = MA_ALLOW_ANY;
  321. fetch_if_missing = 0;
  322. return 1;
  323. }
  324. if (!strcmp(value, "print")) {
  325. arg_missing_action = MA_PRINT;
  326. fetch_if_missing = 0;
  327. return 1;
  328. }
  329. if (!strcmp(value, "allow-promisor")) {
  330. arg_missing_action = MA_ALLOW_PROMISOR;
  331. fetch_if_missing = 0;
  332. return 1;
  333. }
  334. return 0;
  335. }
  336. static int try_bitmap_count(struct rev_info *revs,
  337. struct list_objects_filter_options *filter)
  338. {
  339. uint32_t commit_count = 0,
  340. tag_count = 0,
  341. tree_count = 0,
  342. blob_count = 0;
  343. int max_count;
  344. struct bitmap_index *bitmap_git;
  345. /* This function only handles counting, not general traversal. */
  346. if (!revs->count)
  347. return -1;
  348. /*
  349. * A bitmap result can't know left/right, etc, because we don't
  350. * actually traverse.
  351. */
  352. if (revs->left_right || revs->cherry_mark)
  353. return -1;
  354. /*
  355. * If we're counting reachable objects, we can't handle a max count of
  356. * commits to traverse, since we don't know which objects go with which
  357. * commit.
  358. */
  359. if (revs->max_count >= 0 &&
  360. (revs->tag_objects || revs->tree_objects || revs->blob_objects))
  361. return -1;
  362. /*
  363. * This must be saved before doing any walking, since the revision
  364. * machinery will count it down to zero while traversing.
  365. */
  366. max_count = revs->max_count;
  367. bitmap_git = prepare_bitmap_walk(revs, filter);
  368. if (!bitmap_git)
  369. return -1;
  370. count_bitmap_commit_list(bitmap_git, &commit_count,
  371. revs->tree_objects ? &tree_count : NULL,
  372. revs->blob_objects ? &blob_count : NULL,
  373. revs->tag_objects ? &tag_count : NULL);
  374. if (max_count >= 0 && max_count < commit_count)
  375. commit_count = max_count;
  376. printf("%d\n", commit_count + tree_count + blob_count + tag_count);
  377. free_bitmap_index(bitmap_git);
  378. return 0;
  379. }
  380. static int try_bitmap_traversal(struct rev_info *revs,
  381. struct list_objects_filter_options *filter)
  382. {
  383. struct bitmap_index *bitmap_git;
  384. /*
  385. * We can't use a bitmap result with a traversal limit, since the set
  386. * of commits we'd get would be essentially random.
  387. */
  388. if (revs->max_count >= 0)
  389. return -1;
  390. bitmap_git = prepare_bitmap_walk(revs, filter);
  391. if (!bitmap_git)
  392. return -1;
  393. traverse_bitmap_commit_list(bitmap_git, revs, &show_object_fast);
  394. free_bitmap_index(bitmap_git);
  395. return 0;
  396. }
  397. int cmd_rev_list(int argc, const char **argv, const char *prefix)
  398. {
  399. struct rev_info revs;
  400. struct rev_list_info info;
  401. struct setup_revision_opt s_r_opt = {
  402. .allow_exclude_promisor_objects = 1,
  403. };
  404. int i;
  405. int bisect_list = 0;
  406. int bisect_show_vars = 0;
  407. int bisect_find_all = 0;
  408. int use_bitmap_index = 0;
  409. const char *show_progress = NULL;
  410. if (argc == 2 && !strcmp(argv[1], "-h"))
  411. usage(rev_list_usage);
  412. git_config(git_default_config, NULL);
  413. repo_init_revisions(the_repository, &revs, prefix);
  414. revs.abbrev = DEFAULT_ABBREV;
  415. revs.commit_format = CMIT_FMT_UNSPECIFIED;
  416. /*
  417. * Scan the argument list before invoking setup_revisions(), so that we
  418. * know if fetch_if_missing needs to be set to 0.
  419. *
  420. * "--exclude-promisor-objects" acts as a pre-filter on missing objects
  421. * by not crossing the boundary from realized objects to promisor
  422. * objects.
  423. *
  424. * Let "--missing" to conditionally set fetch_if_missing.
  425. */
  426. for (i = 1; i < argc; i++) {
  427. const char *arg = argv[i];
  428. if (!strcmp(arg, "--exclude-promisor-objects")) {
  429. fetch_if_missing = 0;
  430. revs.exclude_promisor_objects = 1;
  431. break;
  432. }
  433. }
  434. for (i = 1; i < argc; i++) {
  435. const char *arg = argv[i];
  436. if (skip_prefix(arg, "--missing=", &arg)) {
  437. if (revs.exclude_promisor_objects)
  438. die(_("cannot combine --exclude-promisor-objects and --missing"));
  439. if (parse_missing_action_value(arg))
  440. break;
  441. }
  442. }
  443. if (arg_missing_action)
  444. revs.do_not_die_on_missing_tree = 1;
  445. argc = setup_revisions(argc, argv, &revs, &s_r_opt);
  446. memset(&info, 0, sizeof(info));
  447. info.revs = &revs;
  448. if (revs.bisect)
  449. bisect_list = 1;
  450. if (revs.diffopt.flags.quick)
  451. info.flags |= REV_LIST_QUIET;
  452. for (i = 1 ; i < argc; i++) {
  453. const char *arg = argv[i];
  454. if (!strcmp(arg, "--header")) {
  455. revs.verbose_header = 1;
  456. continue;
  457. }
  458. if (!strcmp(arg, "--timestamp")) {
  459. info.show_timestamp = 1;
  460. continue;
  461. }
  462. if (!strcmp(arg, "--bisect")) {
  463. bisect_list = 1;
  464. continue;
  465. }
  466. if (!strcmp(arg, "--bisect-all")) {
  467. bisect_list = 1;
  468. bisect_find_all = 1;
  469. info.flags |= BISECT_SHOW_ALL;
  470. revs.show_decorations = 1;
  471. continue;
  472. }
  473. if (!strcmp(arg, "--bisect-vars")) {
  474. bisect_list = 1;
  475. bisect_show_vars = 1;
  476. continue;
  477. }
  478. if (!strcmp(arg, "--use-bitmap-index")) {
  479. use_bitmap_index = 1;
  480. continue;
  481. }
  482. if (!strcmp(arg, "--test-bitmap")) {
  483. test_bitmap_walk(&revs);
  484. return 0;
  485. }
  486. if (skip_prefix(arg, "--progress=", &arg)) {
  487. show_progress = arg;
  488. continue;
  489. }
  490. if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
  491. parse_list_objects_filter(&filter_options, arg);
  492. if (filter_options.choice && !revs.blob_objects)
  493. die(_("object filtering requires --objects"));
  494. continue;
  495. }
  496. if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
  497. list_objects_filter_set_no_filter(&filter_options);
  498. continue;
  499. }
  500. if (!strcmp(arg, "--filter-print-omitted")) {
  501. arg_print_omitted = 1;
  502. continue;
  503. }
  504. if (!strcmp(arg, "--exclude-promisor-objects"))
  505. continue; /* already handled above */
  506. if (skip_prefix(arg, "--missing=", &arg))
  507. continue; /* already handled above */
  508. if (!strcmp(arg, ("--no-object-names"))) {
  509. arg_show_object_names = 0;
  510. continue;
  511. }
  512. if (!strcmp(arg, ("--object-names"))) {
  513. arg_show_object_names = 1;
  514. continue;
  515. }
  516. usage(rev_list_usage);
  517. }
  518. if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
  519. /* The command line has a --pretty */
  520. info.hdr_termination = '\n';
  521. if (revs.commit_format == CMIT_FMT_ONELINE)
  522. info.header_prefix = "";
  523. else
  524. info.header_prefix = "commit ";
  525. }
  526. else if (revs.verbose_header)
  527. /* Only --header was specified */
  528. revs.commit_format = CMIT_FMT_RAW;
  529. if ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&
  530. (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
  531. !revs.pending.nr) &&
  532. !revs.rev_input_given && !revs.read_from_stdin) ||
  533. revs.diff)
  534. usage(rev_list_usage);
  535. if (revs.show_notes)
  536. die(_("rev-list does not support display of notes"));
  537. if (revs.count &&
  538. (revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
  539. (revs.left_right || revs.cherry_mark))
  540. die(_("marked counting is incompatible with --objects"));
  541. save_commit_buffer = (revs.verbose_header ||
  542. revs.grep_filter.pattern_list ||
  543. revs.grep_filter.header_list);
  544. if (bisect_list)
  545. revs.limited = 1;
  546. if (show_progress)
  547. progress = start_delayed_progress(show_progress, 0);
  548. if (use_bitmap_index) {
  549. if (!try_bitmap_count(&revs, &filter_options))
  550. return 0;
  551. if (!try_bitmap_traversal(&revs, &filter_options))
  552. return 0;
  553. }
  554. if (prepare_revision_walk(&revs))
  555. die("revision walk setup failed");
  556. if (revs.tree_objects)
  557. mark_edges_uninteresting(&revs, show_edge, 0);
  558. if (bisect_list) {
  559. int reaches, all;
  560. unsigned bisect_flags = 0;
  561. if (bisect_find_all)
  562. bisect_flags |= FIND_BISECTION_ALL;
  563. if (revs.first_parent_only)
  564. bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY;
  565. find_bisection(&revs.commits, &reaches, &all, bisect_flags);
  566. if (bisect_show_vars)
  567. return show_bisect_vars(&info, reaches, all);
  568. }
  569. if (arg_print_omitted)
  570. oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
  571. if (arg_missing_action == MA_PRINT)
  572. oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
  573. traverse_commit_list_filtered(
  574. &filter_options, &revs, show_commit, show_object, &info,
  575. (arg_print_omitted ? &omitted_objects : NULL));
  576. if (arg_print_omitted) {
  577. struct oidset_iter iter;
  578. struct object_id *oid;
  579. oidset_iter_init(&omitted_objects, &iter);
  580. while ((oid = oidset_iter_next(&iter)))
  581. printf("~%s\n", oid_to_hex(oid));
  582. oidset_clear(&omitted_objects);
  583. }
  584. if (arg_missing_action == MA_PRINT) {
  585. struct oidset_iter iter;
  586. struct object_id *oid;
  587. oidset_iter_init(&missing_objects, &iter);
  588. while ((oid = oidset_iter_next(&iter)))
  589. printf("?%s\n", oid_to_hex(oid));
  590. oidset_clear(&missing_objects);
  591. }
  592. stop_progress(&progress);
  593. if (revs.count) {
  594. if (revs.left_right && revs.cherry_mark)
  595. printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
  596. else if (revs.left_right)
  597. printf("%d\t%d\n", revs.count_left, revs.count_right);
  598. else if (revs.cherry_mark)
  599. printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same);
  600. else
  601. printf("%d\n", revs.count_left + revs.count_right);
  602. }
  603. return 0;
  604. }