From a2ad79ced25e1b76fabec079549f521e8071ddd1 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:24 +0100 Subject: 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 --- bisect.c | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 bisect.c (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c new file mode 100644 index 0000000000..27def7dacf --- /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; +} + -- cgit v1.3 From 951886481668b97485640a1b24fc73fccff0d629 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:49 +0100 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 27def7dacf..9178a7a8e2 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 31c99fe5f4..2489630da0 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 cdb0f9d913..925d64356c 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, -- cgit v1.3 From 4eb5b64631d281f3789b052efac53f4c1ec2c1b6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 4 Apr 2009 22:59:36 +0200 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 9178a7a8e2..47120c1cd8 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, -- cgit v1.3 From 1bf072e3661eeef8d9721079a332e804b5678c7e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:54 +0100 Subject: 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 (limited to 'bisect.c') diff --git a/Makefile b/Makefile index 42cabe8162..a2bfad43bc 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 47120c1cd8..94ec011786 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 2489630da0..05eea175f7 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 0000000000..8fe778766a --- /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 1495cf6a20..425ff8e89b 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 c2b181ed78..a553926b68 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 }, -- cgit v1.3 From 3b437b0dabfdff12d5dd78b9bb55a0be4e2da51c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 26 Mar 2009 05:55:59 +0100 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 94ec011786..3279fb12b2 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"); -- cgit v1.3 From 37c4c38d7356bf256d0297fdbac78ef8b6807fac Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 29 Mar 2009 11:55:43 +0200 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 3279fb12b2..285bf146c1 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 05eea175f7..b9aa88482e 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 925d64356c..69dca631d9 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, -- cgit v1.3 From e89aa6d2f546b2d4f2d88c15ce7e343751d6922f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 30 Mar 2009 06:59:59 +0200 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 285bf146c1..69f8860ca1 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 b9aa88482e..f5d106735c 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 69dca631d9..eb341477ca 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 0f7590dfc2..5074dda451 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 -- cgit v1.3 From d797257eb280b67dd1f7153a66b03453c0fb927a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 6 Apr 2009 22:28:00 +0200 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 69f8860ca1..4d2a150df2 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 f5d106735c..b1c334d349 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 cd6f6b8fbb..244b73eaeb 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; } -- cgit v1.3 From 13858e5770dd218e5318819d3273c916b46cf8e5 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 7 Apr 2009 05:08:42 +0200 Subject: 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(-) (limited to 'bisect.c') diff --git a/bisect.c b/bisect.c index 4d2a150df2..58f7e6f773 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 b1c334d349..fdba913877 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 244b73eaeb..193993cf44 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, -- cgit v1.3