Skip to content

Commit 8b3a1e0

Browse files
author
Linus Torvalds
committed
git-rev-list: add "--bisect" flag to find the "halfway" point
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.
1 parent 753fd78 commit 8b3a1e0

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

Diff for: rev-list.c

+80
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#define SEEN (1u << 0)
66
#define INTERESTING (1u << 1)
7+
#define COUNTED (1u << 2)
78

89
static const char rev_list_usage[] =
910
"usage: git-rev-list [OPTION] commit-id <commit-id>\n"
@@ -14,6 +15,7 @@ static const char rev_list_usage[] =
1415
" --pretty\n"
1516
" --merge-order [ --show-breaks ]";
1617

18+
static int bisect_list = 0;
1719
static int verbose_header = 0;
1820
static int show_parents = 0;
1921
static int hdr_termination = 0;
@@ -115,6 +117,78 @@ static int everybody_uninteresting(struct commit_list *list)
115117
return 1;
116118
}
117119

120+
/*
121+
* This is a truly stupid algorithm, but it's only
122+
* used for bisection, and we just don't care enough.
123+
*
124+
* We care just barely enough to avoid recursing for
125+
* non-merge entries.
126+
*/
127+
static int count_distance(struct commit_list *entry)
128+
{
129+
int nr = 0;
130+
131+
while (entry) {
132+
struct commit *commit = entry->item;
133+
struct commit_list *p;
134+
135+
if (commit->object.flags & (UNINTERESTING | COUNTED))
136+
break;
137+
nr++;
138+
commit->object.flags |= COUNTED;
139+
p = commit->parents;
140+
entry = p;
141+
if (p) {
142+
p = p->next;
143+
while (p) {
144+
nr += count_distance(p);
145+
p = p->next;
146+
}
147+
}
148+
}
149+
return nr;
150+
}
151+
152+
static int clear_distance(struct commit_list *list)
153+
{
154+
while (list) {
155+
struct commit *commit = list->item;
156+
commit->object.flags &= ~COUNTED;
157+
list = list->next;
158+
}
159+
}
160+
161+
static struct commit_list *find_bisection(struct commit_list *list)
162+
{
163+
int nr, closest;
164+
struct commit_list *p, *best;
165+
166+
nr = 0;
167+
p = list;
168+
while (p) {
169+
nr++;
170+
p = p->next;
171+
}
172+
closest = 0;
173+
best = list;
174+
175+
p = list;
176+
while (p) {
177+
int distance = count_distance(p);
178+
clear_distance(list);
179+
if (nr - distance < distance)
180+
distance = nr - distance;
181+
if (distance > closest) {
182+
best = p;
183+
closest = distance;
184+
}
185+
p = p->next;
186+
}
187+
if (best)
188+
best->next = NULL;
189+
return best;
190+
}
191+
118192
struct commit_list *limit_list(struct commit_list *list)
119193
{
120194
struct commit_list *newlist = NULL;
@@ -131,6 +205,8 @@ struct commit_list *limit_list(struct commit_list *list)
131205
}
132206
p = &commit_list_insert(commit, p)->next;
133207
} while (list);
208+
if (bisect_list)
209+
newlist = find_bisection(newlist);
134210
return newlist;
135211
}
136212

@@ -186,6 +262,10 @@ int main(int argc, char **argv)
186262
show_parents = 1;
187263
continue;
188264
}
265+
if (!strcmp(arg, "--bisect")) {
266+
bisect_list = 1;
267+
continue;
268+
}
189269
if (!strncmp(arg, "--merge-order", 13)) {
190270
merge_order = 1;
191271
continue;

0 commit comments

Comments
 (0)