From ebbc088e13e1bf0dbf8eb08b00519602c176f864 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 29 Mar 2009 11:44:44 +0200 Subject: [PATCH 01/21] quote: implement "sq_dequote_many" to unwrap many args in one string The sq_dequote() function does not allow parsing a string with more than one single-quoted parameter easily; use its code to implement a new API sq_dequote_step() to allow the caller iterate through such a string to parse them one-by-one. The original sq_dequote() becomes a thin wrapper around it. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- quote.c | 18 ++++++++++++++++-- quote.h | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/quote.c b/quote.c index 6a520855d..ea49c7a99 100644 --- a/quote.c +++ b/quote.c @@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) } } -char *sq_dequote(char *arg) +char *sq_dequote_step(char *arg, char **next) { char *dst = arg; char *src = arg; @@ -92,6 +92,8 @@ char *sq_dequote(char *arg) switch (*++src) { case '\0': *dst = 0; + if (next) + *next = NULL; return arg; case '\\': c = *++src; @@ -101,11 +103,23 @@ char *sq_dequote(char *arg) } /* Fallthrough */ default: - return NULL; + if (!next || !isspace(*src)) + return NULL; + do { + c = *++src; + } while (isspace(c)); + *dst = 0; + *next = src; + return arg; } } } +char *sq_dequote(char *arg) +{ + return sq_dequote_step(arg, NULL); +} + /* 1 means: quote as octal * 0 means: quote as octal if (quote_path_fully) * -1 means: never quote diff --git a/quote.h b/quote.h index c5eea6f18..2315105fa 100644 --- a/quote.h +++ b/quote.h @@ -39,6 +39,14 @@ extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); */ extern char *sq_dequote(char *); +/* + * Same as the above, but can be used to unwrap many arguments in the + * same string separated by space. "next" is changed to point to the + * next argument that should be passed as first parameter. When there + * is no more argument to be dequoted, "next" is updated to point to NULL. + */ +extern char *sq_dequote_step(char *arg, char **next); + extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); From eaa759b9141f125d7e55a4b08b60497845d3c52e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 29 Mar 2009 11:44:52 +0200 Subject: [PATCH 02/21] quote: add "sq_dequote_to_argv" to put unwrapped args in an argv array This new function unwraps the space separated shell quoted elements in its first argument and places them in the argv array passed as its second argument. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- quote.c | 17 +++++++++++++++++ quote.h | 1 + 2 files changed, 18 insertions(+) diff --git a/quote.c b/quote.c index ea49c7a99..7a49fcf69 100644 --- a/quote.c +++ b/quote.c @@ -120,6 +120,23 @@ char *sq_dequote(char *arg) return sq_dequote_step(arg, NULL); } +int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc) +{ + char *next = arg; + + if (!*arg) + return 0; + do { + char *dequoted = sq_dequote_step(next, &next); + if (!dequoted) + return -1; + ALLOC_GROW(*argv, *nr + 1, *alloc); + (*argv)[(*nr)++] = dequoted; + } while (next); + + return 0; +} + /* 1 means: quote as octal * 0 means: quote as octal if (quote_path_fully) * -1 means: never quote diff --git a/quote.h b/quote.h index 2315105fa..66730f2bf 100644 --- a/quote.h +++ b/quote.h @@ -46,6 +46,7 @@ extern char *sq_dequote(char *); * is no more argument to be dequoted, "next" is updated to point to NULL. */ extern char *sq_dequote_step(char *arg, char **next); +extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc); extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp); extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq); From 2a8177b63d39503b182248b04ffcc75e3495754c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 30 Mar 2009 05:07:15 +0200 Subject: [PATCH 03/21] refs: add "for_each_ref_in" function to refactor "for_each_*_ref" functions The "for_each_{tag,branch,remote,replace,}_ref" functions are redefined in terms of "for_each_ref_in" so that we can lose the hardcoded length of prefix strings from the code. Signed-off-by: Christian Couder --- refs.c | 11 ++++++++--- refs.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index aeef257ee..2d198a1ad 100644 --- a/refs.c +++ b/refs.c @@ -647,19 +647,24 @@ int for_each_ref(each_ref_fn fn, void *cb_data) return do_for_each_ref("refs/", fn, 0, 0, cb_data); } +int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) +{ + return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data); +} + int for_each_tag_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data); + return for_each_ref_in("refs/tags/", fn, cb_data); } int for_each_branch_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data); + return for_each_ref_in("refs/heads/", fn, cb_data); } int for_each_remote_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data); + return for_each_ref_in("refs/remotes/", fn, cb_data); } int for_each_rawref(each_ref_fn fn, void *cb_data) diff --git a/refs.h b/refs.h index 29bdcecd4..abb125754 100644 --- a/refs.h +++ b/refs.h @@ -20,6 +20,7 @@ struct ref_lock { typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data); extern int head_ref(each_ref_fn, void *); extern int for_each_ref(each_ref_fn, void *); +extern int for_each_ref_in(const char *, each_ref_fn, void *); extern int for_each_tag_ref(each_ref_fn, void *); extern int for_each_branch_ref(each_ref_fn, void *); extern int for_each_remote_ref(each_ref_fn, void *); From ff62d732d826efcf271cd6c50a41f613af97aff6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:17 +0100 Subject: [PATCH 04/21] rev-list: make "bisect_list" variable local to "cmd_rev_list" The "bisect_list" variable was static for no reason as it is only used in the "cmd_rev_list" function. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-rev-list.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 40d5fcb6b..28fe2dc30 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -52,7 +52,6 @@ static const char rev_list_usage[] = static struct rev_info revs; -static int bisect_list; static int show_timestamp; static int hdr_termination; static const char *header_prefix; @@ -618,6 +617,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) struct commit_list *list; int i; int read_from_stdin = 0; + int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; int quiet = 0; From a2ad79ced25e1b76fabec079549f521e8071ddd1 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:24 +0100 Subject: [PATCH 05/21] rev-list: move bisect related code into its own file This patch creates new "bisect.c" and "bisect.h" files and move bisect related code into these files. While at it, we also remove some include directives that are not needed any more from the beginning of "builtin-rev-list.c". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Makefile | 1 + bisect.c | 388 +++++++++++++++++++++++++++++++++++++++++++++ bisect.h | 8 + builtin-rev-list.c | 388 +-------------------------------------------- 4 files changed, 398 insertions(+), 387 deletions(-) create mode 100644 bisect.c create mode 100644 bisect.h diff --git a/Makefile b/Makefile index 320c89786..9fa292835 100644 --- a/Makefile +++ b/Makefile @@ -420,6 +420,7 @@ LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o LIB_OBJS += attr.o LIB_OBJS += base85.o +LIB_OBJS += bisect.o LIB_OBJS += blob.o LIB_OBJS += branch.o LIB_OBJS += bundle.o diff --git a/bisect.c b/bisect.c new file mode 100644 index 000000000..27def7dac --- /dev/null +++ b/bisect.c @@ -0,0 +1,388 @@ +#include "cache.h" +#include "commit.h" +#include "diff.h" +#include "revision.h" +#include "bisect.h" + +/* bits #0-15 in revision.h */ + +#define COUNTED (1u<<16) + +/* + * 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; + if (!(commit->object.flags & TREESAME)) + 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 void clear_distance(struct commit_list *list) +{ + while (list) { + struct commit *commit = list->item; + commit->object.flags &= ~COUNTED; + list = list->next; + } +} + +#define DEBUG_BISECT 0 + +static inline int weight(struct commit_list *elem) +{ + return *((int*)(elem->item->util)); +} + +static inline void weight_set(struct commit_list *elem, int weight) +{ + *((int*)(elem->item->util)) = weight; +} + +static int count_interesting_parents(struct commit *commit) +{ + struct commit_list *p; + int count; + + for (count = 0, p = commit->parents; p; p = p->next) { + if (p->item->object.flags & UNINTERESTING) + continue; + count++; + } + return count; +} + +static inline int halfway(struct commit_list *p, int nr) +{ + /* + * Don't short-cut something we are not going to return! + */ + if (p->item->object.flags & TREESAME) + return 0; + if (DEBUG_BISECT) + return 0; + /* + * 2 and 3 are halfway of 5. + * 3 is halfway of 6 but 2 and 4 are not. + */ + switch (2 * weight(p) - nr) { + case -1: case 0: case 1: + return 1; + default: + return 0; + } +} + +#if !DEBUG_BISECT +#define show_list(a,b,c,d) do { ; } while (0) +#else +static void show_list(const char *debug, int counted, int nr, + struct commit_list *list) +{ + struct commit_list *p; + + fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr); + + for (p = list; p; p = p->next) { + struct commit_list *pp; + struct commit *commit = p->item; + unsigned flags = commit->object.flags; + enum object_type type; + unsigned long size; + char *buf = read_sha1_file(commit->object.sha1, &type, &size); + char *ep, *sp; + + fprintf(stderr, "%c%c%c ", + (flags & TREESAME) ? ' ' : 'T', + (flags & UNINTERESTING) ? 'U' : ' ', + (flags & COUNTED) ? 'C' : ' '); + if (commit->util) + fprintf(stderr, "%3d", weight(p)); + else + fprintf(stderr, "---"); + fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1)); + for (pp = commit->parents; pp; pp = pp->next) + fprintf(stderr, " %.*s", 8, + sha1_to_hex(pp->item->object.sha1)); + + sp = strstr(buf, "\n\n"); + if (sp) { + sp += 2; + for (ep = sp; *ep && *ep != '\n'; ep++) + ; + fprintf(stderr, " %.*s", (int)(ep - sp), sp); + } + fprintf(stderr, "\n"); + } +} +#endif /* DEBUG_BISECT */ + +static struct commit_list *best_bisection(struct commit_list *list, int nr) +{ + struct commit_list *p, *best; + int best_distance = -1; + + best = list; + for (p = list; p; p = p->next) { + int distance; + unsigned flags = p->item->object.flags; + + if (flags & TREESAME) + continue; + distance = weight(p); + if (nr - distance < distance) + distance = nr - distance; + if (distance > best_distance) { + best = p; + best_distance = distance; + } + } + + return best; +} + +struct commit_dist { + struct commit *commit; + int distance; +}; + +static int compare_commit_dist(const void *a_, const void *b_) +{ + struct commit_dist *a, *b; + + a = (struct commit_dist *)a_; + b = (struct commit_dist *)b_; + if (a->distance != b->distance) + return b->distance - a->distance; /* desc sort */ + return hashcmp(a->commit->object.sha1, b->commit->object.sha1); +} + +static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr) +{ + struct commit_list *p; + struct commit_dist *array = xcalloc(nr, sizeof(*array)); + int cnt, i; + + for (p = list, cnt = 0; p; p = p->next) { + int distance; + unsigned flags = p->item->object.flags; + + if (flags & TREESAME) + continue; + distance = weight(p); + if (nr - distance < distance) + distance = nr - distance; + array[cnt].commit = p->item; + array[cnt].distance = distance; + cnt++; + } + qsort(array, cnt, sizeof(*array), compare_commit_dist); + for (p = list, i = 0; i < cnt; i++) { + struct name_decoration *r = xmalloc(sizeof(*r) + 100); + struct object *obj = &(array[i].commit->object); + + sprintf(r->name, "dist=%d", array[i].distance); + r->next = add_decoration(&name_decoration, obj, r); + p->item = array[i].commit; + p = p->next; + } + if (p) + p->next = NULL; + free(array); + return list; +} + +/* + * zero or positive weight is the number of interesting commits it can + * reach, including itself. Especially, weight = 0 means it does not + * reach any tree-changing commits (e.g. just above uninteresting one + * but traversal is with pathspec). + * + * weight = -1 means it has one parent and its distance is yet to + * be computed. + * + * weight = -2 means it has more than one parent and its distance is + * unknown. After running count_distance() first, they will get zero + * or positive distance. + */ +static struct commit_list *do_find_bisection(struct commit_list *list, + int nr, int *weights, + int find_all) +{ + int n, counted; + struct commit_list *p; + + counted = 0; + + for (n = 0, p = list; p; p = p->next) { + struct commit *commit = p->item; + unsigned flags = commit->object.flags; + + p->item->util = &weights[n++]; + switch (count_interesting_parents(commit)) { + case 0: + if (!(flags & TREESAME)) { + weight_set(p, 1); + counted++; + show_list("bisection 2 count one", + counted, nr, list); + } + /* + * otherwise, it is known not to reach any + * tree-changing commit and gets weight 0. + */ + break; + case 1: + weight_set(p, -1); + break; + default: + weight_set(p, -2); + break; + } + } + + show_list("bisection 2 initialize", counted, nr, list); + + /* + * If you have only one parent in the resulting set + * then you can reach one commit more than that parent + * can reach. So we do not have to run the expensive + * count_distance() for single strand of pearls. + * + * However, if you have more than one parents, you cannot + * just add their distance and one for yourself, since + * they usually reach the same ancestor and you would + * end up counting them twice that way. + * + * So we will first count distance of merges the usual + * way, and then fill the blanks using cheaper algorithm. + */ + for (p = list; p; p = p->next) { + if (p->item->object.flags & UNINTERESTING) + continue; + if (weight(p) != -2) + continue; + weight_set(p, count_distance(p)); + clear_distance(list); + + /* Does it happen to be at exactly half-way? */ + if (!find_all && halfway(p, nr)) + return p; + counted++; + } + + show_list("bisection 2 count_distance", counted, nr, list); + + while (counted < nr) { + for (p = list; p; p = p->next) { + struct commit_list *q; + unsigned flags = p->item->object.flags; + + if (0 <= weight(p)) + continue; + for (q = p->item->parents; q; q = q->next) { + if (q->item->object.flags & UNINTERESTING) + continue; + if (0 <= weight(q)) + break; + } + if (!q) + continue; + + /* + * weight for p is unknown but q is known. + * add one for p itself if p is to be counted, + * otherwise inherit it from q directly. + */ + if (!(flags & TREESAME)) { + weight_set(p, weight(q)+1); + counted++; + show_list("bisection 2 count one", + counted, nr, list); + } + else + weight_set(p, weight(q)); + + /* Does it happen to be at exactly half-way? */ + if (!find_all && halfway(p, nr)) + return p; + } + } + + show_list("bisection 2 counted all", counted, nr, list); + + if (!find_all) + return best_bisection(list, nr); + else + return best_bisection_sorted(list, nr); +} + +struct commit_list *find_bisection(struct commit_list *list, + int *reaches, int *all, + int find_all) +{ + int nr, on_list; + struct commit_list *p, *best, *next, *last; + int *weights; + + show_list("bisection 2 entry", 0, 0, list); + + /* + * Count the number of total and tree-changing items on the + * list, while reversing the list. + */ + for (nr = on_list = 0, last = NULL, p = list; + p; + p = next) { + unsigned flags = p->item->object.flags; + + next = p->next; + if (flags & UNINTERESTING) + continue; + p->next = last; + last = p; + if (!(flags & TREESAME)) + nr++; + on_list++; + } + list = last; + show_list("bisection 2 sorted", 0, nr, list); + + *all = nr; + weights = xcalloc(on_list, sizeof(*weights)); + + /* Do the real work of finding bisection commit. */ + best = do_find_bisection(list, nr, weights, find_all); + if (best) { + if (!find_all) + best->next = NULL; + *reaches = weight(best); + } + free(weights); + return best; +} + diff --git a/bisect.h b/bisect.h new file mode 100644 index 000000000..60b2fe1cd --- /dev/null +++ b/bisect.h @@ -0,0 +1,8 @@ +#ifndef BISECT_H +#define BISECT_H + +extern struct commit_list *find_bisection(struct commit_list *list, + int *reaches, int *all, + int find_all); + +#endif diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 28fe2dc30..b1e8200d2 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -1,20 +1,12 @@ #include "cache.h" -#include "refs.h" -#include "tag.h" #include "commit.h" -#include "tree.h" -#include "blob.h" -#include "tree-walk.h" #include "diff.h" #include "revision.h" #include "list-objects.h" #include "builtin.h" #include "log-tree.h" #include "graph.h" - -/* bits #0-15 in revision.h */ - -#define COUNTED (1u<<16) +#include "bisect.h" static const char rev_list_usage[] = "git rev-list [OPTION] ... [ -- paths... ]\n" @@ -195,384 +187,6 @@ static void show_edge(struct commit *commit) printf("-%s\n", sha1_to_hex(commit->object.sha1)); } -/* - * 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; - if (!(commit->object.flags & TREESAME)) - 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 void clear_distance(struct commit_list *list) -{ - while (list) { - struct commit *commit = list->item; - commit->object.flags &= ~COUNTED; - list = list->next; - } -} - -#define DEBUG_BISECT 0 - -static inline int weight(struct commit_list *elem) -{ - return *((int*)(elem->item->util)); -} - -static inline void weight_set(struct commit_list *elem, int weight) -{ - *((int*)(elem->item->util)) = weight; -} - -static int count_interesting_parents(struct commit *commit) -{ - struct commit_list *p; - int count; - - for (count = 0, p = commit->parents; p; p = p->next) { - if (p->item->object.flags & UNINTERESTING) - continue; - count++; - } - return count; -} - -static inline int halfway(struct commit_list *p, int nr) -{ - /* - * Don't short-cut something we are not going to return! - */ - if (p->item->object.flags & TREESAME) - return 0; - if (DEBUG_BISECT) - return 0; - /* - * 2 and 3 are halfway of 5. - * 3 is halfway of 6 but 2 and 4 are not. - */ - switch (2 * weight(p) - nr) { - case -1: case 0: case 1: - return 1; - default: - return 0; - } -} - -#if !DEBUG_BISECT -#define show_list(a,b,c,d) do { ; } while (0) -#else -static void show_list(const char *debug, int counted, int nr, - struct commit_list *list) -{ - struct commit_list *p; - - fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr); - - for (p = list; p; p = p->next) { - struct commit_list *pp; - struct commit *commit = p->item; - unsigned flags = commit->object.flags; - enum object_type type; - unsigned long size; - char *buf = read_sha1_file(commit->object.sha1, &type, &size); - char *ep, *sp; - - fprintf(stderr, "%c%c%c ", - (flags & TREESAME) ? ' ' : 'T', - (flags & UNINTERESTING) ? 'U' : ' ', - (flags & COUNTED) ? 'C' : ' '); - if (commit->util) - fprintf(stderr, "%3d", weight(p)); - else - fprintf(stderr, "---"); - fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1)); - for (pp = commit->parents; pp; pp = pp->next) - fprintf(stderr, " %.*s", 8, - sha1_to_hex(pp->item->object.sha1)); - - sp = strstr(buf, "\n\n"); - if (sp) { - sp += 2; - for (ep = sp; *ep && *ep != '\n'; ep++) - ; - fprintf(stderr, " %.*s", (int)(ep - sp), sp); - } - fprintf(stderr, "\n"); - } -} -#endif /* DEBUG_BISECT */ - -static struct commit_list *best_bisection(struct commit_list *list, int nr) -{ - struct commit_list *p, *best; - int best_distance = -1; - - best = list; - for (p = list; p; p = p->next) { - int distance; - unsigned flags = p->item->object.flags; - - if (flags & TREESAME) - continue; - distance = weight(p); - if (nr - distance < distance) - distance = nr - distance; - if (distance > best_distance) { - best = p; - best_distance = distance; - } - } - - return best; -} - -struct commit_dist { - struct commit *commit; - int distance; -}; - -static int compare_commit_dist(const void *a_, const void *b_) -{ - struct commit_dist *a, *b; - - a = (struct commit_dist *)a_; - b = (struct commit_dist *)b_; - if (a->distance != b->distance) - return b->distance - a->distance; /* desc sort */ - return hashcmp(a->commit->object.sha1, b->commit->object.sha1); -} - -static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr) -{ - struct commit_list *p; - struct commit_dist *array = xcalloc(nr, sizeof(*array)); - int cnt, i; - - for (p = list, cnt = 0; p; p = p->next) { - int distance; - unsigned flags = p->item->object.flags; - - if (flags & TREESAME) - continue; - distance = weight(p); - if (nr - distance < distance) - distance = nr - distance; - array[cnt].commit = p->item; - array[cnt].distance = distance; - cnt++; - } - qsort(array, cnt, sizeof(*array), compare_commit_dist); - for (p = list, i = 0; i < cnt; i++) { - struct name_decoration *r = xmalloc(sizeof(*r) + 100); - struct object *obj = &(array[i].commit->object); - - sprintf(r->name, "dist=%d", array[i].distance); - r->next = add_decoration(&name_decoration, obj, r); - p->item = array[i].commit; - p = p->next; - } - if (p) - p->next = NULL; - free(array); - return list; -} - -/* - * zero or positive weight is the number of interesting commits it can - * reach, including itself. Especially, weight = 0 means it does not - * reach any tree-changing commits (e.g. just above uninteresting one - * but traversal is with pathspec). - * - * weight = -1 means it has one parent and its distance is yet to - * be computed. - * - * weight = -2 means it has more than one parent and its distance is - * unknown. After running count_distance() first, they will get zero - * or positive distance. - */ -static struct commit_list *do_find_bisection(struct commit_list *list, - int nr, int *weights, - int find_all) -{ - int n, counted; - struct commit_list *p; - - counted = 0; - - for (n = 0, p = list; p; p = p->next) { - struct commit *commit = p->item; - unsigned flags = commit->object.flags; - - p->item->util = &weights[n++]; - switch (count_interesting_parents(commit)) { - case 0: - if (!(flags & TREESAME)) { - weight_set(p, 1); - counted++; - show_list("bisection 2 count one", - counted, nr, list); - } - /* - * otherwise, it is known not to reach any - * tree-changing commit and gets weight 0. - */ - break; - case 1: - weight_set(p, -1); - break; - default: - weight_set(p, -2); - break; - } - } - - show_list("bisection 2 initialize", counted, nr, list); - - /* - * If you have only one parent in the resulting set - * then you can reach one commit more than that parent - * can reach. So we do not have to run the expensive - * count_distance() for single strand of pearls. - * - * However, if you have more than one parents, you cannot - * just add their distance and one for yourself, since - * they usually reach the same ancestor and you would - * end up counting them twice that way. - * - * So we will first count distance of merges the usual - * way, and then fill the blanks using cheaper algorithm. - */ - for (p = list; p; p = p->next) { - if (p->item->object.flags & UNINTERESTING) - continue; - if (weight(p) != -2) - continue; - weight_set(p, count_distance(p)); - clear_distance(list); - - /* Does it happen to be at exactly half-way? */ - if (!find_all && halfway(p, nr)) - return p; - counted++; - } - - show_list("bisection 2 count_distance", counted, nr, list); - - while (counted < nr) { - for (p = list; p; p = p->next) { - struct commit_list *q; - unsigned flags = p->item->object.flags; - - if (0 <= weight(p)) - continue; - for (q = p->item->parents; q; q = q->next) { - if (q->item->object.flags & UNINTERESTING) - continue; - if (0 <= weight(q)) - break; - } - if (!q) - continue; - - /* - * weight for p is unknown but q is known. - * add one for p itself if p is to be counted, - * otherwise inherit it from q directly. - */ - if (!(flags & TREESAME)) { - weight_set(p, weight(q)+1); - counted++; - show_list("bisection 2 count one", - counted, nr, list); - } - else - weight_set(p, weight(q)); - - /* Does it happen to be at exactly half-way? */ - if (!find_all && halfway(p, nr)) - return p; - } - } - - show_list("bisection 2 counted all", counted, nr, list); - - if (!find_all) - return best_bisection(list, nr); - else - return best_bisection_sorted(list, nr); -} - -static struct commit_list *find_bisection(struct commit_list *list, - int *reaches, int *all, - int find_all) -{ - int nr, on_list; - struct commit_list *p, *best, *next, *last; - int *weights; - - show_list("bisection 2 entry", 0, 0, list); - - /* - * Count the number of total and tree-changing items on the - * list, while reversing the list. - */ - for (nr = on_list = 0, last = NULL, p = list; - p; - p = next) { - unsigned flags = p->item->object.flags; - - next = p->next; - if (flags & UNINTERESTING) - continue; - p->next = last; - last = p; - if (!(flags & TREESAME)) - nr++; - on_list++; - } - list = last; - show_list("bisection 2 sorted", 0, nr, list); - - *all = nr; - weights = xcalloc(on_list, sizeof(*weights)); - - /* Do the real work of finding bisection commit. */ - best = do_find_bisection(list, nr, weights, find_all); - if (best) { - if (!find_all) - best->next = NULL; - *reaches = weight(best); - } - free(weights); - return best; -} - static inline int log2i(int n) { int log2 = 0; From 9996983c9c35353f352ef7c1abd9b3d2fbb21114 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:30 +0100 Subject: [PATCH 06/21] rev-list: move code to show bisect vars into its own function This is a straightforward clean up to make "cmd_rev_list" function smaller. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-rev-list.c | 83 +++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index b1e8200d2..74d22b465 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -226,6 +226,49 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } +static int show_bisect_vars(int reaches, int all, int bisect_find_all) +{ + int cnt; + char hex[41]; + + if (!revs.commits) + return 1; + + /* + * revs.commits can reach "reaches" commits among + * "all" commits. If it is good, then there are + * (all-reaches) commits left to be bisected. + * On the other hand, if it is bad, then the set + * to bisect is "reaches". + * A bisect set of size N has (N-1) commits further + * to test, as we already know one bad one. + */ + cnt = all - reaches; + if (cnt < reaches) + cnt = reaches; + strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); + + if (bisect_find_all) { + traverse_commit_list(&revs, show_commit, show_object); + printf("------\n"); + } + + printf("bisect_rev=%s\n" + "bisect_nr=%d\n" + "bisect_good=%d\n" + "bisect_bad=%d\n" + "bisect_all=%d\n" + "bisect_steps=%d\n", + hex, + cnt - 1, + all - reaches - 1, + reaches - 1, + all, + estimate_bisect_steps(all)); + + return 0; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct commit_list *list; @@ -313,44 +356,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, bisect_find_all); - if (bisect_show_vars) { - int cnt; - char hex[41]; - if (!revs.commits) - return 1; - /* - * revs.commits can reach "reaches" commits among - * "all" commits. If it is good, then there are - * (all-reaches) commits left to be bisected. - * On the other hand, if it is bad, then the set - * to bisect is "reaches". - * A bisect set of size N has (N-1) commits further - * to test, as we already know one bad one. - */ - cnt = all - reaches; - if (cnt < reaches) - cnt = reaches; - strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); - - if (bisect_find_all) { - traverse_commit_list(&revs, show_commit, show_object); - printf("------\n"); - } - - printf("bisect_rev=%s\n" - "bisect_nr=%d\n" - "bisect_good=%d\n" - "bisect_bad=%d\n" - "bisect_all=%d\n" - "bisect_steps=%d\n", - hex, - cnt - 1, - all - reaches - 1, - reaches - 1, - all, - estimate_bisect_steps(all)); - return 0; - } + if (bisect_show_vars) + return show_bisect_vars(reaches, all, bisect_find_all); } traverse_commit_list(&revs, From 6a17fad73369173ca71d3adf0d4335a0c8137cb9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:35 +0100 Subject: [PATCH 07/21] rev-list: make "show_bisect_vars" non static and declare it in "bisect.h" as we will use this function later. While at it, rename its last argument "show_all" instead of "bisect_find_all". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.h | 2 ++ builtin-rev-list.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bisect.h b/bisect.h index 60b2fe1cd..860a15c9b 100644 --- a/bisect.h +++ b/bisect.h @@ -5,4 +5,6 @@ extern struct commit_list *find_bisection(struct commit_list *list, int *reaches, int *all, int find_all); +extern int show_bisect_vars(int reaches, int all, int show_all); + #endif diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 74d22b465..c700c34be 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -226,7 +226,7 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } -static int show_bisect_vars(int reaches, int all, int bisect_find_all) +int show_bisect_vars(int reaches, int all, int show_all) { int cnt; char hex[41]; @@ -246,9 +246,10 @@ static int show_bisect_vars(int reaches, int all, int bisect_find_all) cnt = all - reaches; if (cnt < reaches) cnt = reaches; + strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); - if (bisect_find_all) { + if (show_all) { traverse_commit_list(&revs, show_commit, show_object); printf("------\n"); } From 7428d754e2dec9e82253d1e02b4df20fab3f3384 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:41 +0100 Subject: [PATCH 08/21] rev-list: pass "revs" to "show_bisect_vars" instead of using static "revs" data Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.h | 3 ++- builtin-rev-list.c | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/bisect.h b/bisect.h index 860a15c9b..31c99fe5f 100644 --- a/bisect.h +++ b/bisect.h @@ -5,6 +5,7 @@ extern struct commit_list *find_bisection(struct commit_list *list, int *reaches, int *all, int find_all); -extern int show_bisect_vars(int reaches, int all, int show_all); +extern int show_bisect_vars(struct rev_info *revs, int reaches, int all, + int show_all); #endif diff --git a/builtin-rev-list.c b/builtin-rev-list.c index c700c34be..cdb0f9d91 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -226,16 +226,16 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } -int show_bisect_vars(int reaches, int all, int show_all) +int show_bisect_vars(struct rev_info *revs, int reaches, int all, int show_all) { int cnt; char hex[41]; - if (!revs.commits) + if (!revs->commits) return 1; /* - * revs.commits can reach "reaches" commits among + * revs->commits can reach "reaches" commits among * "all" commits. If it is good, then there are * (all-reaches) commits left to be bisected. * On the other hand, if it is bad, then the set @@ -247,10 +247,10 @@ int show_bisect_vars(int reaches, int all, int show_all) if (cnt < reaches) cnt = reaches; - strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); + strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); if (show_all) { - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(revs, show_commit, show_object); printf("------\n"); } @@ -358,7 +358,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, bisect_find_all); if (bisect_show_vars) - return show_bisect_vars(reaches, all, bisect_find_all); + return show_bisect_vars(&revs, reaches, all, + bisect_find_all); } traverse_commit_list(&revs, From 96beef8c2efaab06f703991ed7802b8cef4c00e3 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 4 Apr 2009 22:59:26 +0200 Subject: [PATCH 09/21] sha1-lookup: add new "sha1_pos" function to efficiently lookup sha1 This function has been copied from the "patch_pos" function in "patch-ids.c" but an additional parameter has been added. The new parameter is a function pointer, that is used to access the sha1 of an element in the table. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- sha1-lookup.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ sha1-lookup.h | 7 ++++ 2 files changed, 108 insertions(+) diff --git a/sha1-lookup.c b/sha1-lookup.c index da357479c..055dd87dc 100644 --- a/sha1-lookup.c +++ b/sha1-lookup.c @@ -1,6 +1,107 @@ #include "cache.h" #include "sha1-lookup.h" +static uint32_t take2(const unsigned char *sha1) +{ + return ((sha1[0] << 8) | sha1[1]); +} + +/* + * Conventional binary search loop looks like this: + * + * do { + * int mi = (lo + hi) / 2; + * int cmp = "entry pointed at by mi" minus "target"; + * if (!cmp) + * return (mi is the wanted one) + * if (cmp > 0) + * hi = mi; "mi is larger than target" + * else + * lo = mi+1; "mi is smaller than target" + * } while (lo < hi); + * + * The invariants are: + * + * - When entering the loop, lo points at a slot that is never + * above the target (it could be at the target), hi points at a + * slot that is guaranteed to be above the target (it can never + * be at the target). + * + * - We find a point 'mi' between lo and hi (mi could be the same + * as lo, but never can be the same as hi), and check if it hits + * the target. There are three cases: + * + * - if it is a hit, we are happy. + * + * - if it is strictly higher than the target, we update hi with + * it. + * + * - if it is strictly lower than the target, we update lo to be + * one slot after it, because we allow lo to be at the target. + * + * When choosing 'mi', we do not have to take the "middle" but + * anywhere in between lo and hi, as long as lo <= mi < hi is + * satisfied. When we somehow know that the distance between the + * target and lo is much shorter than the target and hi, we could + * pick mi that is much closer to lo than the midway. + */ +/* + * The table should contain "nr" elements. + * The sha1 of element i (between 0 and nr - 1) should be returned + * by "fn(i, table)". + */ +int sha1_pos(const unsigned char *sha1, void *table, size_t nr, + sha1_access_fn fn) +{ + size_t hi = nr; + size_t lo = 0; + size_t mi = 0; + + if (!nr) + return -1; + + if (nr != 1) { + size_t lov, hiv, miv, ofs; + + for (ofs = 0; ofs < 18; ofs += 2) { + lov = take2(fn(0, table) + ofs); + hiv = take2(fn(nr - 1, table) + ofs); + miv = take2(sha1 + ofs); + if (miv < lov) + return -1; + if (hiv < miv) + return -1 - nr; + if (lov != hiv) { + /* + * At this point miv could be equal + * to hiv (but sha1 could still be higher); + * the invariant of (mi < hi) should be + * kept. + */ + mi = (nr - 1) * (miv - lov) / (hiv - lov); + if (lo <= mi && mi < hi) + break; + die("oops"); + } + } + if (18 <= ofs) + die("cannot happen -- lo and hi are identical"); + } + + do { + int cmp; + cmp = hashcmp(fn(mi, table), sha1); + if (!cmp) + return mi; + if (cmp > 0) + hi = mi; + else + lo = mi + 1; + mi = (hi + lo) / 2; + } while (lo < hi); + return -lo-1; +} + /* * Conventional binary search loop looks like this: * diff --git a/sha1-lookup.h b/sha1-lookup.h index 3249a81b3..20af28568 100644 --- a/sha1-lookup.h +++ b/sha1-lookup.h @@ -1,6 +1,13 @@ #ifndef SHA1_LOOKUP_H #define SHA1_LOOKUP_H +typedef const unsigned char *sha1_access_fn(size_t index, void *table); + +extern int sha1_pos(const unsigned char *sha1, + void *table, + size_t nr, + sha1_access_fn fn); + extern int sha1_entry_pos(const void *table, size_t elem_size, size_t key_offset, From 5289bae17f24805cc8507129e21d794b0b56264c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 4 Apr 2009 22:59:31 +0200 Subject: [PATCH 10/21] patch-ids: use the new generic "sha1_pos" function to lookup sha1 instead of the specific one from which the new one has been copied. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- patch-ids.c | 93 +++-------------------------------------------------- 1 file changed, 5 insertions(+), 88 deletions(-) diff --git a/patch-ids.c b/patch-ids.c index 3be5d3165..571725705 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -1,6 +1,7 @@ #include "cache.h" #include "diff.h" #include "commit.h" +#include "sha1-lookup.h" #include "patch-ids.h" static int commit_patch_id(struct commit *commit, struct diff_options *options, @@ -15,99 +16,15 @@ static int commit_patch_id(struct commit *commit, struct diff_options *options, return diff_flush_patch_id(options, sha1); } -static uint32_t take2(const unsigned char *id) +static const unsigned char *patch_id_access(size_t index, void *table) { - return ((id[0] << 8) | id[1]); + struct patch_id **id_table = table; + return id_table[index]->patch_id; } -/* - * Conventional binary search loop looks like this: - * - * do { - * int mi = (lo + hi) / 2; - * int cmp = "entry pointed at by mi" minus "target"; - * if (!cmp) - * return (mi is the wanted one) - * if (cmp > 0) - * hi = mi; "mi is larger than target" - * else - * lo = mi+1; "mi is smaller than target" - * } while (lo < hi); - * - * The invariants are: - * - * - When entering the loop, lo points at a slot that is never - * above the target (it could be at the target), hi points at a - * slot that is guaranteed to be above the target (it can never - * be at the target). - * - * - We find a point 'mi' between lo and hi (mi could be the same - * as lo, but never can be the same as hi), and check if it hits - * the target. There are three cases: - * - * - if it is a hit, we are happy. - * - * - if it is strictly higher than the target, we update hi with - * it. - * - * - if it is strictly lower than the target, we update lo to be - * one slot after it, because we allow lo to be at the target. - * - * When choosing 'mi', we do not have to take the "middle" but - * anywhere in between lo and hi, as long as lo <= mi < hi is - * satisfied. When we somehow know that the distance between the - * target and lo is much shorter than the target and hi, we could - * pick mi that is much closer to lo than the midway. - */ static int patch_pos(struct patch_id **table, int nr, const unsigned char *id) { - int hi = nr; - int lo = 0; - int mi = 0; - - if (!nr) - return -1; - - if (nr != 1) { - unsigned lov, hiv, miv, ofs; - - for (ofs = 0; ofs < 18; ofs += 2) { - lov = take2(table[0]->patch_id + ofs); - hiv = take2(table[nr-1]->patch_id + ofs); - miv = take2(id + ofs); - if (miv < lov) - return -1; - if (hiv < miv) - return -1 - nr; - if (lov != hiv) { - /* - * At this point miv could be equal - * to hiv (but id could still be higher); - * the invariant of (mi < hi) should be - * kept. - */ - mi = (nr-1) * (miv - lov) / (hiv - lov); - if (lo <= mi && mi < hi) - break; - die("oops"); - } - } - if (18 <= ofs) - die("cannot happen -- lo and hi are identical"); - } - - do { - int cmp; - cmp = hashcmp(table[mi]->patch_id, id); - if (!cmp) - return mi; - if (cmp > 0) - hi = mi; - else - lo = mi + 1; - mi = (hi + lo) / 2; - } while (lo < hi); - return -lo-1; + return sha1_pos(id, table, nr, patch_id_access); } #define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */ From 951886481668b97485640a1b24fc73fccff0d629 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:49 +0100 Subject: [PATCH 11/21] rev-list: call new "filter_skip" function This patch implements a new "filter_skip" function in C in "bisect.c" that will later replace the existing implementation in shell in "git-bisect.sh". An array is used to store the skipped commits. But the array is not yet fed anything. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++ bisect.h | 6 ++++- builtin-rev-list.c | 30 ++++++++++++++++++---- 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/bisect.c b/bisect.c index 27def7dac..9178a7a8e 100644 --- a/bisect.c +++ b/bisect.c @@ -4,6 +4,9 @@ #include "revision.h" #include "bisect.h" +static unsigned char (*skipped_sha1)[20]; +static int skipped_sha1_nr; + /* bits #0-15 in revision.h */ #define COUNTED (1u<<16) @@ -386,3 +389,63 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } +static int skipcmp(const void *a, const void *b) +{ + return hashcmp(a, b); +} + +static void prepare_skipped(void) +{ + qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp); +} + +static int lookup_skipped(unsigned char *sha1) +{ + int lo, hi; + lo = 0; + hi = skipped_sha1_nr; + while (lo < hi) { + int mi = (lo + hi) / 2; + int cmp = hashcmp(sha1, skipped_sha1[mi]); + if (!cmp) + return mi; + if (cmp < 0) + hi = mi; + else + lo = mi + 1; + } + return -lo - 1; +} + +struct commit_list *filter_skipped(struct commit_list *list, + struct commit_list **tried, + int show_all) +{ + struct commit_list *filtered = NULL, **f = &filtered; + + *tried = NULL; + + if (!skipped_sha1_nr) + return list; + + prepare_skipped(); + + while (list) { + struct commit_list *next = list->next; + list->next = NULL; + if (0 <= lookup_skipped(list->item->object.sha1)) { + /* Move current to tried list */ + *tried = list; + tried = &list->next; + } else { + if (!show_all) + return list; + /* Move current to filtered list */ + *f = list; + f = &list->next; + } + list = next; + } + + return filtered; +} diff --git a/bisect.h b/bisect.h index 31c99fe5f..2489630da 100644 --- a/bisect.h +++ b/bisect.h @@ -5,7 +5,11 @@ extern struct commit_list *find_bisection(struct commit_list *list, int *reaches, int *all, int find_all); +extern struct commit_list *filter_skipped(struct commit_list *list, + struct commit_list **tried, + int show_all); + extern int show_bisect_vars(struct rev_info *revs, int reaches, int all, - int show_all); + int show_all, int show_tried); #endif diff --git a/builtin-rev-list.c b/builtin-rev-list.c index cdb0f9d91..925d64356 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -226,14 +226,28 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } -int show_bisect_vars(struct rev_info *revs, int reaches, int all, int show_all) +static void show_tried_revs(struct commit_list *tried) +{ + printf("bisect_tried='"); + for (;tried; tried = tried->next) { + char *format = tried->next ? "%s|" : "%s"; + printf(format, sha1_to_hex(tried->item->object.sha1)); + } + printf("'\n"); +} + +int show_bisect_vars(struct rev_info *revs, int reaches, int all, + int show_all, int show_tried) { int cnt; - char hex[41]; + char hex[41] = ""; + struct commit_list *tried; - if (!revs->commits) + if (!revs->commits && !show_tried) return 1; + revs->commits = filter_skipped(revs->commits, &tried, show_all); + /* * revs->commits can reach "reaches" commits among * "all" commits. If it is good, then there are @@ -247,13 +261,16 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int show_all) if (cnt < reaches) cnt = reaches; - strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); + if (revs->commits) + strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); if (show_all) { traverse_commit_list(revs, show_commit, show_object); printf("------\n"); } + if (show_tried) + show_tried_revs(tried); printf("bisect_rev=%s\n" "bisect_nr=%d\n" "bisect_good=%d\n" @@ -278,6 +295,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int bisect_show_all = 0; int quiet = 0; git_config(git_default_config, NULL); @@ -305,6 +323,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--bisect-all")) { bisect_list = 1; bisect_find_all = 1; + bisect_show_all = 1; revs.show_decorations = 1; continue; } @@ -357,9 +376,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, bisect_find_all); + if (bisect_show_vars) return show_bisect_vars(&revs, reaches, all, - bisect_find_all); + bisect_show_all, 0); } traverse_commit_list(&revs, From 4eb5b64631d281f3789b052efac53f4c1ec2c1b6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 4 Apr 2009 22:59:36 +0200 Subject: [PATCH 12/21] bisect: use the new generic "sha1_pos" function to lookup sha1 instead of the specific one that was simpler but less efficient. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/bisect.c b/bisect.c index 9178a7a8e..47120c1cd 100644 --- a/bisect.c +++ b/bisect.c @@ -2,6 +2,7 @@ #include "commit.h" #include "diff.h" #include "revision.h" +#include "sha1-lookup.h" #include "bisect.h" static unsigned char (*skipped_sha1)[20]; @@ -399,22 +400,16 @@ static void prepare_skipped(void) qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp); } +static const unsigned char *skipped_sha1_access(size_t index, void *table) +{ + unsigned char (*skipped)[20] = table; + return skipped[index]; +} + static int lookup_skipped(unsigned char *sha1) { - int lo, hi; - lo = 0; - hi = skipped_sha1_nr; - while (lo < hi) { - int mi = (lo + hi) / 2; - int cmp = hashcmp(sha1, skipped_sha1[mi]); - if (!cmp) - return mi; - if (cmp < 0) - hi = mi; - else - lo = mi + 1; - } - return -lo - 1; + return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr, + skipped_sha1_access); } struct commit_list *filter_skipped(struct commit_list *list, From 1bf072e3661eeef8d9721079a332e804b5678c7e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:54 +0100 Subject: [PATCH 13/21] bisect--helper: implement "git bisect--helper" This patch implements a new "git bisect--helper" builtin plumbing command that will be used to migrate "git-bisect.sh" to C. We start by implementing only the "--next-vars" option that will read bisect refs from "refs/bisect/", and then compute the next bisect step, and output shell variables ready to be eval'ed by the shell. At this step, "git bisect--helper" ignores the paths that may have been put in "$GIT_DIR/BISECT_NAMES". This will be fixed in a later patch. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Makefile | 1 + bisect.c | 68 ++++++++++++++++++++++++++++++++++++++++ bisect.h | 7 +++++ builtin-bisect--helper.c | 27 ++++++++++++++++ builtin.h | 1 + git.c | 1 + 6 files changed, 105 insertions(+) create mode 100644 builtin-bisect--helper.c diff --git a/Makefile b/Makefile index 42cabe816..a2bfad43b 100644 --- a/Makefile +++ b/Makefile @@ -533,6 +533,7 @@ BUILTIN_OBJS += builtin-add.o BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-apply.o BUILTIN_OBJS += builtin-archive.o +BUILTIN_OBJS += builtin-bisect--helper.o BUILTIN_OBJS += builtin-blame.o BUILTIN_OBJS += builtin-branch.o BUILTIN_OBJS += builtin-bundle.o diff --git a/bisect.c b/bisect.c index 47120c1cd..94ec01178 100644 --- a/bisect.c +++ b/bisect.c @@ -2,11 +2,18 @@ #include "commit.h" #include "diff.h" #include "revision.h" +#include "refs.h" +#include "list-objects.h" #include "sha1-lookup.h" #include "bisect.h" static unsigned char (*skipped_sha1)[20]; static int skipped_sha1_nr; +static int skipped_sha1_alloc; + +static const char **rev_argv; +static int rev_argv_nr; +static int rev_argv_alloc; /* bits #0-15 in revision.h */ @@ -390,6 +397,33 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } +static int register_ref(const char *refname, const unsigned char *sha1, + int flags, void *cb_data) +{ + if (!strcmp(refname, "bad")) { + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1)); + } else if (!prefixcmp(refname, "good-")) { + const char *hex = sha1_to_hex(sha1); + char *good = xmalloc(strlen(hex) + 2); + *good = '^'; + memcpy(good + 1, hex, strlen(hex) + 1); + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = good; + } else if (!prefixcmp(refname, "skip-")) { + ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1, + skipped_sha1_alloc); + hashcpy(skipped_sha1[skipped_sha1_nr++], sha1); + } + + return 0; +} + +static int read_bisect_refs(void) +{ + return for_each_ref_in("refs/bisect/", register_ref, NULL); +} + static int skipcmp(const void *a, const void *b) { return hashcmp(a, b); @@ -444,3 +478,37 @@ struct commit_list *filter_skipped(struct commit_list *list, return filtered; } + +int bisect_next_vars(const char *prefix) +{ + struct rev_info revs; + int reaches = 0, all = 0; + + init_revisions(&revs, prefix); + revs.abbrev = 0; + revs.commit_format = CMIT_FMT_UNSPECIFIED; + + /* argv[0] will be ignored by setup_revisions */ + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup"); + + if (read_bisect_refs()) + die("reading bisect refs failed"); + + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = xstrdup("--"); + + setup_revisions(rev_argv_nr, rev_argv, &revs, NULL); + + revs.limited = 1; + + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + if (revs.tree_objects) + mark_edges_uninteresting(revs.commits, &revs, NULL); + + revs.commits = find_bisection(revs.commits, &reaches, &all, + !!skipped_sha1_nr); + + return show_bisect_vars(&revs, reaches, all, 0, 1); +} diff --git a/bisect.h b/bisect.h index 2489630da..05eea175f 100644 --- a/bisect.h +++ b/bisect.h @@ -9,7 +9,14 @@ extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all); +/* + * The "show_all" parameter should be 0 if this function is called + * from outside "builtin-rev-list.c" as otherwise it would use + * static "revs" from this file. + */ extern int show_bisect_vars(struct rev_info *revs, int reaches, int all, int show_all, int show_tried); +extern int bisect_next_vars(const char *prefix); + #endif diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c new file mode 100644 index 000000000..8fe778766 --- /dev/null +++ b/builtin-bisect--helper.c @@ -0,0 +1,27 @@ +#include "builtin.h" +#include "cache.h" +#include "parse-options.h" +#include "bisect.h" + +static const char * const git_bisect_helper_usage[] = { + "git bisect--helper --next-vars", + NULL +}; + +int cmd_bisect__helper(int argc, const char **argv, const char *prefix) +{ + int next_vars = 0; + struct option options[] = { + OPT_BOOLEAN(0, "next-vars", &next_vars, + "output next bisect step variables"), + OPT_END() + }; + + argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0); + + if (!next_vars) + usage_with_options(git_bisect_helper_usage, options); + + /* next-vars */ + return bisect_next_vars(prefix); +} diff --git a/builtin.h b/builtin.h index 1495cf6a2..425ff8e89 100644 --- a/builtin.h +++ b/builtin.h @@ -25,6 +25,7 @@ extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); extern int cmd_archive(int argc, const char **argv, const char *prefix); +extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix); extern int cmd_blame(int argc, const char **argv, const char *prefix); extern int cmd_branch(int argc, const char **argv, const char *prefix); extern int cmd_bundle(int argc, const char **argv, const char *prefix); diff --git a/git.c b/git.c index c2b181ed7..a553926b6 100644 --- a/git.c +++ b/git.c @@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv) { "annotate", cmd_annotate, RUN_SETUP }, { "apply", cmd_apply }, { "archive", cmd_archive }, + { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE }, { "blame", cmd_blame, RUN_SETUP }, { "branch", cmd_branch, RUN_SETUP }, { "bundle", cmd_bundle }, From 3b437b0dabfdff12d5dd78b9bb55a0be4e2da51c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:59 +0100 Subject: [PATCH 14/21] bisect: implement "read_bisect_paths" to read paths in "$GIT_DIR/BISECT_NAMES" This is needed because "git bisect--helper" must read bisect paths in "$GIT_DIR/BISECT_NAMES", so that a bisection can be performed only on commits that touches paths in this file. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/bisect.c b/bisect.c index 94ec01178..3279fb12b 100644 --- a/bisect.c +++ b/bisect.c @@ -4,6 +4,7 @@ #include "revision.h" #include "refs.h" #include "list-objects.h" +#include "quote.h" #include "sha1-lookup.h" #include "bisect.h" @@ -424,6 +425,32 @@ static int read_bisect_refs(void) return for_each_ref_in("refs/bisect/", register_ref, NULL); } +void read_bisect_paths(void) +{ + struct strbuf str = STRBUF_INIT; + const char *filename = git_path("BISECT_NAMES"); + FILE *fp = fopen(filename, "r"); + + if (!fp) + die("Could not open file '%s': %s", filename, strerror(errno)); + + while (strbuf_getline(&str, fp, '\n') != EOF) { + char *quoted; + int res; + + strbuf_trim(&str); + quoted = strbuf_detach(&str, NULL); + res = sq_dequote_to_argv(quoted, &rev_argv, + &rev_argv_nr, &rev_argv_alloc); + if (res) + die("Badly quoted content in file '%s': %s", + filename, quoted); + } + + strbuf_release(&str); + fclose(fp); +} + static int skipcmp(const void *a, const void *b) { return hashcmp(a, b); @@ -479,14 +506,11 @@ struct commit_list *filter_skipped(struct commit_list *list, return filtered; } -int bisect_next_vars(const char *prefix) +static void bisect_rev_setup(struct rev_info *revs, const char *prefix) { - struct rev_info revs; - int reaches = 0, all = 0; - - init_revisions(&revs, prefix); - revs.abbrev = 0; - revs.commit_format = CMIT_FMT_UNSPECIFIED; + init_revisions(revs, prefix); + revs->abbrev = 0; + revs->commit_format = CMIT_FMT_UNSPECIFIED; /* argv[0] will be ignored by setup_revisions */ ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); @@ -498,9 +522,22 @@ int bisect_next_vars(const char *prefix) ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); rev_argv[rev_argv_nr++] = xstrdup("--"); - setup_revisions(rev_argv_nr, rev_argv, &revs, NULL); + read_bisect_paths(); + + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = NULL; + + setup_revisions(rev_argv_nr, rev_argv, revs, NULL); + + revs->limited = 1; +} + +int bisect_next_vars(const char *prefix) +{ + struct rev_info revs; + int reaches = 0, all = 0; - revs.limited = 1; + bisect_rev_setup(&revs, prefix); if (prepare_revision_walk(&revs)) die("revision walk setup failed"); From 23b5f18b50c15155f79618522b5721b880eceb65 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:56:02 +0100 Subject: [PATCH 15/21] bisect: use "bisect--helper" and remove "filter_skipped" function Use the new "git bisect--helper" builtin. It should be faster and safer instead of the old "filter_skipped" shell function. And it is a first step to move more shell code to C. As the output is a little bit different we have to change the code that interpret the results. But these changes improve code clarity. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 89 +++++++-------------------------------------------- 1 file changed, 12 insertions(+), 77 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index e313bdea7..0f7590dfc 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -279,76 +279,13 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -filter_skipped() { +eval_and_string_together() { _eval="$1" - _skip="$2" - - if [ -z "$_skip" ]; then - eval "$_eval" | { - while read line - do - echo "$line &&" - done - echo ':' - } - return - fi - # Let's parse the output of: - # "git rev-list --bisect-vars --bisect-all ..." eval "$_eval" | { - VARS= FOUND= TRIED= - while read hash line + while read line do - case "$VARS,$FOUND,$TRIED,$hash" in - 1,*,*,*) - # "bisect_foo=bar" read from rev-list output. - echo "$hash &&" - ;; - ,*,*,---*) - # Separator - ;; - ,,,bisect_rev*) - # We had nothing to search. - echo "bisect_rev= &&" - VARS=1 - ;; - ,,*,bisect_rev*) - # We did not find a good bisect rev. - # This should happen only if the "bad" - # commit is also a "skip" commit. - echo "bisect_rev='$TRIED' &&" - VARS=1 - ;; - ,,*,*) - # We are searching. - TRIED="${TRIED:+$TRIED|}$hash" - case "$_skip" in - *$hash*) ;; - *) - echo "bisect_rev=$hash &&" - echo "bisect_tried='$TRIED' &&" - FOUND=1 - ;; - esac - ;; - ,1,*,bisect_rev*) - # We have already found a rev to be tested. - VARS=1 - ;; - ,1,*,*) - ;; - *) - # Unexpected input - echo "die 'filter_skipped error'" - die "filter_skipped error " \ - "VARS: '$VARS' " \ - "FOUND: '$FOUND' " \ - "TRIED: '$TRIED' " \ - "hash: '$hash' " \ - "line: '$line'" - ;; - esac + echo "$line &&" done echo ':' } @@ -356,10 +293,12 @@ filter_skipped() { exit_if_skipped_commits () { _tried=$1 - if expr "$_tried" : ".*[|].*" > /dev/null ; then + _bad=$2 + if test -n "$_tried" ; then echo "There are only 'skip'ped commit left to test." echo "The first bad commit could be any of:" echo "$_tried" | tr '[|]' '[\012]' + test -n "$_bad" && echo "$_bad" echo "We cannot bisect more!" exit 2 fi @@ -490,28 +429,24 @@ bisect_next() { test "$?" -eq "1" && return # Get bisection information - BISECT_OPT='' - test -n "$skip" && BISECT_OPT='--bisect-all' - eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" && - eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" && - eval=$(filter_skipped "$eval" "$skip") && + eval="git bisect--helper --next-vars" && + eval=$(eval_and_string_together "$eval") && eval "$eval" || exit if [ -z "$bisect_rev" ]; then + # We should exit here only if the "bad" + # commit is also a "skip" commit (see above). + exit_if_skipped_commits "$bisect_tried" echo "$bad was both good and bad" exit 1 fi if [ "$bisect_rev" = "$bad" ]; then - exit_if_skipped_commits "$bisect_tried" + exit_if_skipped_commits "$bisect_tried" "$bad" echo "$bisect_rev is first bad commit" git diff-tree --pretty $bisect_rev exit 0 fi - # We should exit here only if the "bad" - # commit is also a "skip" commit (see above). - exit_if_skipped_commits "$bisect_rev" - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)" } From b74d7efb108c9d3fd2d057b0c452583552a0577a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 29 Mar 2009 11:45:01 +0200 Subject: [PATCH 16/21] t6030: test bisecting with paths This patch adds some tests to check that "git bisect" works fine when passing paths to "git bisect start" to reduce the number of bisection steps. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 052a6c90f..54b7ea650 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -506,6 +506,66 @@ test_expect_success 'optimized merge base checks' ' unset GIT_TRACE ' +# This creates another side branch called "parallel" with some files +# in some directories, to test bisecting with paths. +# +# We should have the following: +# +# P1-P2-P3-P4-P5-P6-P7 +# / / / +# H1-H2-H3-H4-H5-H6-H7 +# \ \ \ +# S5-A \ +# \ \ +# S6-S7----B +# +test_expect_success '"parallel" side branch creation' ' + git bisect reset && + git checkout -b parallel $HASH1 && + mkdir dir1 dir2 && + add_line_into_file "1(para): line 1 on parallel branch" dir1/file1 && + PARA_HASH1=$(git rev-parse --verify HEAD) && + add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 && + PARA_HASH2=$(git rev-parse --verify HEAD) && + add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 && + PARA_HASH3=$(git rev-parse --verify HEAD) + git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" && + PARA_HASH4=$(git rev-parse --verify HEAD) + add_line_into_file "5(para): add line on parallel branch" dir1/file1 && + PARA_HASH5=$(git rev-parse --verify HEAD) + add_line_into_file "6(para): add line on parallel branch" dir2/file2 && + PARA_HASH6=$(git rev-parse --verify HEAD) + git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" && + PARA_HASH7=$(git rev-parse --verify HEAD) +' + +test_expect_success 'restricting bisection on one dir' ' + git bisect reset && + git bisect start HEAD $HASH1 -- dir1 && + para1=$(git rev-parse --verify HEAD) && + test "$para1" = "$PARA_HASH1" && + git bisect bad > my_bisect_log.txt && + grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt +' + +test_expect_success 'restricting bisection on one dir and a file' ' + git bisect reset && + git bisect start HEAD $HASH1 -- dir1 hello && + para4=$(git rev-parse --verify HEAD) && + test "$para4" = "$PARA_HASH4" && + git bisect bad && + hash3=$(git rev-parse --verify HEAD) && + test "$hash3" = "$HASH3" && + git bisect good && + hash4=$(git rev-parse --verify HEAD) && + test "$hash4" = "$HASH4" && + git bisect good && + para1=$(git rev-parse --verify HEAD) && + test "$para1" = "$PARA_HASH1" && + git bisect good > my_bisect_log.txt && + grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt +' + # # test_done From 37c4c38d7356bf256d0297fdbac78ef8b6807fac Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 29 Mar 2009 11:55:43 +0200 Subject: [PATCH 17/21] rev-list: pass "int flags" as last argument of "show_bisect_vars" Instead of "int show_all, int show_tried" we now only pass "int flags", because we will add one more flag in a later patch. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 2 +- bisect.h | 8 ++++++-- builtin-rev-list.c | 13 ++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/bisect.c b/bisect.c index 3279fb12b..285bf146c 100644 --- a/bisect.c +++ b/bisect.c @@ -547,5 +547,5 @@ int bisect_next_vars(const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, !!skipped_sha1_nr); - return show_bisect_vars(&revs, reaches, all, 0, 1); + return show_bisect_vars(&revs, reaches, all, BISECT_SHOW_TRIED); } diff --git a/bisect.h b/bisect.h index 05eea175f..b9aa88482 100644 --- a/bisect.h +++ b/bisect.h @@ -9,13 +9,17 @@ extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all); +/* show_bisect_vars flags */ +#define BISECT_SHOW_ALL (1<<0) +#define BISECT_SHOW_TRIED (1<<1) + /* - * The "show_all" parameter should be 0 if this function is called + * The flag BISECT_SHOW_ALL should not be set if this function is called * from outside "builtin-rev-list.c" as otherwise it would use * static "revs" from this file. */ extern int show_bisect_vars(struct rev_info *revs, int reaches, int all, - int show_all, int show_tried); + int flags); extern int bisect_next_vars(const char *prefix); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 925d64356..69dca631d 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -236,17 +236,16 @@ static void show_tried_revs(struct commit_list *tried) printf("'\n"); } -int show_bisect_vars(struct rev_info *revs, int reaches, int all, - int show_all, int show_tried) +int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) { int cnt; char hex[41] = ""; struct commit_list *tried; - if (!revs->commits && !show_tried) + if (!revs->commits && !(flags & BISECT_SHOW_TRIED)) return 1; - revs->commits = filter_skipped(revs->commits, &tried, show_all); + revs->commits = filter_skipped(revs->commits, &tried, flags & BISECT_SHOW_ALL); /* * revs->commits can reach "reaches" commits among @@ -264,12 +263,12 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, if (revs->commits) strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); - if (show_all) { + if (flags & BISECT_SHOW_ALL) { traverse_commit_list(revs, show_commit, show_object); printf("------\n"); } - if (show_tried) + if (flags & BISECT_SHOW_TRIED) show_tried_revs(tried); printf("bisect_rev=%s\n" "bisect_nr=%d\n" @@ -379,7 +378,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_show_vars) return show_bisect_vars(&revs, reaches, all, - bisect_show_all, 0); + bisect_show_all ? BISECT_SHOW_ALL : 0); } traverse_commit_list(&revs, From e89aa6d2f546b2d4f2d88c15ce7e343751d6922f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 30 Mar 2009 06:59:59 +0200 Subject: [PATCH 18/21] bisect--helper: string output variables together with "&&" When doing: eval "git bisect--helper --next-vars" | { while read line do echo "$line &&" done echo ':' } the result code comes from the last "echo ':'", not from running "git bisect--helper --next-vars". This patch gets rid of the need to string together the line from the output of "git bisect--helper" with "&&" in the calling script by making "git bisect--helper --next-vars" return output variables already in that format. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 3 ++- bisect.h | 1 + builtin-rev-list.c | 29 +++++++++++++++++++---------- git-bisect.sh | 15 +-------------- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/bisect.c b/bisect.c index 285bf146c..69f8860ca 100644 --- a/bisect.c +++ b/bisect.c @@ -547,5 +547,6 @@ int bisect_next_vars(const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, !!skipped_sha1_nr); - return show_bisect_vars(&revs, reaches, all, BISECT_SHOW_TRIED); + return show_bisect_vars(&revs, reaches, all, + BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED); } diff --git a/bisect.h b/bisect.h index b9aa88482..f5d106735 100644 --- a/bisect.h +++ b/bisect.h @@ -12,6 +12,7 @@ extern struct commit_list *filter_skipped(struct commit_list *list, /* show_bisect_vars flags */ #define BISECT_SHOW_ALL (1<<0) #define BISECT_SHOW_TRIED (1<<1) +#define BISECT_SHOW_STRINGED (1<<2) /* * The flag BISECT_SHOW_ALL should not be set if this function is called diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 69dca631d..eb341477c 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -226,20 +226,20 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } -static void show_tried_revs(struct commit_list *tried) +static void show_tried_revs(struct commit_list *tried, int stringed) { printf("bisect_tried='"); for (;tried; tried = tried->next) { char *format = tried->next ? "%s|" : "%s"; printf(format, sha1_to_hex(tried->item->object.sha1)); } - printf("'\n"); + printf(stringed ? "' &&\n" : "'\n"); } int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) { int cnt; - char hex[41] = ""; + char hex[41] = "", *format; struct commit_list *tried; if (!revs->commits && !(flags & BISECT_SHOW_TRIED)) @@ -269,13 +269,22 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) } if (flags & BISECT_SHOW_TRIED) - show_tried_revs(tried); - printf("bisect_rev=%s\n" - "bisect_nr=%d\n" - "bisect_good=%d\n" - "bisect_bad=%d\n" - "bisect_all=%d\n" - "bisect_steps=%d\n", + show_tried_revs(tried, flags & BISECT_SHOW_STRINGED); + format = (flags & BISECT_SHOW_STRINGED) ? + "bisect_rev=%s &&\n" + "bisect_nr=%d &&\n" + "bisect_good=%d &&\n" + "bisect_bad=%d &&\n" + "bisect_all=%d &&\n" + "bisect_steps=%d\n" + : + "bisect_rev=%s\n" + "bisect_nr=%d\n" + "bisect_good=%d\n" + "bisect_bad=%d\n" + "bisect_all=%d\n" + "bisect_steps=%d\n"; + printf(format, hex, cnt - 1, all - reaches - 1, diff --git a/git-bisect.sh b/git-bisect.sh index 0f7590dfc..5074dda45 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -279,18 +279,6 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -eval_and_string_together() { - _eval="$1" - - eval "$_eval" | { - while read line - do - echo "$line &&" - done - echo ':' - } -} - exit_if_skipped_commits () { _tried=$1 _bad=$2 @@ -429,8 +417,7 @@ bisect_next() { test "$?" -eq "1" && return # Get bisection information - eval="git bisect--helper --next-vars" && - eval=$(eval_and_string_together "$eval") && + eval=$(eval "git bisect--helper --next-vars") && eval "$eval" || exit if [ -z "$bisect_rev" ]; then From 11c211fa06fc396e8ee8132ef83e2f2763ff6976 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 6 Apr 2009 21:28:36 +0200 Subject: [PATCH 19/21] list-objects: add "void *data" parameter to show functions The goal of this patch is to get rid of the "static struct rev_info revs" static variable in "builtin-rev-list.c". To do that, we need to pass the revs to the "show_commit" function in "builtin-rev-list.c" and this in turn means that the "traverse_commit_list" function in "list-objects.c" must be passed functions pointers to functions with 2 parameters instead of one. So we have to change all the callers and all the functions passed to "traverse_commit_list". Anyway this makes the code more clean and more generic, so it should be a good thing in the long run. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 6 ++-- builtin-rev-list.c | 68 ++++++++++++++++++++++-------------------- list-objects.c | 9 +++--- list-objects.h | 6 ++-- upload-pack.c | 6 ++-- 5 files changed, 49 insertions(+), 46 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 9fc3b3554..82536359d 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1901,13 +1901,13 @@ static void read_object_list_from_stdin(void) #define OBJECT_ADDED (1u<<20) -static void show_commit(struct commit *commit) +static void show_commit(struct commit *commit, void *data) { add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0); commit->object.flags |= OBJECT_ADDED; } -static void show_object(struct object_array_entry *p) +static void show_object(struct object_array_entry *p, void *data) { add_preferred_base_object(p->name); add_object_entry(p->item->sha1, p->item->type, p->name, 0); @@ -2071,7 +2071,7 @@ static void get_object_list(int ac, const char **av) if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(&revs, show_commit, show_object, NULL); if (keep_unreachable) add_objects_in_unpacked_packs(&revs); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index eb341477c..cd6f6b8fb 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -42,72 +42,72 @@ static const char rev_list_usage[] = " --bisect-all" ; -static struct rev_info revs; - static int show_timestamp; static int hdr_termination; static const char *header_prefix; -static void finish_commit(struct commit *commit); -static void show_commit(struct commit *commit) +static void finish_commit(struct commit *commit, void *data); +static void show_commit(struct commit *commit, void *data) { - graph_show_commit(revs.graph); + struct rev_info *revs = data; + + graph_show_commit(revs->graph); if (show_timestamp) printf("%lu ", commit->date); if (header_prefix) fputs(header_prefix, stdout); - if (!revs.graph) { + if (!revs->graph) { if (commit->object.flags & BOUNDARY) putchar('-'); else if (commit->object.flags & UNINTERESTING) putchar('^'); - else if (revs.left_right) { + else if (revs->left_right) { if (commit->object.flags & SYMMETRIC_LEFT) putchar('<'); else putchar('>'); } } - if (revs.abbrev_commit && revs.abbrev) - fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev), + if (revs->abbrev_commit && revs->abbrev) + fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev), stdout); else fputs(sha1_to_hex(commit->object.sha1), stdout); - if (revs.print_parents) { + if (revs->print_parents) { struct commit_list *parents = commit->parents; while (parents) { printf(" %s", sha1_to_hex(parents->item->object.sha1)); parents = parents->next; } } - if (revs.children.name) { + if (revs->children.name) { struct commit_list *children; - children = lookup_decoration(&revs.children, &commit->object); + children = lookup_decoration(&revs->children, &commit->object); while (children) { printf(" %s", sha1_to_hex(children->item->object.sha1)); children = children->next; } } - show_decorations(&revs, commit); - if (revs.commit_format == CMIT_FMT_ONELINE) + show_decorations(revs, commit); + if (revs->commit_format == CMIT_FMT_ONELINE) putchar(' '); else putchar('\n'); - if (revs.verbose_header && commit->buffer) { + if (revs->verbose_header && commit->buffer) { struct strbuf buf = STRBUF_INIT; - pretty_print_commit(revs.commit_format, commit, - &buf, revs.abbrev, NULL, NULL, - revs.date_mode, 0); - if (revs.graph) { + pretty_print_commit(revs->commit_format, commit, + &buf, revs->abbrev, NULL, NULL, + revs->date_mode, 0); + if (revs->graph) { if (buf.len) { - if (revs.commit_format != CMIT_FMT_ONELINE) - graph_show_oneline(revs.graph); + if (revs->commit_format != CMIT_FMT_ONELINE) + graph_show_oneline(revs->graph); - graph_show_commit_msg(revs.graph, &buf); + graph_show_commit_msg(revs->graph, &buf); /* * Add a newline after the commit message. @@ -125,7 +125,7 @@ static void show_commit(struct commit *commit) * format doesn't explicitly end in a newline.) */ if (buf.len && buf.buf[buf.len - 1] == '\n') - graph_show_padding(revs.graph); + graph_show_padding(revs->graph); putchar('\n'); } else { /* @@ -133,7 +133,7 @@ static void show_commit(struct commit *commit) * the rest of the graph output for this * commit. */ - if (graph_show_remainder(revs.graph)) + if (graph_show_remainder(revs->graph)) putchar('\n'); } } else { @@ -142,14 +142,14 @@ static void show_commit(struct commit *commit) } strbuf_release(&buf); } else { - if (graph_show_remainder(revs.graph)) + if (graph_show_remainder(revs->graph)) putchar('\n'); } maybe_flush_or_die(stdout, "stdout"); - finish_commit(commit); + finish_commit(commit, data); } -static void finish_commit(struct commit *commit) +static void finish_commit(struct commit *commit, void *data) { if (commit->parents) { free_commit_list(commit->parents); @@ -159,20 +159,20 @@ static void finish_commit(struct commit *commit) commit->buffer = NULL; } -static void finish_object(struct object_array_entry *p) +static void finish_object(struct object_array_entry *p, void *data) { if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); } -static void show_object(struct object_array_entry *p) +static void show_object(struct object_array_entry *p, void *data) { /* An object with name "foo\n0000000..." can be used to * confuse downstream "git pack-objects" very badly. */ const char *ep = strchr(p->name, '\n'); - finish_object(p); + finish_object(p, data); if (ep) { printf("%s %.*s\n", sha1_to_hex(p->item->sha1), (int) (ep - p->name), @@ -264,7 +264,7 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); if (flags & BISECT_SHOW_ALL) { - traverse_commit_list(revs, show_commit, show_object); + traverse_commit_list(revs, show_commit, show_object, revs); printf("------\n"); } @@ -297,6 +297,7 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) int cmd_rev_list(int argc, const char **argv, const char *prefix) { + struct rev_info revs; struct commit_list *list; int i; int read_from_stdin = 0; @@ -391,8 +392,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } traverse_commit_list(&revs, - quiet ? finish_commit : show_commit, - quiet ? finish_object : show_object); + quiet ? finish_commit : show_commit, + quiet ? finish_object : show_object, + &revs); return 0; } diff --git a/list-objects.c b/list-objects.c index c8b8375e4..208a4cb00 100644 --- a/list-objects.c +++ b/list-objects.c @@ -137,8 +137,9 @@ void mark_edges_uninteresting(struct commit_list *list, } void traverse_commit_list(struct rev_info *revs, - void (*show_commit)(struct commit *), - void (*show_object)(struct object_array_entry *)) + show_commit_fn show_commit, + show_object_fn show_object, + void *data) { int i; struct commit *commit; @@ -146,7 +147,7 @@ void traverse_commit_list(struct rev_info *revs, while ((commit = get_revision(revs)) != NULL) { process_tree(revs, commit->tree, &objects, NULL, ""); - show_commit(commit); + show_commit(commit, data); } for (i = 0; i < revs->pending.nr; i++) { struct object_array_entry *pending = revs->pending.objects + i; @@ -173,7 +174,7 @@ void traverse_commit_list(struct rev_info *revs, sha1_to_hex(obj->sha1), name); } for (i = 0; i < objects.nr; i++) - show_object(&objects.objects[i]); + show_object(&objects.objects[i], data); free(objects.objects); if (revs->pending.nr) { free(revs->pending.objects); diff --git a/list-objects.h b/list-objects.h index 0f41391ec..47fae2e46 100644 --- a/list-objects.h +++ b/list-objects.h @@ -1,11 +1,11 @@ #ifndef LIST_OBJECTS_H #define LIST_OBJECTS_H -typedef void (*show_commit_fn)(struct commit *); -typedef void (*show_object_fn)(struct object_array_entry *); +typedef void (*show_commit_fn)(struct commit *, void *); +typedef void (*show_object_fn)(struct object_array_entry *, void *); typedef void (*show_edge_fn)(struct commit *); -void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn); +void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *); void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn); diff --git a/upload-pack.c b/upload-pack.c index a49d87244..495c99f80 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -66,7 +66,7 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz) } static FILE *pack_pipe = NULL; -static void show_commit(struct commit *commit) +static void show_commit(struct commit *commit, void *data) { if (commit->object.flags & BOUNDARY) fputc('-', pack_pipe); @@ -78,7 +78,7 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } -static void show_object(struct object_array_entry *p) +static void show_object(struct object_array_entry *p, void *data) { /* An object with name "foo\n0000000..." can be used to * confuse downstream git-pack-objects very badly. @@ -134,7 +134,7 @@ static int do_rev_list(int fd, void *create_full_pack) if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(revs.commits, &revs, show_edge); - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(&revs, show_commit, show_object, NULL); fflush(pack_pipe); fclose(pack_pipe); return 0; From d797257eb280b67dd1f7153a66b03453c0fb927a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 6 Apr 2009 22:28:00 +0200 Subject: [PATCH 20/21] rev-list: remove last static vars used in "show_commit" This patch removes the last static variables that were used in the "show_commit" function. To do that, we create a new "rev_list_info" struct that we will pass in the "void *data" argument to "show_commit". This means that we have to change the first argument to "show_bisect_vars" too. While at it, we also remove a "struct commit_list *list" variable in "cmd_rev_list" that is not really needed. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 6 +++++- bisect.h | 14 ++++++++------ builtin-rev-list.c | 42 +++++++++++++++++++++--------------------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/bisect.c b/bisect.c index 69f8860ca..4d2a150df 100644 --- a/bisect.c +++ b/bisect.c @@ -535,8 +535,12 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix) int bisect_next_vars(const char *prefix) { struct rev_info revs; + struct rev_list_info info; int reaches = 0, all = 0; + memset(&info, 0, sizeof(info)); + info.revs = &revs; + bisect_rev_setup(&revs, prefix); if (prepare_revision_walk(&revs)) @@ -547,6 +551,6 @@ int bisect_next_vars(const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, !!skipped_sha1_nr); - return show_bisect_vars(&revs, reaches, all, + return show_bisect_vars(&info, reaches, all, BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED); } diff --git a/bisect.h b/bisect.h index f5d106735..b1c334d34 100644 --- a/bisect.h +++ b/bisect.h @@ -14,12 +14,14 @@ extern struct commit_list *filter_skipped(struct commit_list *list, #define BISECT_SHOW_TRIED (1<<1) #define BISECT_SHOW_STRINGED (1<<2) -/* - * The flag BISECT_SHOW_ALL should not be set if this function is called - * from outside "builtin-rev-list.c" as otherwise it would use - * static "revs" from this file. - */ -extern int show_bisect_vars(struct rev_info *revs, int reaches, int all, +struct rev_list_info { + struct rev_info *revs; + int show_timestamp; + int hdr_termination; + const char *header_prefix; +}; + +extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all, int flags); extern int bisect_next_vars(const char *prefix); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index cd6f6b8fb..244b73eae 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -42,21 +42,18 @@ static const char rev_list_usage[] = " --bisect-all" ; -static int show_timestamp; -static int hdr_termination; -static const char *header_prefix; - static void finish_commit(struct commit *commit, void *data); static void show_commit(struct commit *commit, void *data) { - struct rev_info *revs = data; + struct rev_list_info *info = data; + struct rev_info *revs = info->revs; graph_show_commit(revs->graph); - if (show_timestamp) + if (info->show_timestamp) printf("%lu ", commit->date); - if (header_prefix) - fputs(header_prefix, stdout); + if (info->header_prefix) + fputs(info->header_prefix, stdout); if (!revs->graph) { if (commit->object.flags & BOUNDARY) @@ -138,7 +135,7 @@ static void show_commit(struct commit *commit, void *data) } } else { if (buf.len) - printf("%s%c", buf.buf, hdr_termination); + printf("%s%c", buf.buf, info->hdr_termination); } strbuf_release(&buf); } else { @@ -236,11 +233,13 @@ static void show_tried_revs(struct commit_list *tried, int stringed) printf(stringed ? "' &&\n" : "'\n"); } -int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) +int show_bisect_vars(struct rev_list_info *info, int reaches, int all, + int flags) { int cnt; char hex[41] = "", *format; struct commit_list *tried; + struct rev_info *revs = info->revs; if (!revs->commits && !(flags & BISECT_SHOW_TRIED)) return 1; @@ -264,7 +263,7 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1)); if (flags & BISECT_SHOW_ALL) { - traverse_commit_list(revs, show_commit, show_object, revs); + traverse_commit_list(revs, show_commit, show_object, info); printf("------\n"); } @@ -298,7 +297,7 @@ int show_bisect_vars(struct rev_info *revs, int reaches, int all, int flags) int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; - struct commit_list *list; + struct rev_list_info info; int i; int read_from_stdin = 0; int bisect_list = 0; @@ -313,6 +312,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.commit_format = CMIT_FMT_UNSPECIFIED; argc = setup_revisions(argc, argv, &revs, NULL); + memset(&info, 0, sizeof(info)); + info.revs = &revs; + quiet = DIFF_OPT_TST(&revs.diffopt, QUIET); for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; @@ -322,7 +324,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--timestamp")) { - show_timestamp = 1; + info.show_timestamp = 1; continue; } if (!strcmp(arg, "--bisect")) { @@ -352,19 +354,17 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } if (revs.commit_format != CMIT_FMT_UNSPECIFIED) { /* The command line has a --pretty */ - hdr_termination = '\n'; + info.hdr_termination = '\n'; if (revs.commit_format == CMIT_FMT_ONELINE) - header_prefix = ""; + info.header_prefix = ""; else - header_prefix = "commit "; + info.header_prefix = "commit "; } else if (revs.verbose_header) /* Only --header was specified */ revs.commit_format = CMIT_FMT_RAW; - list = revs.commits; - - if ((!list && + if ((!revs.commits && (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending.nr)) || revs.diff) @@ -387,14 +387,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_find_all); if (bisect_show_vars) - return show_bisect_vars(&revs, reaches, all, + return show_bisect_vars(&info, reaches, all, bisect_show_all ? BISECT_SHOW_ALL : 0); } traverse_commit_list(&revs, quiet ? finish_commit : show_commit, quiet ? finish_object : show_object, - &revs); + &info); return 0; } From 13858e5770dd218e5318819d3273c916b46cf8e5 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 7 Apr 2009 05:08:42 +0200 Subject: [PATCH 21/21] rev-list: add "int bisect_show_flags" in "struct rev_list_info" This is a cleanup patch to make it easier to use the "show_bisect_vars" function and take advantage of the rev_list_info struct. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 4 ++-- bisect.h | 6 +++--- builtin-rev-list.c | 11 ++++------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/bisect.c b/bisect.c index 4d2a150df..58f7e6f77 100644 --- a/bisect.c +++ b/bisect.c @@ -540,6 +540,7 @@ int bisect_next_vars(const char *prefix) memset(&info, 0, sizeof(info)); info.revs = &revs; + info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED; bisect_rev_setup(&revs, prefix); @@ -551,6 +552,5 @@ int bisect_next_vars(const char *prefix) revs.commits = find_bisection(revs.commits, &reaches, &all, !!skipped_sha1_nr); - return show_bisect_vars(&info, reaches, all, - BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED); + return show_bisect_vars(&info, reaches, all); } diff --git a/bisect.h b/bisect.h index b1c334d34..fdba91387 100644 --- a/bisect.h +++ b/bisect.h @@ -9,20 +9,20 @@ extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all); -/* show_bisect_vars flags */ +/* bisect_show_flags flags in struct rev_list_info */ #define BISECT_SHOW_ALL (1<<0) #define BISECT_SHOW_TRIED (1<<1) #define BISECT_SHOW_STRINGED (1<<2) struct rev_list_info { struct rev_info *revs; + int bisect_show_flags; int show_timestamp; int hdr_termination; const char *header_prefix; }; -extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all, - int flags); +extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); extern int bisect_next_vars(const char *prefix); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 244b73eae..193993cf4 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -233,10 +233,9 @@ static void show_tried_revs(struct commit_list *tried, int stringed) printf(stringed ? "' &&\n" : "'\n"); } -int show_bisect_vars(struct rev_list_info *info, int reaches, int all, - int flags) +int show_bisect_vars(struct rev_list_info *info, int reaches, int all) { - int cnt; + int cnt, flags = info->bisect_show_flags; char hex[41] = "", *format; struct commit_list *tried; struct rev_info *revs = info->revs; @@ -303,7 +302,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; - int bisect_show_all = 0; int quiet = 0; git_config(git_default_config, NULL); @@ -334,7 +332,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--bisect-all")) { bisect_list = 1; bisect_find_all = 1; - bisect_show_all = 1; + info.bisect_show_flags = BISECT_SHOW_ALL; revs.show_decorations = 1; continue; } @@ -387,8 +385,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_find_all); if (bisect_show_vars) - return show_bisect_vars(&info, reaches, all, - bisect_show_all ? BISECT_SHOW_ALL : 0); + return show_bisect_vars(&info, reaches, all); } traverse_commit_list(&revs,