Skip to content

Commit cec3f98

Browse files
committed
Merge branch 'cc/bisect' (early part)
* 'cc/bisect' (early part): t6030: test skipping away from an already skipped commit bisect: when skipping, choose a commit away from a skipped commit bisect: add parameters to "filter_skipped" bisect: display first bad commit without forking a new process bisect: drop unparse_commit() and use clear_commit_marks()
2 parents fa71e80 + a66037c commit cec3f98

File tree

6 files changed

+130
-32
lines changed

6 files changed

+130
-32
lines changed

bisect.c

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "quote.h"
88
#include "sha1-lookup.h"
99
#include "run-command.h"
10+
#include "log-tree.h"
1011
#include "bisect.h"
1112

1213
struct sha1_array {
@@ -27,7 +28,6 @@ struct argv_array {
2728
int argv_alloc;
2829
};
2930

30-
static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL};
3131
static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
3232
static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
3333

@@ -521,14 +521,34 @@ static char *join_sha1_array_hex(struct sha1_array *array, char delim)
521521
return strbuf_detach(&joined_hexs, NULL);
522522
}
523523

524+
/*
525+
* In this function, passing a not NULL skipped_first is very special.
526+
* It means that we want to know if the first commit in the list is
527+
* skipped because we will want to test a commit away from it if it is
528+
* indeed skipped.
529+
* So if the first commit is skipped, we cannot take the shortcut to
530+
* just "return list" when we find the first non skipped commit, we
531+
* have to return a fully filtered list.
532+
*
533+
* We use (*skipped_first == -1) to mean "it has been found that the
534+
* first commit is not skipped". In this case *skipped_first is set back
535+
* to 0 just before the function returns.
536+
*/
524537
struct commit_list *filter_skipped(struct commit_list *list,
525538
struct commit_list **tried,
526-
int show_all)
539+
int show_all,
540+
int *count,
541+
int *skipped_first)
527542
{
528543
struct commit_list *filtered = NULL, **f = &filtered;
529544

530545
*tried = NULL;
531546

547+
if (skipped_first)
548+
*skipped_first = 0;
549+
if (count)
550+
*count = 0;
551+
532552
if (!skipped_revs.sha1_nr)
533553
return list;
534554

@@ -537,22 +557,82 @@ struct commit_list *filter_skipped(struct commit_list *list,
537557
list->next = NULL;
538558
if (0 <= lookup_sha1_array(&skipped_revs,
539559
list->item->object.sha1)) {
560+
if (skipped_first && !*skipped_first)
561+
*skipped_first = 1;
540562
/* Move current to tried list */
541563
*tried = list;
542564
tried = &list->next;
543565
} else {
544-
if (!show_all)
545-
return list;
566+
if (!show_all) {
567+
if (!skipped_first || !*skipped_first)
568+
return list;
569+
} else if (skipped_first && !*skipped_first) {
570+
/* This means we know it's not skipped */
571+
*skipped_first = -1;
572+
}
546573
/* Move current to filtered list */
547574
*f = list;
548575
f = &list->next;
576+
if (count)
577+
(*count)++;
549578
}
550579
list = next;
551580
}
552581

582+
if (skipped_first && *skipped_first == -1)
583+
*skipped_first = 0;
584+
553585
return filtered;
554586
}
555587

588+
static struct commit_list *apply_skip_ratio(struct commit_list *list,
589+
int count,
590+
int skip_num, int skip_denom)
591+
{
592+
int index, i;
593+
struct commit_list *cur, *previous;
594+
595+
cur = list;
596+
previous = NULL;
597+
index = count * skip_num / skip_denom;
598+
599+
for (i = 0; cur; cur = cur->next, i++) {
600+
if (i == index) {
601+
if (hashcmp(cur->item->object.sha1, current_bad_sha1))
602+
return cur;
603+
if (previous)
604+
return previous;
605+
return list;
606+
}
607+
previous = cur;
608+
}
609+
610+
return list;
611+
}
612+
613+
static struct commit_list *managed_skipped(struct commit_list *list,
614+
struct commit_list **tried)
615+
{
616+
int count, skipped_first;
617+
int skip_num, skip_denom;
618+
619+
*tried = NULL;
620+
621+
if (!skipped_revs.sha1_nr)
622+
return list;
623+
624+
list = filter_skipped(list, tried, 0, &count, &skipped_first);
625+
626+
if (!skipped_first)
627+
return list;
628+
629+
/* Use alternatively 1/5, 2/5 and 3/5 as skip ratio. */
630+
skip_num = count % 3 + 1;
631+
skip_denom = 5;
632+
633+
return apply_skip_ratio(list, count, skip_num, skip_denom);
634+
}
635+
556636
static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
557637
const char *bad_format, const char *good_format,
558638
int read_paths)
@@ -771,7 +851,7 @@ static int check_ancestors(const char *prefix)
771851
/* Clean up objects used, as they will be reused. */
772852
for (i = 0; i < pending_copy.nr; i++) {
773853
struct object *o = pending_copy.objects[i].item;
774-
unparse_commit((struct commit *)o);
854+
clear_commit_marks((struct commit *)o, ALL_REV_FLAGS);
775855
}
776856

777857
return res;
@@ -815,6 +895,31 @@ static void check_good_are_ancestors_of_bad(const char *prefix)
815895
close(fd);
816896
}
817897

898+
/*
899+
* This does "git diff-tree --pretty COMMIT" without one fork+exec.
900+
*/
901+
static void show_diff_tree(const char *prefix, struct commit *commit)
902+
{
903+
struct rev_info opt;
904+
905+
/* diff-tree init */
906+
init_revisions(&opt, prefix);
907+
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
908+
opt.abbrev = 0;
909+
opt.diff = 1;
910+
911+
/* This is what "--pretty" does */
912+
opt.verbose_header = 1;
913+
opt.use_terminator = 0;
914+
opt.commit_format = CMIT_FMT_DEFAULT;
915+
916+
/* diff-tree init */
917+
if (!opt.diffopt.output_format)
918+
opt.diffopt.output_format = DIFF_FORMAT_RAW;
919+
920+
log_tree_commit(&opt, commit);
921+
}
922+
818923
/*
819924
* We use the convention that exiting with an exit code 10 means that
820925
* the bisection process finished successfully.
@@ -840,7 +945,7 @@ int bisect_next_all(const char *prefix)
840945

841946
revs.commits = find_bisection(revs.commits, &reaches, &all,
842947
!!skipped_revs.sha1_nr);
843-
revs.commits = filter_skipped(revs.commits, &tried, 0);
948+
revs.commits = managed_skipped(revs.commits, &tried);
844949

845950
if (!revs.commits) {
846951
/*
@@ -860,8 +965,7 @@ int bisect_next_all(const char *prefix)
860965
if (!hashcmp(bisect_rev, current_bad_sha1)) {
861966
exit_if_skipped_commits(tried, current_bad_sha1);
862967
printf("%s is first bad commit\n", bisect_rev_hex);
863-
argv_diff_tree[2] = bisect_rev_hex;
864-
run_command_v_opt(argv_diff_tree, RUN_GIT_CMD);
968+
show_diff_tree(prefix, revs.commits->item);
865969
/* This means the bisection process succeeded. */
866970
exit(10);
867971
}

bisect.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ extern struct commit_list *find_bisection(struct commit_list *list,
77

88
extern struct commit_list *filter_skipped(struct commit_list *list,
99
struct commit_list **tried,
10-
int show_all);
10+
int show_all,
11+
int *count,
12+
int *skipped_first);
1113

1214
extern void print_commit_list(struct commit_list *list,
1315
const char *format_cur,

builtin-rev-list.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,9 @@ int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
262262
if (!revs->commits && !(flags & BISECT_SHOW_TRIED))
263263
return 1;
264264

265-
revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL);
265+
revs->commits = filter_skipped(revs->commits, &tried,
266+
flags & BISECT_SHOW_ALL,
267+
NULL, NULL);
266268

267269
/*
268270
* revs->commits can reach "reaches" commits among

commit.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -316,26 +316,6 @@ int parse_commit(struct commit *item)
316316
return ret;
317317
}
318318

319-
static void unparse_commit_list(struct commit_list *list)
320-
{
321-
for (; list; list = list->next)
322-
unparse_commit(list->item);
323-
}
324-
325-
void unparse_commit(struct commit *item)
326-
{
327-
item->object.flags = 0;
328-
item->object.used = 0;
329-
if (item->object.parsed) {
330-
item->object.parsed = 0;
331-
if (item->parents) {
332-
unparse_commit_list(item->parents);
333-
free_commit_list(item->parents);
334-
item->parents = NULL;
335-
}
336-
}
337-
}
338-
339319
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
340320
{
341321
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));

commit.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
4040

4141
int parse_commit(struct commit *item);
4242

43-
void unparse_commit(struct commit *item);
44-
4543
struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
4644
unsigned commit_list_count(const struct commit_list *l);
4745
struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);

t/t6030-bisect-porcelain.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,18 @@ test_expect_success 'restricting bisection on one dir and a file' '
555555
grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
556556
'
557557

558+
test_expect_success 'skipping away from skipped commit' '
559+
git bisect start $PARA_HASH7 $HASH1 &&
560+
para4=$(git rev-parse --verify HEAD) &&
561+
test "$para4" = "$PARA_HASH4" &&
562+
git bisect skip &&
563+
hash7=$(git rev-parse --verify HEAD) &&
564+
test "$hash7" = "$HASH7" &&
565+
git bisect skip &&
566+
hash3=$(git rev-parse --verify HEAD) &&
567+
test "$hash3" = "$HASH3"
568+
'
569+
558570
#
559571
#
560572
test_done

0 commit comments

Comments
 (0)