bisect: implement the "check_merge_bases" function
And all functions needed to make it work. This is a port from the shell function with the same name "git-bisect.sh". This function is not used yet but it will be used later. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Christian Couder committed
May 9, 2009 at 17:55 UTC
c0537662806f4b3d3ba883d05a48fdf9120893f0
1 file changed
+130
bisect.c
+130
@@ -507,6 +507,20 @@ static int lookup_sha1_array(struct sha1_array *array,
507
return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access);
508
}
509
510
+static char *join_sha1_array_hex(struct sha1_array *array, char delim)
511
+{
512
+ struct strbuf joined_hexs = STRBUF_INIT;
513
+ int i;
514
+
515
+ for (i = 0; i < array->sha1_nr; i++) {
516
+ strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
517
+ if (i + 1 < array->sha1_nr)
518
+ strbuf_addch(&joined_hexs, delim);
519
+ }
520
+
521
+ return strbuf_detach(&joined_hexs, NULL);
522
+}
523
+
524
struct commit_list *filter_skipped(struct commit_list *list,
525
struct commit_list **tried,
526
int show_all)
@@ -587,6 +601,30 @@ static void exit_if_skipped_commits(struct commit_list *tried,
601
exit(2);
602
}
603
604
+static int is_expected_rev(const unsigned char *sha1)
605
+{
606
+ const char *filename = git_path("BISECT_EXPECTED_REV");
607
+ struct stat st;
608
+ struct strbuf str = STRBUF_INIT;
609
+ FILE *fp;
610
+ int res = 0;
611
+
612
+ if (stat(filename, &st) || !S_ISREG(st.st_mode))
613
+ return 0;
614
+
615
+ fp = fopen(filename, "r");
616
+ if (!fp)
617
+ return 0;
618
+
619
+ if (strbuf_getline(&str, fp, '\n') != EOF)
620
+ res = !strcmp(str.buf, sha1_to_hex(sha1));
621
+
622
+ strbuf_release(&str);
623
+ fclose(fp);
624
+
625
+ return res;
626
+}
627
+
628
static void mark_expected_rev(char *bisect_rev_hex)
629
{
630
int len = strlen(bisect_rev_hex);
@@ -620,6 +658,98 @@ static int bisect_checkout(char *bisect_rev_hex)
658
return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
659
}
660
661
+static struct commit *get_commit_reference(const unsigned char *sha1)
662
+{
663
+ struct commit *r = lookup_commit_reference(sha1);
664
+ if (!r)
665
+ die("Not a valid commit name %s", sha1_to_hex(sha1));
666
+ return r;
667
+}
668
+
669
+static struct commit **get_bad_and_good_commits(int *rev_nr)
670
+{
671
+ int len = 1 + good_revs.sha1_nr;
672
+ struct commit **rev = xmalloc(len * sizeof(*rev));
673
+ int i, n = 0;
674
+
675
+ rev[n++] = get_commit_reference(current_bad_sha1);
676
+ for (i = 0; i < good_revs.sha1_nr; i++)
677
+ rev[n++] = get_commit_reference(good_revs.sha1[i]);
678
+ *rev_nr = n;
679
+
680
+ return rev;
681
+}
682
+
683
+static void handle_bad_merge_base(void)
684
+{
685
+ if (is_expected_rev(current_bad_sha1)) {
686
+ char *bad_hex = sha1_to_hex(current_bad_sha1);
687
+ char *good_hex = join_sha1_array_hex(&good_revs, ' ');
688
+
689
+ fprintf(stderr, "The merge base %s is bad.\n"
690
+ "This means the bug has been fixed "
691
+ "between %s and [%s].\n",
692
+ bad_hex, bad_hex, good_hex);
693
+
694
+ exit(3);
695
+ }
696
+
697
+ fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
698
+ "git bisect cannot work properly in this case.\n"
699
+ "Maybe you mistake good and bad revs?\n");
700
+ exit(1);
701
+}
702
+
703
+void handle_skipped_merge_base(const unsigned char *mb)
704
+{
705
+ char *mb_hex = sha1_to_hex(mb);
706
+ char *bad_hex = sha1_to_hex(current_bad_sha1);
707
+ char *good_hex = join_sha1_array_hex(&good_revs, ' ');
708
+
709
+ fprintf(stderr, "Warning: the merge base between %s and [%s] "
710
+ "must be skipped.\n"
711
+ "So we cannot be sure the first bad commit is "
712
+ "between %s and %s.\n"
713
+ "We continue anyway.\n",
714
+ bad_hex, good_hex, mb_hex, bad_hex);
715
+ free(good_hex);
716
+}
717
+
718
+/*
719
+ * "check_merge_bases" checks that merge bases are not "bad".
720
+ *
721
+ * - If one is "bad", it means the user assumed something wrong
722
+ * and we must exit with a non 0 error code.
723
+ * - If one is "good", that's good, we have nothing to do.
724
+ * - If one is "skipped", we can't know but we should warn.
725
+ * - If we don't know, we should check it out and ask the user to test.
726
+ */
727
+static void check_merge_bases(void)
728
+{
729
+ struct commit_list *result;
730
+ int rev_nr;
731
+ struct commit **rev = get_bad_and_good_commits(&rev_nr);
732
+
733
+ result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
734
+
735
+ for (; result; result = result->next) {
736
+ const unsigned char *mb = result->item->object.sha1;
737
+ if (!hashcmp(mb, current_bad_sha1)) {
738
+ handle_bad_merge_base();
739
+ } else if (0 <= lookup_sha1_array(&good_revs, mb)) {
740
+ continue;
741
+ } else if (0 <= lookup_sha1_array(&skipped_revs, mb)) {
742
+ handle_skipped_merge_base(mb);
743
+ } else {
744
+ printf("Bisecting: a merge base must be tested\n");
745
+ exit(bisect_checkout(sha1_to_hex(mb)));
746
+ }
747
+ }
748
+
749
+ free(rev);
750
+ free_commit_list(result);
751
+}
752
+
753
/*
754
* We use the convention that exiting with an exit code 10 means that
755
* the bisection process finished successfully.