From fe4a0a288842e225f99254b3e6ce14ff98875501 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 1 Sep 2012 07:25:27 -0400 Subject: [PATCH 1/4] argv-array: add pop function Sometimes we build a set of similar command lines, differing only in the final arguments (e.g., "fetch --multiple"). To use argv_array for this, you have to either push the same set of elements repeatedly, or break the abstraction by manually manipulating the array's internal members. Instead, let's provide a sanctioned "pop" function to remove elements from the end. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/technical/api-argv-array.txt | 4 ++++ argv-array.c | 9 +++++++++ argv-array.h | 1 + 3 files changed, 14 insertions(+) diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt index 1b7d8f140c..1a797812fb 100644 --- a/Documentation/technical/api-argv-array.txt +++ b/Documentation/technical/api-argv-array.txt @@ -46,6 +46,10 @@ Functions Format a string and push it onto the end of the array. This is a convenience wrapper combining `strbuf_addf` and `argv_array_push`. +`argv_array_pop`:: + Remove the final element from the array. If there are no + elements in the array, do nothing. + `argv_array_clear`:: Free all memory associated with the array and return it to the initial, empty state. diff --git a/argv-array.c b/argv-array.c index 0b5f8898a1..55e8443ff9 100644 --- a/argv-array.c +++ b/argv-array.c @@ -49,6 +49,15 @@ void argv_array_pushl(struct argv_array *array, ...) va_end(ap); } +void argv_array_pop(struct argv_array *array) +{ + if (!array->argc) + return; + free((char *)array->argv[array->argc - 1]); + array->argv[array->argc - 1] = NULL; + array->argc--; +} + void argv_array_clear(struct argv_array *array) { if (array->argv != empty_argv) { diff --git a/argv-array.h b/argv-array.h index b93a69c36c..f4b98660f8 100644 --- a/argv-array.h +++ b/argv-array.h @@ -16,6 +16,7 @@ void argv_array_push(struct argv_array *, const char *); __attribute__((format (printf,2,3))) void argv_array_pushf(struct argv_array *, const char *fmt, ...); void argv_array_pushl(struct argv_array *, ...); +void argv_array_pop(struct argv_array *); void argv_array_clear(struct argv_array *); #endif /* ARGV_ARRAY_H */ From ba4d1c7b1623b2c7ec198aee08036acf779375e6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 1 Sep 2012 07:34:09 -0400 Subject: [PATCH 2/4] argv-array: fix bogus cast when freeing array Since the array struct stores a "const char **" argv member (for compatibility with most of our argv-taking functions), we have to cast away the const-ness when freeing its elements. However, we used the wrong type when doing so. It doesn't make a difference since free() take a void pointer anyway, but it can be slightly confusing to a reader. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- argv-array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/argv-array.c b/argv-array.c index 55e8443ff9..256741d226 100644 --- a/argv-array.c +++ b/argv-array.c @@ -63,7 +63,7 @@ void argv_array_clear(struct argv_array *array) if (array->argv != empty_argv) { int i; for (i = 0; i < array->argc; i++) - free((char **)array->argv[i]); + free((char *)array->argv[i]); free(array->argv); } argv_array_init(array); From 85556d4e37db4c8ed88c68a602f6f85054996bea Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 1 Sep 2012 07:27:35 -0400 Subject: [PATCH 3/4] fetch: use argv_array instead of hand-building arrays Fetch invokes itself recursively when recursing into submodules or handling "fetch --multiple". In both cases, it builds the child's command line by pushing options onto a statically-sized array. In both cases, the array is currently just big enough to handle the largest possible case. However, this technique is brittle and error-prone, so let's replace it with a dynamic argv_array. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/fetch.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 1c8cb62445..d5442e1eb7 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -14,6 +14,7 @@ #include "transport.h" #include "submodule.h" #include "connected.h" +#include "argv-array.h" static const char * const builtin_fetch_usage[] = { "git fetch [] [ [...]]", @@ -841,38 +842,35 @@ static int add_remote_or_group(const char *name, struct string_list *list) return 1; } -static void add_options_to_argv(int *argc, const char **argv) +static void add_options_to_argv(struct argv_array *argv) { if (dry_run) - argv[(*argc)++] = "--dry-run"; + argv_array_push(argv, "--dry-run"); if (prune) - argv[(*argc)++] = "--prune"; + argv_array_push(argv, "--prune"); if (update_head_ok) - argv[(*argc)++] = "--update-head-ok"; + argv_array_push(argv, "--update-head-ok"); if (force) - argv[(*argc)++] = "--force"; + argv_array_push(argv, "--force"); if (keep) - argv[(*argc)++] = "--keep"; + argv_array_push(argv, "--keep"); if (recurse_submodules == RECURSE_SUBMODULES_ON) - argv[(*argc)++] = "--recurse-submodules"; + argv_array_push(argv, "--recurse-submodules"); else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) - argv[(*argc)++] = "--recurse-submodules=on-demand"; + argv_array_push(argv, "--recurse-submodules=on-demand"); if (verbosity >= 2) - argv[(*argc)++] = "-v"; + argv_array_push(argv, "-v"); if (verbosity >= 1) - argv[(*argc)++] = "-v"; + argv_array_push(argv, "-v"); else if (verbosity < 0) - argv[(*argc)++] = "-q"; + argv_array_push(argv, "-q"); } static int fetch_multiple(struct string_list *list) { int i, result = 0; - const char *argv[12] = { "fetch", "--append" }; - int argc = 2; - - add_options_to_argv(&argc, argv); + struct argv_array argv = ARGV_ARRAY_INIT; if (!append && !dry_run) { int errcode = truncate_fetch_head(); @@ -880,18 +878,22 @@ static int fetch_multiple(struct string_list *list) return errcode; } + argv_array_pushl(&argv, "fetch", "--append", NULL); + add_options_to_argv(&argv); + for (i = 0; i < list->nr; i++) { const char *name = list->items[i].string; - argv[argc] = name; - argv[argc + 1] = NULL; + argv_array_push(&argv, name); if (verbosity >= 0) printf(_("Fetching %s\n"), name); - if (run_command_v_opt(argv, RUN_GIT_CMD)) { + if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) { error(_("Could not fetch %s"), name); result = 1; } + argv_array_pop(&argv); } + argv_array_clear(&argv); return result; } @@ -1007,13 +1009,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { - const char *options[10]; - int num_options = 0; - add_options_to_argv(&num_options, options); - result = fetch_populated_submodules(num_options, options, + struct argv_array options = ARGV_ARRAY_INIT; + + add_options_to_argv(&options); + result = fetch_populated_submodules(options.argc, options.argv, submodule_prefix, recurse_submodules, verbosity < 0); + argv_array_clear(&options); } /* All names were strdup()ed or strndup()ed */ From 50d89ad6542c8acafefa6d42f8b42dfa9b8fafe1 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Sat, 1 Sep 2012 17:27:06 +0200 Subject: [PATCH 4/4] submodule: use argv_array instead of hand-building arrays fetch_populated_submodules() allocates the full argv array it uses to recurse into the submodules from the number of given options plus the six argv values it is going to add. It then initializes it with those values which won't change during the iteration and copies the given options into it. Inside the loop the two argv values different for each submodule get replaced with those currently valid. However, this technique is brittle and error-prone (as the comment to explain the magic number 6 indicates), so let's replace it with an argv_array. Instead of replacing the argv values, push them to the argv_array just before the run_command() call (including the option separating them) and pop them from the argv_array right after that. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- builtin/fetch.c | 2 +- submodule.c | 31 ++++++++++++++++--------------- submodule.h | 3 ++- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index d5442e1eb7..6196e91798 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1012,7 +1012,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct argv_array options = ARGV_ARRAY_INIT; add_options_to_argv(&options); - result = fetch_populated_submodules(options.argc, options.argv, + result = fetch_populated_submodules(&options, submodule_prefix, recurse_submodules, verbosity < 0); diff --git a/submodule.c b/submodule.c index 784b58039d..83e157d8d5 100644 --- a/submodule.c +++ b/submodule.c @@ -586,13 +586,13 @@ static void calculate_changed_submodule_paths(void) initialized_fetch_ref_tips = 0; } -int fetch_populated_submodules(int num_options, const char **options, +int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, int quiet) { - int i, result = 0, argc = 0, default_argc; + int i, result = 0; struct child_process cp; - const char **argv; + struct argv_array argv = ARGV_ARRAY_INIT; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); if (!work_tree) @@ -602,17 +602,13 @@ int fetch_populated_submodules(int num_options, const char **options, if (read_cache() < 0) die("index file corrupt"); - /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */ - argv = xcalloc(num_options + 6, sizeof(const char *)); - argv[argc++] = "fetch"; - for (i = 0; i < num_options; i++) - argv[argc++] = options[i]; - argv[argc++] = "--recurse-submodules-default"; - default_argc = argc++; - argv[argc++] = "--submodule-prefix"; + argv_array_push(&argv, "fetch"); + for (i = 0; i < options->argc; i++) + argv_array_push(&argv, options->argv[i]); + argv_array_push(&argv, "--recurse-submodules-default"); + /* default value, "--submodule-prefix" and its value are added later */ memset(&cp, 0, sizeof(cp)); - cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; @@ -672,16 +668,21 @@ int fetch_populated_submodules(int num_options, const char **options, if (!quiet) printf("Fetching submodule %s%s\n", prefix, ce->name); cp.dir = submodule_path.buf; - argv[default_argc] = default_argv; - argv[argc] = submodule_prefix.buf; + argv_array_push(&argv, default_argv); + argv_array_push(&argv, "--submodule-prefix"); + argv_array_push(&argv, submodule_prefix.buf); + cp.argv = argv.argv; if (run_command(&cp)) result = 1; + argv_array_pop(&argv); + argv_array_pop(&argv); + argv_array_pop(&argv); } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); } - free(argv); + argv_array_clear(&argv); out: string_list_clear(&changed_submodule_paths, 1); return result; diff --git a/submodule.h b/submodule.h index e105b0ebe6..594b50d510 100644 --- a/submodule.h +++ b/submodule.h @@ -2,6 +2,7 @@ #define SUBMODULE_H struct diff_options; +struct argv_array; enum { RECURSE_SUBMODULES_ON_DEMAND = -1, @@ -23,7 +24,7 @@ void show_submodule_summary(FILE *f, const char *path, const char *del, const char *add, const char *reset); void set_config_fetch_recurse_submodules(int value); void check_for_new_submodule_commits(unsigned char new_sha1[20]); -int fetch_populated_submodules(int num_options, const char **options, +int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, int quiet); unsigned is_submodule_modified(const char *path, int ignore_untracked);