git-rev-list: add "--bisect" flag to find the "halfway" point
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 18 Jun 2005 05:54:50 +0000 (22:54 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 18 Jun 2005 05:54:50 +0000 (22:54 -0700)
This is useful for doing binary searching for problems.  You start with
a known good and known bad point, and you then test the "halfway" point
in between:

git-rev-list --bisect bad ^good

and you test that.  If that one tests good, you now still have a known
bad case, but two known good points, and you can bisect again:

git-rev-list --bisect bad ^good1 ^good2

and test that point.  If that point is bad, you now use that as your
known-bad starting point:

git-rev-list --bisect newbad ^good1 ^good2

and basically at every iteration you shrink your list of commits by
half: you're binary searching for the point where the troubles started,
even though there isn't a nice linear ordering.

rev-list.c

index b526ad4..c7ebd2f 100644 (file)
@@ -4,6 +4,7 @@
 
 #define SEEN           (1u << 0)
 #define INTERESTING    (1u << 1)
 
 #define SEEN           (1u << 0)
 #define INTERESTING    (1u << 1)
+#define COUNTED                (1u << 2)
 
 static const char rev_list_usage[] =
        "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
 
 static const char rev_list_usage[] =
        "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
@@ -14,6 +15,7 @@ static const char rev_list_usage[] =
                      "  --pretty\n"
                      "  --merge-order [ --show-breaks ]";
 
                      "  --pretty\n"
                      "  --merge-order [ --show-breaks ]";
 
+static int bisect_list = 0;
 static int verbose_header = 0;
 static int show_parents = 0;
 static int hdr_termination = 0;
 static int verbose_header = 0;
 static int show_parents = 0;
 static int hdr_termination = 0;
@@ -115,6 +117,78 @@ static int everybody_uninteresting(struct commit_list *list)
        return 1;
 }
 
        return 1;
 }
 
+/*
+ * 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;
+}
+
+static int clear_distance(struct commit_list *list)
+{
+       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;
+}
+
 struct commit_list *limit_list(struct commit_list *list)
 {
        struct commit_list *newlist = NULL;
 struct commit_list *limit_list(struct commit_list *list)
 {
        struct commit_list *newlist = NULL;
@@ -131,6 +205,8 @@ struct commit_list *limit_list(struct commit_list *list)
                }
                p = &commit_list_insert(commit, p)->next;
        } while (list);
                }
                p = &commit_list_insert(commit, p)->next;
        } while (list);
+       if (bisect_list)
+               newlist = find_bisection(newlist);
        return newlist;
 }
 
        return newlist;
 }
 
@@ -186,6 +262,10 @@ int main(int argc, char **argv)
                        show_parents = 1;
                        continue;
                }
                        show_parents = 1;
                        continue;
                }
+               if (!strcmp(arg, "--bisect")) {
+                       bisect_list = 1;
+                       continue;
+               }
                if (!strncmp(arg, "--merge-order", 13)) {
                        merge_order = 1;
                        continue;
                if (!strncmp(arg, "--merge-order", 13)) {
                        merge_order = 1;
                        continue;