From 1c876546bda5dde35d4ae887ad6d24ed16ec953a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 19 Apr 2009 11:55:38 +0200 Subject: rev-list: make "estimate_bisect_steps" non static Because it will be used from "bisect.c" too. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.h | 2 ++ builtin-rev-list.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bisect.h b/bisect.h index fdba913877..cb37ddfac0 100644 --- a/bisect.h +++ b/bisect.h @@ -26,4 +26,6 @@ extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); extern int bisect_next_vars(const char *prefix); +extern int estimate_bisect_steps(int all); + #endif diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 38a8f234de..4c5f5eec0e 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -211,7 +211,7 @@ static inline int exp2i(int n) * * and P(2^n + x) < 0.5 means 2^n < 3x */ -static int estimate_bisect_steps(int all) +int estimate_bisect_steps(int all) { int n, x, e; -- cgit v1.2.3 From 280e65cbd83a867437cef3543d445013ecbfe29a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 19 Apr 2009 11:55:43 +0200 Subject: rev-list: refactor printing bisect vars This simplifies the code, and while at it we create the "print_commit_list" function that will be reused later. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.h | 4 ++++ builtin-rev-list.c | 61 +++++++++++++++++++++++++++++------------------------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/bisect.h b/bisect.h index cb37ddfac0..89aa6cb0b3 100644 --- a/bisect.h +++ b/bisect.h @@ -9,6 +9,10 @@ extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all); +extern void print_commit_list(struct commit_list *list, + const char *format_cur, + const char *format_last); + /* bisect_show_flags flags in struct rev_list_info */ #define BISECT_SHOW_ALL (1<<0) #define BISECT_SHOW_TRIED (1<<1) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 4c5f5eec0e..35f88ca425 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -225,20 +225,37 @@ int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } +void print_commit_list(struct commit_list *list, + const char *format_cur, + const char *format_last) +{ + for ( ; list; list = list->next) { + const char *format = list->next ? format_cur : format_last; + printf(format, sha1_to_hex(list->item->object.sha1)); + } +} + 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)); - } + print_commit_list(tried, "%s|", "%s"); printf(stringed ? "' &&\n" : "'\n"); } +static void print_var_str(const char *var, const char *val, int stringed) +{ + printf("%s='%s'%s\n", var, val, stringed ? " &&" : ""); +} + +static void print_var_int(const char *var, int val, int stringed) +{ + printf("%s=%d%s\n", var, val, stringed ? " &&" : ""); +} + int show_bisect_vars(struct rev_list_info *info, int reaches, int all) { - int cnt, flags = info->bisect_show_flags; - char hex[41] = "", *format; + int cnt, stringed, flags = info->bisect_show_flags; + char hex[41] = ""; struct commit_list *tried; struct rev_info *revs = info->revs; @@ -268,29 +285,17 @@ int show_bisect_vars(struct rev_list_info *info, int reaches, int all) printf("------\n"); } + stringed = flags & BISECT_SHOW_STRINGED; + if (flags & BISECT_SHOW_TRIED) - 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, - reaches - 1, - all, - estimate_bisect_steps(all)); + show_tried_revs(tried, stringed); + + print_var_str("bisect_rev", hex, stringed); + print_var_int("bisect_nr", cnt - 1, stringed); + print_var_int("bisect_good", all - reaches - 1, stringed); + print_var_int("bisect_bad", reaches - 1, stringed); + print_var_int("bisect_all", all, stringed); + print_var_int("bisect_steps", estimate_bisect_steps(all), 0); return 0; } -- cgit v1.2.3 From 2ace9727be2b04ce9fbc5939466da38be7fd8496 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 19 Apr 2009 11:55:57 +0200 Subject: bisect: move common bisect functionality to "bisect_common" So we can easily reuse the code in a later patch. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/bisect.c b/bisect.c index 58f7e6f773..0448eae3f8 100644 --- a/bisect.c +++ b/bisect.c @@ -532,6 +532,20 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix) revs->limited = 1; } +static void bisect_common(struct rev_info *revs, const char *prefix, + int *reaches, int *all) +{ + bisect_rev_setup(revs, prefix); + + 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); +} + int bisect_next_vars(const char *prefix) { struct rev_info revs; @@ -542,15 +556,7 @@ int bisect_next_vars(const char *prefix) info.revs = &revs; info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED; - bisect_rev_setup(&revs, prefix); - - 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); + bisect_common(&revs, prefix, &reaches, &all); return show_bisect_vars(&info, reaches, all); } -- cgit v1.2.3 From ef24c7ca055f03c673c2b67a3a7c92bcc32f62f0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 19 Apr 2009 11:56:07 +0200 Subject: bisect--helper: add "--next-exit" to output bisect results The goal of this patch is to port more shell code from the "bisect_next" function in "git-bisect.sh" to C code in "builtin-bisect--helper.c". So we port the code that interprets the bisection result and stops or continues (by checking out the next revision) the bisection process. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++ bisect.h | 1 + builtin-bisect--helper.c | 12 ++++-- 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/bisect.c b/bisect.c index 0448eae3f8..5902e83fac 100644 --- a/bisect.c +++ b/bisect.c @@ -6,6 +6,7 @@ #include "list-objects.h" #include "quote.h" #include "sha1-lookup.h" +#include "run-command.h" #include "bisect.h" static unsigned char (*skipped_sha1)[20]; @@ -16,6 +17,12 @@ static const char **rev_argv; static int rev_argv_nr; static int rev_argv_alloc; +static const unsigned char *current_bad_sha1; + +static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL}; +static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; +static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; + /* bits #0-15 in revision.h */ #define COUNTED (1u<<16) @@ -403,6 +410,7 @@ static int register_ref(const char *refname, const unsigned char *sha1, { if (!strcmp(refname, "bad")) { ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + current_bad_sha1 = sha1; rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1)); } else if (!prefixcmp(refname, "good-")) { const char *hex = sha1_to_hex(sha1); @@ -560,3 +568,100 @@ int bisect_next_vars(const char *prefix) return show_bisect_vars(&info, reaches, all); } + +static void exit_if_skipped_commits(struct commit_list *tried, + const unsigned char *bad) +{ + if (!tried) + return; + + printf("There are only 'skip'ped commits left to test.\n" + "The first bad commit could be any of:\n"); + print_commit_list(tried, "%s\n", "%s\n"); + if (bad) + printf("%s\n", sha1_to_hex(bad)); + printf("We cannot bisect more!\n"); + exit(2); +} + +static void mark_expected_rev(char *bisect_rev_hex) +{ + int len = strlen(bisect_rev_hex); + const char *filename = git_path("BISECT_EXPECTED_REV"); + int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + + if (fd < 0) + die("could not create file '%s': %s", + filename, strerror(errno)); + + bisect_rev_hex[len] = '\n'; + write_or_die(fd, bisect_rev_hex, len + 1); + bisect_rev_hex[len] = '\0'; + + if (close(fd) < 0) + die("closing file %s: %s", filename, strerror(errno)); +} + +static int bisect_checkout(char *bisect_rev_hex) +{ + int res; + + mark_expected_rev(bisect_rev_hex); + + argv_checkout[2] = bisect_rev_hex; + res = run_command_v_opt(argv_checkout, RUN_GIT_CMD); + if (res) + exit(res); + + argv_show_branch[1] = bisect_rev_hex; + return run_command_v_opt(argv_show_branch, RUN_GIT_CMD); +} + +/* + * We use the convention that exiting with an exit code 10 means that + * the bisection process finished successfully. + * In this case the calling shell script should exit 0. + */ +int bisect_next_exit(const char *prefix) +{ + struct rev_info revs; + struct commit_list *tried; + int reaches = 0, all = 0, nr; + const unsigned char *bisect_rev; + char bisect_rev_hex[41]; + + bisect_common(&revs, prefix, &reaches, &all); + + revs.commits = filter_skipped(revs.commits, &tried, 0); + + if (!revs.commits) { + /* + * We should exit here only if the "bad" + * commit is also a "skip" commit. + */ + exit_if_skipped_commits(tried, NULL); + + printf("%s was both good and bad\n", + sha1_to_hex(current_bad_sha1)); + exit(1); + } + + bisect_rev = revs.commits->item->object.sha1; + memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41); + + if (!hashcmp(bisect_rev, current_bad_sha1)) { + exit_if_skipped_commits(tried, current_bad_sha1); + printf("%s is first bad commit\n", bisect_rev_hex); + argv_diff_tree[2] = bisect_rev_hex; + run_command_v_opt(argv_diff_tree, RUN_GIT_CMD); + /* This means the bisection process succeeded. */ + exit(10); + } + + nr = all - reaches - 1; + printf("Bisecting: %d revisions left to test after this " + "(roughly %d steps)\n", nr, estimate_bisect_steps(all)); + + return bisect_checkout(bisect_rev_hex); +} + diff --git a/bisect.h b/bisect.h index 89aa6cb0b3..028eb85220 100644 --- a/bisect.h +++ b/bisect.h @@ -29,6 +29,7 @@ struct rev_list_info { extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); extern int bisect_next_vars(const char *prefix); +extern int bisect_next_exit(const char *prefix); extern int estimate_bisect_steps(int all); diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c index 8fe778766a..cb86a9a9e0 100644 --- a/builtin-bisect--helper.c +++ b/builtin-bisect--helper.c @@ -5,23 +5,29 @@ static const char * const git_bisect_helper_usage[] = { "git bisect--helper --next-vars", + "git bisect--helper --next-exit", NULL }; int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { int next_vars = 0; + int next_exit = 0; struct option options[] = { OPT_BOOLEAN(0, "next-vars", &next_vars, "output next bisect step variables"), + OPT_BOOLEAN(0, "next-exit", &next_exit, + "output bisect result and exit instuctions"), OPT_END() }; argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0); - if (!next_vars) + if ((next_vars && next_exit) || (!next_vars && !next_exit)) usage_with_options(git_bisect_helper_usage, options); - /* next-vars */ - return bisect_next_vars(prefix); + if (next_vars) + return bisect_next_vars(prefix); + else /* next-exit */ + return bisect_next_exit(prefix); } -- cgit v1.2.3 From 5a1d31c7e43a54c3a5275266745488de7208d871 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 19 Apr 2009 11:56:16 +0200 Subject: bisect: use "git bisect--helper --next-exit" in "git-bisect.sh" instead of "git bisect--helper --next-vars". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 24712ff304..e1f300bed1 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -279,19 +279,6 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -exit_if_skipped_commits () { - _tried=$1 - _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 -} - bisect_checkout() { _rev="$1" _msg="$2" @@ -416,25 +403,17 @@ bisect_next() { # Return now if a checkout has already been done test "$?" -eq "1" && return - # Get bisection information - eval=$(eval "git bisect--helper --next-vars") && - eval "$eval" || exit + # Perform bisection computation, display and checkout + git bisect--helper --next-exit + res=$? - 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" "$bad" - echo "$bisect_rev is first bad commit" - git diff-tree --pretty $bisect_rev - exit 0 - fi + # Check if we should exit because bisection is finished + test $res -eq 10 && exit 0 - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)" + # Check for an error in the bisection process + test $res -ne 0 && exit $res + + return 0 } bisect_visualize() { -- cgit v1.2.3 From c99f069de2f06b7abe0406c2ff1b46621dbc9398 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 21 Apr 2009 07:54:09 +0200 Subject: bisect--helper: remove "--next-vars" option as it is now useless Because it has been replaced by "--next-exit". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 15 --------------- bisect.h | 1 - builtin-bisect--helper.c | 12 +++--------- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/bisect.c b/bisect.c index 5902e83fac..4796aa9198 100644 --- a/bisect.c +++ b/bisect.c @@ -554,21 +554,6 @@ static void bisect_common(struct rev_info *revs, const char *prefix, !!skipped_sha1_nr); } -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; - info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED; - - bisect_common(&revs, prefix, &reaches, &all); - - return show_bisect_vars(&info, reaches, all); -} - static void exit_if_skipped_commits(struct commit_list *tried, const unsigned char *bad) { diff --git a/bisect.h b/bisect.h index 028eb85220..908e362f8b 100644 --- a/bisect.h +++ b/bisect.h @@ -28,7 +28,6 @@ struct rev_list_info { extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); -extern int bisect_next_vars(const char *prefix); extern int bisect_next_exit(const char *prefix); extern int estimate_bisect_steps(int all); diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c index cb86a9a9e0..aca7018d83 100644 --- a/builtin-bisect--helper.c +++ b/builtin-bisect--helper.c @@ -4,18 +4,14 @@ #include "bisect.h" static const char * const git_bisect_helper_usage[] = { - "git bisect--helper --next-vars", "git bisect--helper --next-exit", NULL }; int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { - int next_vars = 0; int next_exit = 0; struct option options[] = { - OPT_BOOLEAN(0, "next-vars", &next_vars, - "output next bisect step variables"), OPT_BOOLEAN(0, "next-exit", &next_exit, "output bisect result and exit instuctions"), OPT_END() @@ -23,11 +19,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0); - if ((next_vars && next_exit) || (!next_vars && !next_exit)) + if (!next_exit) usage_with_options(git_bisect_helper_usage, options); - if (next_vars) - return bisect_next_vars(prefix); - else /* next-exit */ - return bisect_next_exit(prefix); + /* next-exit */ + return bisect_next_exit(prefix); } -- cgit v1.2.3 From 38ef7507d1dce87d68f177a6209d716339fee854 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 21 Apr 2009 07:54:10 +0200 Subject: rev-list: remove stringed output flag from "show_bisect_vars" Because it was used only by "git bisect--helper --next-vars" but the "--next-vars" option has been removed. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.h | 1 - builtin-rev-list.c | 32 +++++++++++++++----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/bisect.h b/bisect.h index 908e362f8b..0b5d122a7b 100644 --- a/bisect.h +++ b/bisect.h @@ -16,7 +16,6 @@ extern void print_commit_list(struct commit_list *list, /* 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; diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 35f88ca425..31ea5f4aac 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -235,26 +235,26 @@ void print_commit_list(struct commit_list *list, } } -static void show_tried_revs(struct commit_list *tried, int stringed) +static void show_tried_revs(struct commit_list *tried) { printf("bisect_tried='"); print_commit_list(tried, "%s|", "%s"); - printf(stringed ? "' &&\n" : "'\n"); + printf("'\n"); } -static void print_var_str(const char *var, const char *val, int stringed) +static void print_var_str(const char *var, const char *val) { - printf("%s='%s'%s\n", var, val, stringed ? " &&" : ""); + printf("%s='%s'\n", var, val); } -static void print_var_int(const char *var, int val, int stringed) +static void print_var_int(const char *var, int val) { - printf("%s=%d%s\n", var, val, stringed ? " &&" : ""); + printf("%s=%d\n", var, val); } int show_bisect_vars(struct rev_list_info *info, int reaches, int all) { - int cnt, stringed, flags = info->bisect_show_flags; + int cnt, flags = info->bisect_show_flags; char hex[41] = ""; struct commit_list *tried; struct rev_info *revs = info->revs; @@ -285,17 +285,15 @@ int show_bisect_vars(struct rev_list_info *info, int reaches, int all) printf("------\n"); } - stringed = flags & BISECT_SHOW_STRINGED; - if (flags & BISECT_SHOW_TRIED) - show_tried_revs(tried, stringed); - - print_var_str("bisect_rev", hex, stringed); - print_var_int("bisect_nr", cnt - 1, stringed); - print_var_int("bisect_good", all - reaches - 1, stringed); - print_var_int("bisect_bad", reaches - 1, stringed); - print_var_int("bisect_all", all, stringed); - print_var_int("bisect_steps", estimate_bisect_steps(all), 0); + show_tried_revs(tried); + + print_var_str("bisect_rev", hex); + print_var_int("bisect_nr", cnt - 1); + print_var_int("bisect_good", all - reaches - 1); + print_var_int("bisect_bad", reaches - 1); + print_var_int("bisect_all", all); + print_var_int("bisect_steps", estimate_bisect_steps(all)); return 0; } -- cgit v1.2.3 From 503253771e93fd9f29c9f0773223d456677452c4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 25 Apr 2009 06:55:26 +0200 Subject: rev-parse: add --sq-quote to shell quote arguments Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 35 ++++++++++++++++++++++++++++++++++- builtin-rev-parse.c | 15 +++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 52c353e674..4bbdd056da 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -30,6 +30,11 @@ OPTIONS Only meaningful in `--parseopt` mode. Tells the option parser to echo out the first `--` met instead of skipping it. +--sq-quote:: + Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE + section below). In contrast to the `--sq` option below, this + mode does only quoting. Nothing else is done to command input. + --revs-only:: Do not output flags and parameters not meant for 'git-rev-list' command. @@ -64,7 +69,8 @@ OPTIONS properly quoted for consumption by shell. Useful when you expect your parameter to contain whitespaces and newlines (e.g. when using pickaxe `-S` with - 'git-diff-\*'). + 'git-diff-\*'). In contrast to the `--sq-quote` option, + the command input is still interpreted as usual. --not:: When showing object names, prefix them with '{caret}' and @@ -406,6 +412,33 @@ C? option C with an optional argument" eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?` ------------ +SQ-QUOTE +-------- + +In `--sq-quote` mode, 'git-rev-parse' echoes on the standard output a +single line suitable for `sh(1)` `eval`. This line is made by +normalizing the arguments following `--sq-quote`. Nothing other than +quoting the arguments is done. + +If you want command input to still be interpreted as usual by +'git-rev-parse' before the output is shell quoted, see the `--sq` +option. + +Example +~~~~~~~ + +------------ +$ cat >your-git-script.sh <<\EOF +#!/bin/sh +args=$(git rev-parse --sq-quote "$@") # quote user-supplied arguments +command="git frotz -n24 $args" # and use it inside a handcrafted + # command line +eval "$command" +EOF + +$ sh your-git-script.sh "a b'c" +------------ + EXAMPLES -------- diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 22c6d6ad16..c5b3d6e31b 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -402,6 +402,18 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) return 0; } +static int cmd_sq_quote(int argc, const char **argv) +{ + struct strbuf buf = STRBUF_INIT; + + if (argc) + sq_quote_argv(&buf, argv, 0); + printf("%s\n", buf.buf); + strbuf_release(&buf); + + return 0; +} + static void die_no_single_rev(int quiet) { if (quiet) @@ -419,6 +431,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (argc > 1 && !strcmp("--parseopt", argv[1])) return cmd_parseopt(argc - 1, argv + 1, prefix); + if (argc > 1 && !strcmp("--sq-quote", argv[1])) + return cmd_sq_quote(argc - 2, argv + 2); + prefix = setup_git_directory(); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { -- cgit v1.2.3 From de52f5a806f58c6b73b2e24768530213fab5a635 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 24 Apr 2009 08:29:00 +0200 Subject: bisect: use "git rev-parse --sq-quote" instead of a custom "sq" function As the "sq" function was the only place using Perl in "git-bisect.sh", this removes the Perl dependency in this script. While at it, we also remove the sed instruction in the Makefile that substituted @@PERL@@ with the Perl path in shell scripts, as this is not needed anymore. (It is now only needed in "git-instaweb.sh" but this command is dealt with separately in the Makefile.) Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Makefile | 1 - git-bisect.sh | 16 +++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 6e216436c3..274d97c836 100644 --- a/Makefile +++ b/Makefile @@ -1248,7 +1248,6 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(QUIET_GEN)$(RM) $@ $@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ - -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ $@.sh >$@+ && \ diff --git a/git-bisect.sh b/git-bisect.sh index e1f300bed1..786b7b9110 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -33,16 +33,6 @@ require_work_tree _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" -sq() { - @@PERL@@ -e ' - for (@ARGV) { - s/'\''/'\'\\\\\'\''/g; - print " '\''$_'\''"; - } - print "\n"; - ' "$@" -} - bisect_autostart() { test -s "$GIT_DIR/BISECT_START" || { echo >&2 'You need to start by "git bisect start"' @@ -107,7 +97,7 @@ bisect_start() { for arg; do case "$arg" in --) has_double_dash=1; break ;; esac done - orig_args=$(sq "$@") + orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' while [ $# -gt 0 ]; do @@ -147,7 +137,7 @@ bisect_start() { # Write new start state. # echo "$start_head" >"$GIT_DIR/BISECT_START" && - sq "$@" >"$GIT_DIR/BISECT_NAMES" && + git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && eval "$eval" && echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit # @@ -199,7 +189,7 @@ bisect_skip() { *..*) revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;; *) - revs=$(sq "$arg") ;; + revs=$(git rev-parse --sq-quote "$arg") ;; esac all="$all $revs" done -- cgit v1.2.3 From 47c9739e5e02484de618ca70a592ecd25c05fd1a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 24 Apr 2009 08:29:01 +0200 Subject: am: simplify "sq" function by using "git rev-parse --sq-quote" Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-am.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/git-am.sh b/git-am.sh index 6d1848b6cc..578780be13 100755 --- a/git-am.sh +++ b/git-am.sh @@ -44,11 +44,7 @@ else fi sq () { - for sqarg - do - printf "%s" "$sqarg" | - sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/' - done + git rev-parse --sq-quote "$@" } stop_here () { -- cgit v1.2.3 From 6212b1aae9dcc0de1dca1553298c6668579dd184 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:38 +0200 Subject: bisect: use "sha1_array" to store skipped revisions This patch creates a "struct sha1_array" to store skipped revisions, so that the same struct can be reused in a later patch for good revisions. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/bisect.c b/bisect.c index 4796aa9198..12df855cfa 100644 --- a/bisect.c +++ b/bisect.c @@ -9,9 +9,13 @@ #include "run-command.h" #include "bisect.h" -static unsigned char (*skipped_sha1)[20]; -static int skipped_sha1_nr; -static int skipped_sha1_alloc; +struct sha1_array { + unsigned char (*sha1)[20]; + int sha1_nr; + int sha1_alloc; +}; + +static struct sha1_array skipped_revs; static const char **rev_argv; static int rev_argv_nr; @@ -420,9 +424,9 @@ static int register_ref(const char *refname, const unsigned char *sha1, 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); + ALLOC_GROW(skipped_revs.sha1, skipped_revs.sha1_nr + 1, + skipped_revs.sha1_alloc); + hashcpy(skipped_revs.sha1[skipped_revs.sha1_nr++], sha1); } return 0; @@ -466,7 +470,8 @@ static int skipcmp(const void *a, const void *b) static void prepare_skipped(void) { - qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp); + qsort(skipped_revs.sha1, skipped_revs.sha1_nr, + sizeof(*skipped_revs.sha1), skipcmp); } static const unsigned char *skipped_sha1_access(size_t index, void *table) @@ -477,7 +482,7 @@ static const unsigned char *skipped_sha1_access(size_t index, void *table) static int lookup_skipped(unsigned char *sha1) { - return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr, + return sha1_pos(sha1, skipped_revs.sha1, skipped_revs.sha1_nr, skipped_sha1_access); } @@ -489,7 +494,7 @@ struct commit_list *filter_skipped(struct commit_list *list, *tried = NULL; - if (!skipped_sha1_nr) + if (!skipped_revs.sha1_nr) return list; prepare_skipped(); @@ -551,7 +556,7 @@ static void bisect_common(struct rev_info *revs, const char *prefix, mark_edges_uninteresting(revs->commits, revs, NULL); revs->commits = find_bisection(revs->commits, reaches, all, - !!skipped_sha1_nr); + !!skipped_revs.sha1_nr); } static void exit_if_skipped_commits(struct commit_list *tried, -- cgit v1.2.3 From 3755ccdb65a3bcaa1d55a6b4ec9c47eab0501baa Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:39 +0200 Subject: bisect: implement "rev_argv_push" to fill an argv with revs This patch is a minor clean up right now, but the new function will evolve and be used more later. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/bisect.c b/bisect.c index 12df855cfa..f99637d9cf 100644 --- a/bisect.c +++ b/bisect.c @@ -409,20 +409,23 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } +static void rev_argv_push(const unsigned char *sha1, const char *format) +{ + struct strbuf buf = STRBUF_INIT; + + strbuf_addf(&buf, format, sha1_to_hex(sha1)); + ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); + rev_argv[rev_argv_nr++] = strbuf_detach(&buf, NULL); +} + 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); current_bad_sha1 = sha1; - rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1)); + rev_argv_push(sha1, "%s"); } 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; + rev_argv_push(sha1, "^%s"); } else if (!prefixcmp(refname, "skip-")) { ALLOC_GROW(skipped_revs.sha1, skipped_revs.sha1_nr + 1, skipped_revs.sha1_alloc); -- cgit v1.2.3 From fad2d31d62277eced9aa7fc187ae3b97e72895bb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:40 +0200 Subject: bisect: store good revisions in a "sha1_array" This will make it easier to use good revisions for checking merge bases later. To simplify the code, a new "sha1_array_push" function is also introduced. And while at it we move the earlier part of the code to fill the argv that is passed to "setup_revisions", so that all this code is now completely after "read_bisect_refs". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/bisect.c b/bisect.c index f99637d9cf..7976cbfcd7 100644 --- a/bisect.c +++ b/bisect.c @@ -15,6 +15,7 @@ struct sha1_array { int sha1_alloc; }; +static struct sha1_array good_revs; static struct sha1_array skipped_revs; static const char **rev_argv; @@ -418,18 +419,22 @@ static void rev_argv_push(const unsigned char *sha1, const char *format) rev_argv[rev_argv_nr++] = strbuf_detach(&buf, NULL); } +static void sha1_array_push(struct sha1_array *array, + const unsigned char *sha1) +{ + ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc); + hashcpy(array->sha1[array->sha1_nr++], sha1); +} + static int register_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { if (!strcmp(refname, "bad")) { current_bad_sha1 = sha1; - rev_argv_push(sha1, "%s"); } else if (!prefixcmp(refname, "good-")) { - rev_argv_push(sha1, "^%s"); + sha1_array_push(&good_revs, sha1); } else if (!prefixcmp(refname, "skip-")) { - ALLOC_GROW(skipped_revs.sha1, skipped_revs.sha1_nr + 1, - skipped_revs.sha1_alloc); - hashcpy(skipped_revs.sha1[skipped_revs.sha1_nr++], sha1); + sha1_array_push(&skipped_revs, sha1); } return 0; @@ -524,16 +529,23 @@ struct commit_list *filter_skipped(struct commit_list *list, static void bisect_rev_setup(struct rev_info *revs, const char *prefix) { + int i; + init_revisions(revs, prefix); revs->abbrev = 0; revs->commit_format = CMIT_FMT_UNSPECIFIED; + if (read_bisect_refs()) + die("reading bisect refs failed"); + /* 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"); + rev_argv_push(current_bad_sha1, "%s"); + + for (i = 0; i < good_revs.sha1_nr; i++) + rev_argv_push(good_revs.sha1[i], "^%s"); ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); rev_argv[rev_argv_nr++] = xstrdup("--"); -- cgit v1.2.3 From 1c953a1f46340a9ca0fc84345e826c9372dee42a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:41 +0200 Subject: bisect: use new "struct argv_array" to prepare argv for "setup_revisions" Because we will use other instances of this struct. The "rev_argv_push" function is changed into 2 functions "argv_array_push" and "argv_array_push_sha1" that take a "struct argv_array *" as first argument. And these functions are used to simplify "bisect_rev_setup". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 57 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/bisect.c b/bisect.c index 7976cbfcd7..8e34186f95 100644 --- a/bisect.c +++ b/bisect.c @@ -18,12 +18,16 @@ struct sha1_array { static struct sha1_array good_revs; static struct sha1_array skipped_revs; -static const char **rev_argv; -static int rev_argv_nr; -static int rev_argv_alloc; - static const unsigned char *current_bad_sha1; +struct argv_array { + const char **argv; + int argv_nr; + int argv_alloc; +}; + +struct argv_array rev_argv; + static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL}; static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; @@ -410,13 +414,19 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } -static void rev_argv_push(const unsigned char *sha1, const char *format) +static void argv_array_push(struct argv_array *array, const char *string) { - struct strbuf buf = STRBUF_INIT; + ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc); + array->argv[array->argv_nr++] = string; +} +static void argv_array_push_sha1(struct argv_array *array, + const unsigned char *sha1, + const char *format) +{ + struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, format, sha1_to_hex(sha1)); - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = strbuf_detach(&buf, NULL); + argv_array_push(array, strbuf_detach(&buf, NULL)); } static void sha1_array_push(struct sha1_array *array, @@ -445,7 +455,7 @@ static int read_bisect_refs(void) return for_each_ref_in("refs/bisect/", register_ref, NULL); } -void read_bisect_paths(void) +void read_bisect_paths(struct argv_array *array) { struct strbuf str = STRBUF_INIT; const char *filename = git_path("BISECT_NAMES"); @@ -460,8 +470,8 @@ void read_bisect_paths(void) strbuf_trim(&str); quoted = strbuf_detach(&str, NULL); - res = sq_dequote_to_argv(quoted, &rev_argv, - &rev_argv_nr, &rev_argv_alloc); + res = sq_dequote_to_argv(quoted, &array->argv, + &array->argv_nr, &array->argv_alloc); if (res) die("Badly quoted content in file '%s': %s", filename, quoted); @@ -538,25 +548,16 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix) if (read_bisect_refs()) die("reading bisect refs failed"); - /* 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"); - - rev_argv_push(current_bad_sha1, "%s"); - + /* rev_argv.argv[0] will be ignored by setup_revisions */ + argv_array_push(&rev_argv, xstrdup("bisect_rev_setup")); + argv_array_push_sha1(&rev_argv, current_bad_sha1, "%s"); for (i = 0; i < good_revs.sha1_nr; i++) - rev_argv_push(good_revs.sha1[i], "^%s"); - - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = xstrdup("--"); - - 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); + argv_array_push_sha1(&rev_argv, good_revs.sha1[i], "^%s"); + argv_array_push(&rev_argv, xstrdup("--")); + read_bisect_paths(&rev_argv); + argv_array_push(&rev_argv, NULL); + setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL); revs->limited = 1; } -- cgit v1.2.3 From 2b020695e4db8dbaee7997090aec08760903291b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:42 +0200 Subject: bisect: remove too much function nesting This patch moves some function calls into "bisect_next_exit" so that functions are nesting less. The call to "bisect_rev_setup" is moved from "bisect_common" into "bisect_next_exit" and the call to "read_bisect_refs" from "bisect_rev_setup" into "bisect_next_exit". While at it, "rev_argv" is moved into "bisect_rev_setup". This will make it easier and cleaner to implement checking merge bases. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bisect.c b/bisect.c index 8e34186f95..9e01b9e0ef 100644 --- a/bisect.c +++ b/bisect.c @@ -26,8 +26,6 @@ struct argv_array { int argv_alloc; }; -struct argv_array rev_argv; - static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL}; static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; @@ -539,15 +537,13 @@ struct commit_list *filter_skipped(struct commit_list *list, static void bisect_rev_setup(struct rev_info *revs, const char *prefix) { + struct argv_array rev_argv = { NULL, 0, 0 }; int i; init_revisions(revs, prefix); revs->abbrev = 0; revs->commit_format = CMIT_FMT_UNSPECIFIED; - if (read_bisect_refs()) - die("reading bisect refs failed"); - /* rev_argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&rev_argv, xstrdup("bisect_rev_setup")); argv_array_push_sha1(&rev_argv, current_bad_sha1, "%s"); @@ -561,11 +557,8 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix) revs->limited = 1; } -static void bisect_common(struct rev_info *revs, const char *prefix, - int *reaches, int *all) +static void bisect_common(struct rev_info *revs, int *reaches, int *all) { - bisect_rev_setup(revs, prefix); - if (prepare_revision_walk(revs)) die("revision walk setup failed"); if (revs->tree_objects) @@ -636,7 +629,12 @@ int bisect_next_exit(const char *prefix) const unsigned char *bisect_rev; char bisect_rev_hex[41]; - bisect_common(&revs, prefix, &reaches, &all); + if (read_bisect_refs()) + die("reading bisect refs failed"); + + bisect_rev_setup(&revs, prefix); + + bisect_common(&revs, &reaches, &all); revs.commits = filter_skipped(revs.commits, &tried, 0); -- cgit v1.2.3 From aaaff9e2d29a68c23a416bcc5f3f382504770bf8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:43 +0200 Subject: bisect: make skipped array functions more generic So they can be used on the good array too. This is done by renaming many functions and some variables to remove "skip" in the name, and by adding a "struct sha1_array *array" argument where needed. While at it, make the second argument to "lookup_sha1_array" const. It becomes "const unsigned char *sha1". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bisect.c b/bisect.c index 9e01b9e0ef..77edecaaec 100644 --- a/bisect.c +++ b/bisect.c @@ -479,27 +479,26 @@ void read_bisect_paths(struct argv_array *array) fclose(fp); } -static int skipcmp(const void *a, const void *b) +static int array_cmp(const void *a, const void *b) { return hashcmp(a, b); } -static void prepare_skipped(void) +static void sort_sha1_array(struct sha1_array *array) { - qsort(skipped_revs.sha1, skipped_revs.sha1_nr, - sizeof(*skipped_revs.sha1), skipcmp); + qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp); } -static const unsigned char *skipped_sha1_access(size_t index, void *table) +static const unsigned char *sha1_access(size_t index, void *table) { - unsigned char (*skipped)[20] = table; - return skipped[index]; + unsigned char (*array)[20] = table; + return array[index]; } -static int lookup_skipped(unsigned char *sha1) +static int lookup_sha1_array(struct sha1_array *array, + const unsigned char *sha1) { - return sha1_pos(sha1, skipped_revs.sha1, skipped_revs.sha1_nr, - skipped_sha1_access); + return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access); } struct commit_list *filter_skipped(struct commit_list *list, @@ -513,12 +512,13 @@ struct commit_list *filter_skipped(struct commit_list *list, if (!skipped_revs.sha1_nr) return list; - prepare_skipped(); + sort_sha1_array(&skipped_revs); while (list) { struct commit_list *next = list->next; list->next = NULL; - if (0 <= lookup_skipped(list->item->object.sha1)) { + if (0 <= lookup_sha1_array(&skipped_revs, + list->item->object.sha1)) { /* Move current to tried list */ *tried = list; tried = &list->next; -- cgit v1.2.3 From 1da8c4fc2c85ed99e23d640bddedd8119150a1b8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:44 +0200 Subject: bisect: automatically sort sha1_array if needed when looking it up This makes sha1_array easier to use, so later patches will be simpler. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bisect.c b/bisect.c index 77edecaaec..d2a34d1e51 100644 --- a/bisect.c +++ b/bisect.c @@ -13,6 +13,7 @@ struct sha1_array { unsigned char (*sha1)[20]; int sha1_nr; int sha1_alloc; + int sorted; }; static struct sha1_array good_revs; @@ -487,6 +488,8 @@ static int array_cmp(const void *a, const void *b) static void sort_sha1_array(struct sha1_array *array) { qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp); + + array->sorted = 1; } static const unsigned char *sha1_access(size_t index, void *table) @@ -498,6 +501,9 @@ static const unsigned char *sha1_access(size_t index, void *table) static int lookup_sha1_array(struct sha1_array *array, const unsigned char *sha1) { + if (!array->sorted) + sort_sha1_array(array); + return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access); } @@ -512,8 +518,6 @@ struct commit_list *filter_skipped(struct commit_list *list, if (!skipped_revs.sha1_nr) return list; - sort_sha1_array(&skipped_revs); - while (list) { struct commit_list *next = list->next; list->next = NULL; -- cgit v1.2.3 From c0537662806f4b3d3ba883d05a48fdf9120893f0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:45 +0200 Subject: bisect: implement the "check_merge_bases" function And all functions needed to make it work. This is a port from the shell function with the same name "git-bisect.sh". This function is not used yet but it will be used later. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/bisect.c b/bisect.c index d2a34d1e51..b24ee78ba9 100644 --- a/bisect.c +++ b/bisect.c @@ -507,6 +507,20 @@ static int lookup_sha1_array(struct sha1_array *array, return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access); } +static char *join_sha1_array_hex(struct sha1_array *array, char delim) +{ + struct strbuf joined_hexs = STRBUF_INIT; + int i; + + for (i = 0; i < array->sha1_nr; i++) { + strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i])); + if (i + 1 < array->sha1_nr) + strbuf_addch(&joined_hexs, delim); + } + + return strbuf_detach(&joined_hexs, NULL); +} + struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all) @@ -587,6 +601,30 @@ static void exit_if_skipped_commits(struct commit_list *tried, exit(2); } +static int is_expected_rev(const unsigned char *sha1) +{ + const char *filename = git_path("BISECT_EXPECTED_REV"); + struct stat st; + struct strbuf str = STRBUF_INIT; + FILE *fp; + int res = 0; + + if (stat(filename, &st) || !S_ISREG(st.st_mode)) + return 0; + + fp = fopen(filename, "r"); + if (!fp) + return 0; + + if (strbuf_getline(&str, fp, '\n') != EOF) + res = !strcmp(str.buf, sha1_to_hex(sha1)); + + strbuf_release(&str); + fclose(fp); + + return res; +} + static void mark_expected_rev(char *bisect_rev_hex) { int len = strlen(bisect_rev_hex); @@ -620,6 +658,98 @@ static int bisect_checkout(char *bisect_rev_hex) return run_command_v_opt(argv_show_branch, RUN_GIT_CMD); } +static struct commit *get_commit_reference(const unsigned char *sha1) +{ + struct commit *r = lookup_commit_reference(sha1); + if (!r) + die("Not a valid commit name %s", sha1_to_hex(sha1)); + return r; +} + +static struct commit **get_bad_and_good_commits(int *rev_nr) +{ + int len = 1 + good_revs.sha1_nr; + struct commit **rev = xmalloc(len * sizeof(*rev)); + int i, n = 0; + + rev[n++] = get_commit_reference(current_bad_sha1); + for (i = 0; i < good_revs.sha1_nr; i++) + rev[n++] = get_commit_reference(good_revs.sha1[i]); + *rev_nr = n; + + return rev; +} + +static void handle_bad_merge_base(void) +{ + if (is_expected_rev(current_bad_sha1)) { + char *bad_hex = sha1_to_hex(current_bad_sha1); + char *good_hex = join_sha1_array_hex(&good_revs, ' '); + + fprintf(stderr, "The merge base %s is bad.\n" + "This means the bug has been fixed " + "between %s and [%s].\n", + bad_hex, bad_hex, good_hex); + + exit(3); + } + + fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n" + "git bisect cannot work properly in this case.\n" + "Maybe you mistake good and bad revs?\n"); + exit(1); +} + +void handle_skipped_merge_base(const unsigned char *mb) +{ + char *mb_hex = sha1_to_hex(mb); + char *bad_hex = sha1_to_hex(current_bad_sha1); + char *good_hex = join_sha1_array_hex(&good_revs, ' '); + + fprintf(stderr, "Warning: the merge base between %s and [%s] " + "must be skipped.\n" + "So we cannot be sure the first bad commit is " + "between %s and %s.\n" + "We continue anyway.\n", + bad_hex, good_hex, mb_hex, bad_hex); + free(good_hex); +} + +/* + * "check_merge_bases" checks that merge bases are not "bad". + * + * - If one is "bad", it means the user assumed something wrong + * and we must exit with a non 0 error code. + * - If one is "good", that's good, we have nothing to do. + * - If one is "skipped", we can't know but we should warn. + * - If we don't know, we should check it out and ask the user to test. + */ +static void check_merge_bases(void) +{ + struct commit_list *result; + int rev_nr; + struct commit **rev = get_bad_and_good_commits(&rev_nr); + + result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); + + for (; result; result = result->next) { + const unsigned char *mb = result->item->object.sha1; + if (!hashcmp(mb, current_bad_sha1)) { + handle_bad_merge_base(); + } else if (0 <= lookup_sha1_array(&good_revs, mb)) { + continue; + } else if (0 <= lookup_sha1_array(&skipped_revs, mb)) { + handle_skipped_merge_base(mb); + } else { + printf("Bisecting: a merge base must be tested\n"); + exit(bisect_checkout(sha1_to_hex(mb))); + } + } + + free(rev); + free_commit_list(result); +} + /* * We use the convention that exiting with an exit code 10 means that * the bisection process finished successfully. -- cgit v1.2.3 From d937d4aca1f10b9202b620a89dc6a5972e9605eb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:46 +0200 Subject: bisect: add "check_good_are_ancestors_of_bad" function This is a port of the function with the same name that is in "git-bisect.sh". The new function is not used yet but will be in a later patch. We also implement an helper "check_ancestors" function that use "start_command" and "finish_command" to launch "git rev-list $good ^$bad". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/bisect.c b/bisect.c index b24ee78ba9..09102daba8 100644 --- a/bisect.c +++ b/bisect.c @@ -750,6 +750,81 @@ static void check_merge_bases(void) free_commit_list(result); } +/* + * This function runs the command "git rev-list $_good ^$_bad" + * and returns 1 if it produces some output, 0 otherwise. + */ +static int check_ancestors(void) +{ + struct argv_array rev_argv = { NULL, 0, 0 }; + struct strbuf str = STRBUF_INIT; + int i, result = 0; + struct child_process rls; + FILE *rls_fout; + + argv_array_push(&rev_argv, xstrdup("rev-list")); + argv_array_push_sha1(&rev_argv, current_bad_sha1, "^%s"); + for (i = 0; i < good_revs.sha1_nr; i++) + argv_array_push_sha1(&rev_argv, good_revs.sha1[i], "%s"); + argv_array_push(&rev_argv, NULL); + + memset(&rls, 0, sizeof(rls)); + rls.argv = rev_argv.argv; + rls.out = -1; + rls.git_cmd = 1; + if (start_command(&rls)) + die("Could not launch 'git rev-list' command."); + rls_fout = fdopen(rls.out, "r"); + while (strbuf_getline(&str, rls_fout, '\n') != EOF) { + strbuf_trim(&str); + if (*str.buf) { + result = 1; + break; + } + } + fclose(rls_fout); + finish_command(&rls); + + return result; +} + +/* + * "check_good_are_ancestors_of_bad" checks that all "good" revs are + * ancestor of the "bad" rev. + * + * If that's not the case, we need to check the merge bases. + * If a merge base must be tested by the user, its source code will be + * checked out to be tested by the user and we will exit. + */ +static void check_good_are_ancestors_of_bad(const char *prefix) +{ + const char *filename = git_path("BISECT_ANCESTORS_OK"); + struct stat st; + int fd; + + if (!current_bad_sha1) + die("a bad revision is needed"); + + /* Check if file BISECT_ANCESTORS_OK exists. */ + if (!stat(filename, &st) && S_ISREG(st.st_mode)) + return; + + /* Bisecting with no good rev is ok. */ + if (good_revs.sha1_nr == 0) + return; + + if (check_ancestors()) + check_merge_bases(); + + /* Create file BISECT_ANCESTORS_OK. */ + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) + warning("could not create file '%s': %s", + filename, strerror(errno)); + else + close(fd); +} + /* * We use the convention that exiting with an exit code 10 means that * the bisection process finished successfully. -- cgit v1.2.3 From 0871984d304c9201b85897743eae583cd165106d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 9 May 2009 17:55:47 +0200 Subject: bisect: make "git bisect" use new "--next-all" bisect-helper function This patch replace the "--next-exit" option of "git bisect--helper" with a "--next-all" option that does merge base checking using the "check_good_are_ancestors_of_bad" function implemented in "bisect.c" in a former patch. The new "--next-all" option is then used in "git-bisect.sh" instead of the "--next-exit" option, and all the shell functions in "git-bisect.sh" that are now unused are removed. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- bisect.c | 4 +- bisect.h | 2 +- builtin-bisect--helper.c | 14 +++--- git-bisect.sh | 127 +---------------------------------------------- 4 files changed, 13 insertions(+), 134 deletions(-) diff --git a/bisect.c b/bisect.c index 09102daba8..f57b62cddd 100644 --- a/bisect.c +++ b/bisect.c @@ -830,7 +830,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix) * the bisection process finished successfully. * In this case the calling shell script should exit 0. */ -int bisect_next_exit(const char *prefix) +int bisect_next_all(const char *prefix) { struct rev_info revs; struct commit_list *tried; @@ -841,6 +841,8 @@ int bisect_next_exit(const char *prefix) if (read_bisect_refs()) die("reading bisect refs failed"); + check_good_are_ancestors_of_bad(prefix); + bisect_rev_setup(&revs, prefix); bisect_common(&revs, &reaches, &all); diff --git a/bisect.h b/bisect.h index 0b5d122a7b..fb744fdb79 100644 --- a/bisect.h +++ b/bisect.h @@ -27,7 +27,7 @@ struct rev_list_info { extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); -extern int bisect_next_exit(const char *prefix); +extern int bisect_next_all(const char *prefix); extern int estimate_bisect_steps(int all); diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c index aca7018d83..cb3e155116 100644 --- a/builtin-bisect--helper.c +++ b/builtin-bisect--helper.c @@ -4,24 +4,24 @@ #include "bisect.h" static const char * const git_bisect_helper_usage[] = { - "git bisect--helper --next-exit", + "git bisect--helper --next-all", NULL }; int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { - int next_exit = 0; + int next_all = 0; struct option options[] = { - OPT_BOOLEAN(0, "next-exit", &next_exit, - "output bisect result and exit instuctions"), + OPT_BOOLEAN(0, "next-all", &next_all, + "perform 'git bisect next'"), OPT_END() }; argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0); - if (!next_exit) + if (!next_all) usage_with_options(git_bisect_helper_usage, options); - /* next-exit */ - return bisect_next_exit(prefix); + /* next-all */ + return bisect_next_all(prefix); } diff --git a/git-bisect.sh b/git-bisect.sh index 786b7b9110..8969553658 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -167,10 +167,6 @@ is_expected_rev() { test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV") } -mark_expected_rev() { - echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV" -} - check_expected_revs() { for _rev in "$@"; do if ! is_expected_rev "$_rev"; then @@ -269,132 +265,13 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -bisect_checkout() { - _rev="$1" - _msg="$2" - echo "Bisecting: $_msg" - mark_expected_rev "$_rev" - git checkout -q "$_rev" -- || exit - git show-branch "$_rev" -} - -is_among() { - _rev="$1" - _list="$2" - case "$_list" in *$_rev*) return 0 ;; esac - return 1 -} - -handle_bad_merge_base() { - _badmb="$1" - _good="$2" - if is_expected_rev "$_badmb"; then - cat >&2 <&2 <&2 < "$GIT_DIR/BISECT_ANCESTORS_OK" - - return 0 -} - bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart bisect_next_check good - # Get bad, good and skipped revs - bad=$(git rev-parse --verify refs/bisect/bad) && - good=$(git for-each-ref --format='^%(objectname)' \ - "refs/bisect/good-*" | tr '\012' ' ') && - skip=$(git for-each-ref --format='%(objectname)' \ - "refs/bisect/skip-*" | tr '\012' ' ') || exit - - # Maybe some merge bases must be tested first - check_good_are_ancestors_of_bad "$bad" "$good" "$skip" - # Return now if a checkout has already been done - test "$?" -eq "1" && return - - # Perform bisection computation, display and checkout - git bisect--helper --next-exit + # Perform all bisection computation, display and checkout + git bisect--helper --next-all res=$? # Check if we should exit because bisection is finished -- cgit v1.2.3