diff options
Diffstat (limited to 'builtin')
42 files changed, 1075 insertions, 812 deletions
diff --git a/builtin/am.c b/builtin/am.c index 93bec62afa..39fea24833 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2301,7 +2301,7 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar return 0; } -static int git_am_config(const char *k, const char *v, void *cb) +static int git_am_config(const char *k, const char *v, void *cb UNUSED) { int status; diff --git a/builtin/archive.c b/builtin/archive.c index 7176b041b6..f094390ee0 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -75,7 +75,7 @@ static int run_remote_archiver(int argc, const char **argv, #define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ PARSE_OPT_KEEP_ARGV0 | \ - PARSE_OPT_KEEP_UNKNOWN | \ + PARSE_OPT_KEEP_UNKNOWN_OPT | \ PARSE_OPT_NO_INTERNAL_HELP ) int cmd_archive(int argc, const char **argv, const char *prefix) diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 8a052c7111..501245fac9 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -329,8 +329,9 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd) return 0; } -static int inc_nr(const char *refname, const struct object_id *oid, - int flag, void *cb_data) +static int inc_nr(const char *refname UNUSED, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cb_data) { unsigned int *nr = (unsigned int *)cb_data; (*nr)++; @@ -518,7 +519,7 @@ finish: } static int add_bisect_ref(const char *refname, const struct object_id *oid, - int flags, void *cb) + int flags UNUSED, void *cb) { struct add_bisect_ref_data *data = cb; @@ -1134,8 +1135,9 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a return res; } -static int get_first_good(const char *refname, const struct object_id *oid, - int flag, void *cb_data) +static int get_first_good(const char *refname UNUSED, + const struct object_id *oid, + int flag UNUSED, void *cb_data) { oidcpy(cb_data, oid); return 1; @@ -1324,7 +1326,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_bisect_helper_usage, - PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN_OPT); if (!cmdmode) usage_with_options(git_bisect_helper_usage, options); diff --git a/builtin/blame.c b/builtin/blame.c index 02e39420b6..a9fe8cf7a6 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -920,6 +920,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: + case PARSE_OPT_SUBCOMMAND: exit(129); case PARSE_OPT_COMPLETE: exit(0); diff --git a/builtin/bundle.c b/builtin/bundle.c index 2adad545a2..e80efce3a4 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -195,30 +195,19 @@ cleanup: int cmd_bundle(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("create", &fn, cmd_bundle_create), + OPT_SUBCOMMAND("verify", &fn, cmd_bundle_verify), + OPT_SUBCOMMAND("list-heads", &fn, cmd_bundle_list_heads), + OPT_SUBCOMMAND("unbundle", &fn, cmd_bundle_unbundle), OPT_END() }; - int result; argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + 0); packet_trace_identity("bundle"); - if (argc < 2) - usage_with_options(builtin_bundle_usage, options); - - else if (!strcmp(argv[0], "create")) - result = cmd_bundle_create(argc, argv, prefix); - else if (!strcmp(argv[0], "verify")) - result = cmd_bundle_verify(argc, argv, prefix); - else if (!strcmp(argv[0], "list-heads")) - result = cmd_bundle_list_heads(argc, argv, prefix); - else if (!strcmp(argv[0], "unbundle")) - result = cmd_bundle_unbundle(argc, argv, prefix); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_bundle_usage, options); - } - return result ? 1 : 0; + return !!fn(argc, argv, prefix); } diff --git a/builtin/checkout.c b/builtin/checkout.c index f9d63d80b9..2a132392fb 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -125,7 +125,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm } static int update_some(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, void *context UNUSED) { int len; struct cache_entry *ce; @@ -990,7 +990,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, static int add_pending_uninteresting_ref(const char *refname, const struct object_id *oid, - int flags, void *cb_data) + int flags UNUSED, void *cb_data) { add_pending_oid(cb_data, refname, oid, UNINTERESTING); return 0; diff --git a/builtin/clone.c b/builtin/clone.c index c4ff4643ec..d269d6fec6 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -34,6 +34,7 @@ #include "list-objects-filter-options.h" #include "hook.h" #include "bundle.h" +#include "bundle-uri.h" /* * Overall FIXMEs: @@ -72,11 +73,12 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; static int option_dissociate; static int max_jobs = -1; static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; -static struct list_objects_filter_options filter_options; +static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static int option_filter_submodules = -1; /* unspecified */ static int config_filter_submodules = -1; /* unspecified */ static struct string_list server_options = STRING_LIST_INIT_NODUP; static int option_remote_submodules; +static const char *bundle_uri; static int recurse_submodules_cb(const struct option *opt, const char *arg, int unset) @@ -160,6 +162,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will use their remote-tracking branch")), OPT_BOOL(0, "sparse", &option_sparse_checkout, N_("initialize sparse-checkout file to include only files at root")), + OPT_STRING(0, "bundle-uri", &bundle_uri, + N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")), OPT_END() }; @@ -933,6 +937,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_no_checkout = 1; } + if (bundle_uri && deepen) + die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude")); + repo_name = argv[0]; path = get_repo_path(repo_name, &is_bundle); @@ -1232,6 +1239,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; + /* + * Before fetching from the remote, download and install bundle + * data from the --bundle-uri option. + */ + if (bundle_uri) { + /* At this point, we need the_repository to match the cloned repo. */ + if (repo_init(the_repository, git_dir, work_tree)) + warning(_("failed to initialize the repo, skipping bundle URI")); + else if (fetch_bundle_uri(the_repository, bundle_uri)) + warning(_("failed to fetch objects from bundle URI '%s'"), + bundle_uri); + } strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); refspec_ref_prefixes(&remote->fetch, diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 51c4040ea6..51557fe786 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -58,7 +58,7 @@ static struct option *add_common_options(struct option *to) return parse_options_concat(common_opts, to); } -static int graph_verify(int argc, const char **argv) +static int graph_verify(int argc, const char **argv, const char *prefix) { struct commit_graph *graph = NULL; struct object_directory *odb = NULL; @@ -80,7 +80,7 @@ static int graph_verify(int argc, const char **argv) trace2_cmd_mode("verify"); opts.progress = isatty(2); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_commit_graph_verify_usage, 0); if (argc) @@ -179,7 +179,7 @@ static int write_option_max_new_filters(const struct option *opt, } static int git_commit_graph_write_config(const char *var, const char *value, - void *cb) + void *cb UNUSED) { if (!strcmp(var, "commitgraph.maxnewfilters")) write_opts.max_new_filters = git_config_int(var, value); @@ -190,7 +190,7 @@ static int git_commit_graph_write_config(const char *var, const char *value, return 0; } -static int graph_write(int argc, const char **argv) +static int graph_write(int argc, const char **argv, const char *prefix) { struct string_list pack_indexes = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; @@ -241,7 +241,7 @@ static int graph_write(int argc, const char **argv) git_config(git_commit_graph_write_config, &opts); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_commit_graph_write_usage, 0); if (argc) @@ -307,26 +307,22 @@ cleanup: int cmd_commit_graph(int argc, const char **argv, const char *prefix) { - struct option *builtin_commit_graph_options = common_opts; + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_commit_graph_options[] = { + OPT_SUBCOMMAND("verify", &fn, graph_verify), + OPT_SUBCOMMAND("write", &fn, graph_write), + OPT_END(), + }; + struct option *options = parse_options_concat(builtin_commit_graph_options, common_opts); git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, - builtin_commit_graph_options, - builtin_commit_graph_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc) - goto usage; read_replace_refs = 0; save_commit_buffer = 0; - if (!strcmp(argv[0], "verify")) - return graph_verify(argc, argv); - else if (argc && !strcmp(argv[0], "write")) - return graph_write(argc, argv); + argc = parse_options(argc, argv, prefix, options, + builtin_commit_graph_usage, 0); + FREE_AND_NULL(options); - error(_("unrecognized subcommand: %s"), argv[0]); -usage: - usage_with_options(builtin_commit_graph_usage, - builtin_commit_graph_options); + return fn(argc, argv, prefix); } diff --git a/builtin/config.c b/builtin/config.c index e7b88a9c08..753e5fac29 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -207,7 +207,8 @@ static void show_config_scope(struct strbuf *buf) strbuf_addch(buf, term); } -static int show_all_config(const char *key_, const char *value_, void *cb) +static int show_all_config(const char *key_, const char *value_, + void *cb UNUSED) { if (show_origin || show_scope) { struct strbuf buf = STRBUF_INIT; @@ -458,7 +459,8 @@ static const char *get_color_slot; static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; -static int git_get_color_config(const char *var, const char *value, void *cb) +static int git_get_color_config(const char *var, const char *value, + void *cb UNUSED) { if (!strcmp(var, get_color_slot)) { if (!value) @@ -490,7 +492,7 @@ static int get_colorbool_found; static int get_diff_color_found; static int get_color_ui_found; static int git_get_colorbool_config(const char *var, const char *value, - void *cb) + void *data UNUSED) { if (!strcmp(var, get_colorbool_slot)) get_colorbool_found = git_config_colorbool(var, value); diff --git a/builtin/describe.c b/builtin/describe.c index a76f1a1a7a..e17c4b4c69 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -63,7 +63,7 @@ static const char *prio_names[] = { N_("head"), N_("lightweight"), N_("annotated"), }; -static int commit_name_neq(const void *unused_cmp_data, +static int commit_name_neq(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *peeled) @@ -140,7 +140,8 @@ static void add_to_known_names(const char *path, } } -static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data) +static int get_name(const char *path, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { int is_tag = 0; struct object_id peeled; diff --git a/builtin/difftool.c b/builtin/difftool.c index b3c509b8de..4b10ad1a36 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -125,10 +125,10 @@ struct working_tree_entry { char path[FLEX_ARRAY]; }; -static int working_tree_entry_cmp(const void *unused_cmp_data, +static int working_tree_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct working_tree_entry *a, *b; @@ -148,10 +148,10 @@ struct pair_entry { const char path[FLEX_ARRAY]; }; -static int pair_cmp(const void *unused_cmp_data, +static int pair_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct pair_entry *a, *b; @@ -184,7 +184,7 @@ struct path_entry { char path[FLEX_ARRAY]; }; -static int path_entry_cmp(const void *unused_cmp_data, +static int path_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *key) @@ -716,7 +716,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) symlinks = has_symlinks; argc = parse_options(argc, argv, prefix, builtin_difftool_options, - builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN | + builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); if (tool_help) diff --git a/builtin/env--helper.c b/builtin/env--helper.c index 27349098b0..ea04c16636 100644 --- a/builtin/env--helper.c +++ b/builtin/env--helper.c @@ -50,7 +50,7 @@ int cmd_env__helper(int argc, const char **argv, const char *prefix) }; argc = parse_options(argc, argv, prefix, opts, env__helper_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (env_default && !*env_default) usage_with_options(env__helper_usage, opts); if (!cmdmode) diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e1748fb98b..3b3314e7b2 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -119,7 +119,7 @@ struct anonymized_entry_key { size_t orig_len; }; -static int anonymized_entry_cmp(const void *unused_cmp_data, +static int anonymized_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -1221,7 +1221,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) revs.sources = &revision_sources; revs.rewrite_parents = 1; argc = parse_options(argc, argv, prefix, options, fast_export_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) usage_with_options (fast_export_usage, options); diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 14113cfd82..7134683ab9 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -46,7 +46,7 @@ struct object_entry { depth : DEPTH_BITS; }; -static int object_entry_hashcmp(const void *map_data, +static int object_entry_hashcmp(const void *map_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index f045bbbe94..afe679368d 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -62,6 +62,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("fetch-pack"); memset(&args, 0, sizeof(args)); + list_objects_filter_init(&args.filter_options); args.uploadpack = "git-upload-pack"; for (i = 1; i < argc && *argv[i] == '-'; i++) { diff --git a/builtin/fetch.c b/builtin/fetch.c index 368a0f5329..a0fca93bb6 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -80,7 +80,7 @@ static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static int shown_url = 0; static struct refspec refmap = REFSPEC_INIT_FETCH; -static struct list_objects_filter_options filter_options; +static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static struct string_list server_options = STRING_LIST_INIT_DUP; static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; static int fetch_write_commit_graph = -1; @@ -301,7 +301,7 @@ struct refname_hash_entry { char refname[FLEX_ARRAY]; }; -static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data, +static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -329,7 +329,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map, static int add_one_refname(const char *refname, const struct object_id *oid, - int flag, void *cbdata) + int flag UNUSED, void *cbdata) { struct hashmap *refname_map = cbdata; @@ -1464,8 +1464,9 @@ static void set_option(struct transport *transport, const char *name, const char } -static int add_oid(const char *refname, const struct object_id *oid, int flags, - void *cb_data) +static int add_oid(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct oid_array *oids = cb_data; @@ -1616,9 +1617,21 @@ static int do_fetch(struct transport *transport, break; } } - } else if (transport->remote && transport->remote->fetch.nr) - refspec_ref_prefixes(&transport->remote->fetch, - &transport_ls_refs_options.ref_prefixes); + } else { + struct branch *branch = branch_get(NULL); + + if (transport->remote->fetch.nr) + refspec_ref_prefixes(&transport->remote->fetch, + &transport_ls_refs_options.ref_prefixes); + if (branch_has_merge_config(branch) && + !strcmp(branch->remote_name, transport->remote->name)) { + int i; + for (i = 0; i < branch->merge_nr; i++) { + strvec_push(&transport_ls_refs_options.ref_prefixes, + branch->merge[i]->src); + } + } + } if (tags == TAGS_SET || tags == TAGS_DEFAULT) { must_list_refs = 1; diff --git a/builtin/fsck.c b/builtin/fsck.c index 6c73092f10..f7916f06ed 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -488,8 +488,9 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, } static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) + const char *email UNUSED, + timestamp_t timestamp, int tz UNUSED, + const char *message UNUSED, void *cb_data) { const char *refname = cb_data; @@ -502,8 +503,9 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid return 0; } -static int fsck_handle_reflog(const char *logname, const struct object_id *oid, - int flag, void *cb_data) +static int fsck_handle_reflog(const char *logname, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cb_data) { struct strbuf refname = STRBUF_INIT; @@ -514,7 +516,7 @@ static int fsck_handle_reflog(const char *logname, const struct object_id *oid, } static int fsck_handle_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object *obj; diff --git a/builtin/gc.c b/builtin/gc.c index 6c22205217..0accc02406 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -782,8 +782,9 @@ struct cg_auto_data { int limit; }; -static int dfs_on_ref(const char *refname, - const struct object_id *oid, int flags, +static int dfs_on_ref(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct cg_auto_data *data = (struct cg_auto_data *)cb_data; @@ -1459,14 +1460,28 @@ static char *get_maintpath(void) return strbuf_detach(&sb, NULL); } -static int maintenance_register(void) +static char const * const builtin_maintenance_register_usage[] = { + N_("git maintenance register"), + NULL +}; + +static int maintenance_register(int argc, const char **argv, const char *prefix) { + struct option options[] = { + OPT_END(), + }; int rc; char *config_value; struct child_process config_set = CHILD_PROCESS_INIT; struct child_process config_get = CHILD_PROCESS_INIT; char *maintpath = get_maintpath(); + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_register_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_register_usage, + options); + /* Disable foreground maintenance */ git_config_set("maintenance.auto", "false"); @@ -1503,12 +1518,26 @@ done: return rc; } -static int maintenance_unregister(void) +static char const * const builtin_maintenance_unregister_usage[] = { + N_("git maintenance unregister"), + NULL +}; + +static int maintenance_unregister(int argc, const char **argv, const char *prefix) { + struct option options[] = { + OPT_END(), + }; int rc; struct child_process config_unset = CHILD_PROCESS_INIT; char *maintpath = get_maintpath(); + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_unregister_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_unregister_usage, + options); + config_unset.git_cmd = 1; strvec_pushl(&config_unset.args, "config", "--global", "--unset", "--fixed-value", "maintenance.repo", maintpath, NULL); @@ -2059,6 +2088,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) struct child_process crontab_edit = CHILD_PROCESS_INIT; FILE *cron_list, *cron_in; struct strbuf line = STRBUF_INIT; + struct tempfile *tmpedit = NULL; get_schedule_cmd(&cmd, NULL); strvec_split(&crontab_list.args, cmd); @@ -2073,6 +2103,17 @@ static int crontab_update_schedule(int run_maintenance, int fd) /* Ignore exit code, as an empty crontab will return error. */ finish_command(&crontab_list); + tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX"); + if (!tmpedit) { + result = error(_("failed to create crontab temporary file")); + goto out; + } + cron_in = fdopen_tempfile(tmpedit, "w"); + if (!cron_in) { + result = error(_("failed to open temporary file")); + goto out; + } + /* * Read from the .lock file, filtering out the old * schedule while appending the new schedule. @@ -2080,19 +2121,6 @@ static int crontab_update_schedule(int run_maintenance, int fd) cron_list = fdopen(fd, "r"); rewind(cron_list); - strvec_split(&crontab_edit.args, cmd); - crontab_edit.in = -1; - crontab_edit.git_cmd = 0; - - if (start_command(&crontab_edit)) - return error(_("failed to run 'crontab'; your system might not support 'cron'")); - - cron_in = fdopen(crontab_edit.in, "w"); - if (!cron_in) { - result = error(_("failed to open stdin of 'crontab'")); - goto done_editing; - } - while (!strbuf_getline_lf(&line, cron_list)) { if (!in_old_region && !strcmp(line.buf, BEGIN_LINE)) in_old_region = 1; @@ -2126,14 +2154,22 @@ static int crontab_update_schedule(int run_maintenance, int fd) } fflush(cron_in); - fclose(cron_in); - close(crontab_edit.in); -done_editing: + strvec_split(&crontab_edit.args, cmd); + strvec_push(&crontab_edit.args, get_tempfile_path(tmpedit)); + crontab_edit.git_cmd = 0; + + if (start_command(&crontab_edit)) { + result = error(_("failed to run 'crontab'; your system might not support 'cron'")); + goto out; + } + if (finish_command(&crontab_edit)) result = error(_("'crontab' died")); else fclose(cron_list); +out: + delete_tempfile(&tmpedit); return result; } @@ -2490,6 +2526,7 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) PARSE_OPT_NONEG, maintenance_opt_scheduler), OPT_END() }; + const char *register_args[] = { "register", NULL }; argc = parse_options(argc, argv, prefix, options, builtin_maintenance_start_usage, 0); @@ -2499,34 +2536,46 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) opts.scheduler = resolve_scheduler(opts.scheduler); validate_scheduler(opts.scheduler); - if (maintenance_register()) + if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL)) warning(_("failed to add repo to global config")); return update_background_schedule(&opts, 1); } -static int maintenance_stop(void) +static const char *const builtin_maintenance_stop_usage[] = { + N_("git maintenance stop"), + NULL +}; + +static int maintenance_stop(int argc, const char **argv, const char *prefix) { + struct option options[] = { + OPT_END() + }; + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_stop_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_stop_usage, options); return update_background_schedule(NULL, 0); } -static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]"); +static const char * const builtin_maintenance_usage[] = { + N_("git maintenance <subcommand> [<options>]"), + NULL, +}; int cmd_maintenance(int argc, const char **argv, const char *prefix) { - if (argc < 2 || - (argc == 2 && !strcmp(argv[1], "-h"))) - usage(builtin_maintenance_usage); - - if (!strcmp(argv[1], "run")) - return maintenance_run(argc - 1, argv + 1, prefix); - if (!strcmp(argv[1], "start")) - return maintenance_start(argc - 1, argv + 1, prefix); - if (!strcmp(argv[1], "stop")) - return maintenance_stop(); - if (!strcmp(argv[1], "register")) - return maintenance_register(); - if (!strcmp(argv[1], "unregister")) - return maintenance_unregister(); - - die(_("invalid subcommand: %s"), argv[1]); + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_maintenance_options[] = { + OPT_SUBCOMMAND("run", &fn, maintenance_run), + OPT_SUBCOMMAND("start", &fn, maintenance_start), + OPT_SUBCOMMAND("stop", &fn, maintenance_stop), + OPT_SUBCOMMAND("register", &fn, maintenance_register), + OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister), + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, builtin_maintenance_options, + builtin_maintenance_usage, 0); + return fn(argc, argv, prefix); } diff --git a/builtin/hook.c b/builtin/hook.c index 54e5c6ec93..b6530d189a 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -67,18 +67,14 @@ usage: int cmd_hook(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option builtin_hook_options[] = { + OPT_SUBCOMMAND("run", &fn, run), OPT_END(), }; argc = parse_options(argc, argv, NULL, builtin_hook_options, - builtin_hook_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc) - goto usage; + builtin_hook_usage, 0); - if (!strcmp(argv[0], "run")) - return run(argc, argv, prefix); - -usage: - usage_with_options(builtin_hook_usage, builtin_hook_options); + return fn(argc, argv, prefix); } diff --git a/builtin/log.c b/builtin/log.c index fd209725e5..ee19dc5d45 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -52,6 +52,7 @@ static int default_encode_email_headers = 1; static int decoration_style; static int decoration_given; static int use_mailmap_config = 1; +static unsigned int force_in_body_from; static const char *fmt_patch_subject_prefix = "PATCH"; static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; static const char *fmt_pretty; @@ -260,7 +261,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, mailmap = use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); if (quiet) @@ -697,9 +698,10 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) return 0; } -static int show_tree_object(const struct object_id *oid, - struct strbuf *base, - const char *pathname, unsigned mode, void *context) +static int show_tree_object(const struct object_id *oid UNUSED, + struct strbuf *base UNUSED, + const char *pathname, unsigned mode, + void *context) { FILE *file = context; fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : ""); @@ -1058,6 +1060,10 @@ static int git_format_config(const char *var, const char *value, void *cb) from = NULL; return 0; } + if (!strcmp(var, "format.forceinbodyfrom")) { + force_in_body_from = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "format.notes")) { int b = git_parse_maybe_bool(value); if (b < 0) @@ -1949,6 +1955,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("show changes against <refspec> in cover letter or single patch")), OPT_INTEGER(0, "creation-factor", &creation_factor, N_("percentage by which creation is weighted")), + OPT_BOOL(0, "force-in-body-from", &force_in_body_from, + N_("show in-body From: even if identical to the e-mail header")), OPT_END() }; @@ -1989,9 +1997,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); + rev.force_in_body_from = force_in_body_from; + /* Make sure "0000-$sub.patch" gives non-negative length for $sub */ if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 779dc18e59..4cf8a23648 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -257,7 +257,7 @@ static size_t expand_show_index(struct strbuf *sb, const char *start, end = strchr(start + 1, ')'); if (!end) - die(_("bad ls-files format: element '%s'" + die(_("bad ls-files format: element '%s' " "does not end in ')'"), start); len = end - start + 1; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index e279be8bb6..c3ea09281a 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -142,7 +142,7 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname } static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, void *context UNUSED) { size_t baselen; int recurse = 0; @@ -213,7 +213,7 @@ static void show_tree_common_default_long(struct strbuf *base, static int show_tree_default(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, - void *context) + void *context UNUSED) { int early; int recurse; @@ -230,7 +230,8 @@ static int show_tree_default(const struct object_id *oid, struct strbuf *base, } static int show_tree_long(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context UNUSED) { int early; int recurse; @@ -259,7 +260,8 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, } static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context UNUSED) { int early; int recurse; @@ -279,7 +281,8 @@ static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, } static int show_tree_object(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context UNUSED) { int early; int recurse; diff --git a/builtin/merge.c b/builtin/merge.c index f7c92c0e64..5900b81729 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -503,7 +503,8 @@ static void finish(struct commit *head_commit, /* Run a post-merge hook */ run_hooks_l("post-merge", squash ? "1" : "0", NULL); - apply_autostash(git_path_merge_autostash(the_repository)); + if (new_head) + apply_autostash(git_path_merge_autostash(the_repository)); strbuf_release(&reflog_message); } @@ -1692,7 +1693,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (save_state(&stash)) oidclr(&stash); - for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) { + for (i = 0; i < use_strategies_nr; i++) { int ret, cnt; if (i) { printf(_("Rewinding the tree to pristine...\n")); @@ -1707,7 +1708,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) */ wt_strategy = use_strategies[i]->name; - ret = try_merge_strategy(use_strategies[i]->name, + ret = try_merge_strategy(wt_strategy, common, remoteheads, head_commit); /* @@ -1717,16 +1718,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) */ if (ret < 2) { if (!ret) { - if (option_commit) { - /* Automerge succeeded. */ - automerge_was_ok = 1; - break; - } + /* + * This strategy worked; no point in trying + * another. + */ merge_was_ok = 1; + best_strategy = wt_strategy; + break; } cnt = (use_strategies_nr > 1) ? evaluate_result() : 0; if (best_cnt <= 0 || cnt <= best_cnt) { - best_strategy = use_strategies[i]->name; + best_strategy = wt_strategy; best_cnt = cnt; } } @@ -1736,7 +1738,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * If we have a resulting tree, that means the strategy module * auto resolved the merge cleanly. */ - if (automerge_was_ok) { + if (merge_was_ok && option_commit) { + automerge_was_ok = 1; ret = finish_automerge(head_commit, head_subsumed, common, remoteheads, &result_tree, wt_strategy); @@ -1781,6 +1784,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) "stopped before committing as requested\n")); else ret = suggest_conflicts(); + if (autostash) + printf(_("When finished, apply stashed changes with `git stash pop`\n")); done: if (!automerge_was_ok) { diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 8f24d59a75..9b126d6ce0 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -78,7 +78,7 @@ static struct option *add_common_options(struct option *prev) } static int git_multi_pack_index_write_config(const char *var, const char *value, - void *cb) + void *cb UNUSED) { if (!strcmp(var, "pack.writebitmaphashcache")) { if (git_config_bool(var, value)) @@ -87,6 +87,13 @@ static int git_multi_pack_index_write_config(const char *var, const char *value, opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE; } + if (!strcmp(var, "pack.writebitmaplookuptable")) { + if (git_config_bool(var, value)) + opts.flags |= MIDX_WRITE_BITMAP_LOOKUP_TABLE; + else + opts.flags &= ~MIDX_WRITE_BITMAP_LOOKUP_TABLE; + } + /* * We should never make a fall-back call to 'git_default_config', since * this was already called in 'cmd_multi_pack_index()'. @@ -104,7 +111,8 @@ static void read_packs_from_stdin(struct string_list *to) strbuf_release(&buf); } -static int cmd_multi_pack_index_write(int argc, const char **argv) +static int cmd_multi_pack_index_write(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_write_options[] = { @@ -132,7 +140,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_write_usage, 0); if (argc) @@ -160,7 +168,8 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) opts.refs_snapshot, opts.flags); } -static int cmd_multi_pack_index_verify(int argc, const char **argv) +static int cmd_multi_pack_index_verify(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_verify_options[] = { @@ -174,7 +183,7 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_verify_usage, 0); if (argc) @@ -186,7 +195,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv) return verify_midx_file(the_repository, opts.object_dir, opts.flags); } -static int cmd_multi_pack_index_expire(int argc, const char **argv) +static int cmd_multi_pack_index_expire(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_expire_options[] = { @@ -200,7 +210,7 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_expire_usage, 0); if (argc) @@ -212,7 +222,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv) return expire_midx_packs(the_repository, opts.object_dir, opts.flags); } -static int cmd_multi_pack_index_repack(int argc, const char **argv) +static int cmd_multi_pack_index_repack(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_repack_options[] = { @@ -229,7 +240,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_repack_usage, 0); @@ -247,7 +258,15 @@ int cmd_multi_pack_index(int argc, const char **argv, const char *prefix) { int res; - struct option *builtin_multi_pack_index_options = common_opts; + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_multi_pack_index_options[] = { + OPT_SUBCOMMAND("repack", &fn, cmd_multi_pack_index_repack), + OPT_SUBCOMMAND("write", &fn, cmd_multi_pack_index_write), + OPT_SUBCOMMAND("verify", &fn, cmd_multi_pack_index_verify), + OPT_SUBCOMMAND("expire", &fn, cmd_multi_pack_index_expire), + OPT_END(), + }; + struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts); git_config(git_default_config, NULL); @@ -256,31 +275,12 @@ int cmd_multi_pack_index(int argc, const char **argv, the_repository->objects->odb) opts.object_dir = xstrdup(the_repository->objects->odb->path); - argc = parse_options(argc, argv, prefix, - builtin_multi_pack_index_options, - builtin_multi_pack_index_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (!argc) - goto usage; - - if (!strcmp(argv[0], "repack")) - res = cmd_multi_pack_index_repack(argc, argv); - else if (!strcmp(argv[0], "write")) - res = cmd_multi_pack_index_write(argc, argv); - else if (!strcmp(argv[0], "verify")) - res = cmd_multi_pack_index_verify(argc, argv); - else if (!strcmp(argv[0], "expire")) - res = cmd_multi_pack_index_expire(argc, argv); - else { - error(_("unrecognized subcommand: %s"), argv[0]); - goto usage; - } + argc = parse_options(argc, argv, prefix, options, + builtin_multi_pack_index_usage, 0); + FREE_AND_NULL(options); + + res = fn(argc, argv, prefix); free(opts.object_dir); return res; - -usage: - usage_with_options(builtin_multi_pack_index_usage, - builtin_multi_pack_index_options); } diff --git a/builtin/mv.c b/builtin/mv.c index 4729bb1a1a..3413ad1c9b 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -21,7 +21,6 @@ static const char * const builtin_mv_usage[] = { }; enum update_mode { - BOTH = 0, WORKING_DIRECTORY = (1 << 1), INDEX = (1 << 2), SPARSE = (1 << 3), @@ -72,7 +71,7 @@ static const char **internal_prefix_pathspec(const char *prefix, static const char *add_slash(const char *path) { size_t len = strlen(path); - if (path[len - 1] != '/') { + if (len && path[len - 1] != '/') { char *with_slash = xmalloc(st_add(len, 2)); memcpy(with_slash, path, len); with_slash[len++] = '/'; @@ -125,16 +124,15 @@ static int index_range_of_same_dir(const char *src, int length, } /* - * Check if an out-of-cone directory should be in the index. Imagine this case - * that all the files under a directory are marked with 'CE_SKIP_WORKTREE' bit - * and thus the directory is sparsified. - * - * Return 0 if such directory exist (i.e. with any of its contained files not - * marked with CE_SKIP_WORKTREE, the directory would be present in working tree). - * Return 1 otherwise. + * Given the path of a directory that does not exist on-disk, check whether the + * directory contains any entries in the index with the SKIP_WORKTREE flag + * enabled. + * Return 1 if such index entries exist. + * Return 0 otherwise. */ -static int check_dir_in_index(const char *name) +static int empty_dir_has_sparse_contents(const char *name) { + int ret = 0; const char *with_slash = add_slash(name); int length = strlen(with_slash); @@ -144,14 +142,18 @@ static int check_dir_in_index(const char *name) if (pos < 0) { pos = -pos - 1; if (pos >= the_index.cache_nr) - return 1; + goto free_return; ce = active_cache[pos]; if (strncmp(with_slash, ce->name, length)) - return 1; + goto free_return; if (ce_skip_worktree(ce)) - return 0; + ret = 1; } - return 1; + +free_return: + if (with_slash != name) + free((char *)with_slash); + return ret; } int cmd_mv(int argc, const char **argv, const char *prefix) @@ -168,12 +170,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; - enum update_mode *modes; + const char *dst_w_slash; + const char **src_dir = NULL; + int src_dir_nr = 0, src_dir_alloc = 0; + struct strbuf a_src_dir = STRBUF_INIT; + enum update_mode *modes, dst_mode = 0; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; struct lock_file lock_file = LOCK_INIT; struct cache_entry *ce; struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; + struct string_list dirty_paths = STRING_LIST_INIT_NODUP; git_config(git_default_config, NULL); @@ -198,6 +205,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); + dst_w_slash = add_slash(dest_path[0]); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') @@ -205,12 +213,31 @@ int cmd_mv(int argc, const char **argv, const char *prefix) destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { - dest_path[0] = add_slash(dest_path[0]); - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); } else { - if (argc != 1) + if (!path_in_sparse_checkout(dst_w_slash, &the_index) && + empty_dir_has_sparse_contents(dst_w_slash)) { + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); + dst_mode = SKIP_WORKTREE_DIR; + } else if (argc != 1) { die(_("destination '%s' is not a directory"), dest_path[0]); - destination = dest_path; + } else { + destination = dest_path; + /* + * <destination> is a file outside of sparse-checkout + * cone. Insist on cone mode here for backward + * compatibility. We don't want dst_mode to be assigned + * for a file when the repo is using no-cone mode (which + * is deprecated at this point) sparse-checkout. As + * SPARSE here is only considering cone-mode situation. + */ + if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index)) + dst_mode = SPARSE; + } + } + if (dst_w_slash != dest_path[0]) { + free((char *)dst_w_slash); + dst_w_slash = NULL; } /* Checking */ @@ -232,7 +259,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (pos < 0) { const char *src_w_slash = add_slash(src); if (!path_in_sparse_checkout(src_w_slash, &the_index) && - !check_dir_in_index(src)) { + empty_dir_has_sparse_contents(src)) { modes[i] |= SKIP_WORKTREE_DIR; goto dir_check; } @@ -290,6 +317,10 @@ dir_check: /* last - first >= 1 */ modes[i] |= WORKING_DIRECTORY; + + ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc); + src_dir[src_dir_nr++] = src; + n = argc + last - first; REALLOC_ARRAY(source, n); REALLOC_ARRAY(destination, n); @@ -346,6 +377,18 @@ dir_check: goto act_on_entry; } + if (ignore_sparse && + (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && + index_entry_exists(&the_index, dst, strlen(dst))) { + bad = _("destination exists in the index"); + if (force) { + if (verbose) + warning(_("overwriting '%s'"), dst); + bad = NULL; + } else { + goto act_on_entry; + } + } /* * We check if the paths are in the sparse-checkout * definition as a very final check, since that @@ -396,6 +439,7 @@ remove_entry: const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; + int sparse_and_dirty = 0; struct checkout state = CHECKOUT_INIT; state.istate = &the_index; @@ -406,6 +450,7 @@ remove_entry: if (show_only) continue; if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) && + !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && rename(src, dst) < 0) { if (ignore_errors) continue; @@ -425,20 +470,81 @@ remove_entry: pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); + if (!(mode & SPARSE) && !lstat(src, &st)) + sparse_and_dirty = ce_modified(active_cache[pos], &st, 0); rename_cache_entry_at(pos, dst); - if ((mode & SPARSE) && - (path_in_sparse_checkout(dst, &the_index))) { - int dst_pos; + if (ignore_sparse && + core_apply_sparse_checkout && + core_sparse_checkout_cone) { + /* + * NEEDSWORK: we are *not* paying attention to + * "out-to-out" move (<source> is out-of-cone and + * <destination> is out-of-cone) at this point. It + * should be added in a future patch. + */ + if ((mode & SPARSE) && + path_in_sparse_checkout(dst, &the_index)) { + /* from out-of-cone to in-cone */ + int dst_pos = cache_name_pos(dst, strlen(dst)); + struct cache_entry *dst_ce = active_cache[dst_pos]; + + dst_ce->ce_flags &= ~CE_SKIP_WORKTREE; + + if (checkout_entry(dst_ce, &state, NULL, NULL)) + die(_("cannot checkout %s"), dst_ce->name); + } else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && + !(mode & SPARSE) && + !path_in_sparse_checkout(dst, &the_index)) { + /* from in-cone to out-of-cone */ + int dst_pos = cache_name_pos(dst, strlen(dst)); + struct cache_entry *dst_ce = active_cache[dst_pos]; - dst_pos = cache_name_pos(dst, strlen(dst)); - active_cache[dst_pos]->ce_flags &= ~CE_SKIP_WORKTREE; + /* + * if src is clean, it will suffice to remove it + */ + if (!sparse_and_dirty) { + dst_ce->ce_flags |= CE_SKIP_WORKTREE; + unlink_or_warn(src); + } else { + /* + * if src is dirty, move it to the + * destination and create leading + * dirs if necessary + */ + char *dst_dup = xstrdup(dst); + string_list_append(&dirty_paths, dst); + safe_create_leading_directories(dst_dup); + FREE_AND_NULL(dst_dup); + rename(src, dst); + } + } + } + } - if (checkout_entry(active_cache[dst_pos], &state, NULL, NULL)) - die(_("cannot checkout %s"), active_cache[dst_pos]->name); + /* + * cleanup the empty src_dirs + */ + for (i = 0; i < src_dir_nr; i++) { + int dummy; + strbuf_addstr(&a_src_dir, src_dir[i]); + /* + * if entries under a_src_dir are all moved away, + * recursively remove a_src_dir to cleanup + */ + if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len, + &dummy, &dummy) < 1) { + remove_dir_recursively(&a_src_dir, 0); } + strbuf_reset(&a_src_dir); } + strbuf_release(&a_src_dir); + free(src_dir); + + if (dirty_paths.nr) + advise_on_moving_dirty_path(&dirty_paths); + if (gitmodules_modified) stage_updated_gitmodules(&the_index); @@ -447,6 +553,7 @@ remove_entry: die(_("Unable to write new index file")); string_list_clear(&src_for_dst, 0); + string_list_clear(&dirty_paths, 0); UNLEAK(source); UNLEAK(dest_path); free(submodule_gitfile); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 580b1eb170..15535e914a 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -344,7 +344,8 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_) return a->taggerdate != b->taggerdate; } -static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) +static int name_ref(const char *path, const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct object *o = parse_object(the_repository, oid); struct name_ref_data *data = cb_data; diff --git a/builtin/notes.c b/builtin/notes.c index a3d0d15a22..be51f69225 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -994,17 +994,34 @@ static int get_ref(int argc, const char **argv, const char *prefix) int cmd_notes(int argc, const char **argv, const char *prefix) { - int result; const char *override_notes_ref = NULL; + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"), N_("use notes from <notes-ref>")), + OPT_SUBCOMMAND("list", &fn, list), + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("copy", &fn, copy), + OPT_SUBCOMMAND("append", &fn, append_edit), + OPT_SUBCOMMAND("edit", &fn, append_edit), + OPT_SUBCOMMAND("show", &fn, show), + OPT_SUBCOMMAND("merge", &fn, merge), + OPT_SUBCOMMAND("remove", &fn, remove_cmd), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("get-ref", &fn, get_ref), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_notes_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + PARSE_OPT_SUBCOMMAND_OPTIONAL); + if (!fn) { + if (argc) { + error(_("unknown subcommand: `%s'"), argv[0]); + usage_with_options(git_notes_usage, options); + } + fn = list; + } if (override_notes_ref) { struct strbuf sb = STRBUF_INIT; @@ -1014,28 +1031,5 @@ int cmd_notes(int argc, const char **argv, const char *prefix) strbuf_release(&sb); } - if (argc < 1 || !strcmp(argv[0], "list")) - result = list(argc, argv, prefix); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv, prefix); - else if (!strcmp(argv[0], "copy")) - result = copy(argc, argv, prefix); - else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit")) - result = append_edit(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv, prefix); - else if (!strcmp(argv[0], "merge")) - result = merge(argc, argv, prefix); - else if (!strcmp(argv[0], "remove")) - result = remove_cmd(argc, argv, prefix); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv, prefix); - else if (!strcmp(argv[0], "get-ref")) - result = get_ref(argc, argv, prefix); - else { - result = error(_("unknown subcommand: %s"), argv[0]); - usage_with_options(git_notes_usage, options); - } - - return result ? 1 : 0; + return !!fn(argc, argv, prefix); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 39e28cfcaf..3658c05caf 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -759,8 +759,8 @@ static enum write_one_status write_one(struct hashfile *f, return WRITE_ONE_WRITTEN; } -static int mark_tagged(const char *path, const struct object_id *oid, int flag, - void *cb_data) +static int mark_tagged(const char *path UNUSED, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { struct object_id peeled; struct object_entry *entry = packlist_find(&to_pack, oid); @@ -3035,7 +3035,8 @@ static void add_tag_chain(const struct object_id *oid) } } -static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data) +static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { struct object_id peeled; @@ -3148,6 +3149,14 @@ static int git_pack_config(const char *k, const char *v, void *cb) else write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE; } + + if (!strcmp(k, "pack.writebitmaplookuptable")) { + if (git_config_bool(k, v)) + write_bitmap_options |= BITMAP_OPT_LOOKUP_TABLE; + else + write_bitmap_options &= ~BITMAP_OPT_LOOKUP_TABLE; + } + if (!strcmp(k, "pack.usebitmaps")) { use_bitmap_index_default = git_config_bool(k, v); return 0; @@ -3950,8 +3959,9 @@ static void record_recent_commit(struct commit *commit, void *data) } static int mark_bitmap_preferred_tip(const char *refname, - const struct object_id *oid, int flags, - void *_data) + const struct object_id *oid, + int flags UNUSED, + void *data UNUSED) { struct object_id peeled; struct object *object; diff --git a/builtin/range-diff.c b/builtin/range-diff.c index 50318849d6..e2a74efb42 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -38,8 +38,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) OPT_END() }; struct option *options; - int res = 0; + int i, dash_dash = -1, res = 0; struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; + struct object_id oid; + const char *three_dots = NULL; git_config(git_diff_ui_config, NULL); @@ -47,7 +49,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) options = parse_options_concat(range_diff_options, diffopt.parseopts); argc = parse_options(argc, argv, prefix, options, - builtin_range_diff_usage, 0); + builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH); diff_setup_done(&diffopt); @@ -55,40 +57,91 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) if (!simple_color) diffopt.use_color = 1; - if (argc == 2) { - if (!is_range_diff_range(argv[0])) - die(_("not a commit range: '%s'"), argv[0]); - strbuf_addstr(&range1, argv[0]); + for (i = 0; i < argc; i++) + if (!strcmp(argv[i], "--")) { + dash_dash = i; + break; + } + + if (dash_dash == 3 || + (dash_dash < 0 && argc > 2 && + !get_oid_committish(argv[0], &oid) && + !get_oid_committish(argv[1], &oid) && + !get_oid_committish(argv[2], &oid))) { + if (dash_dash < 0) + ; /* already validated arguments */ + else if (get_oid_committish(argv[0], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[0]); + else if (get_oid_committish(argv[1], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[1]); + else if (get_oid_committish(argv[2], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[2]); - if (!is_range_diff_range(argv[1])) - die(_("not a commit range: '%s'"), argv[1]); - strbuf_addstr(&range2, argv[1]); - } else if (argc == 3) { strbuf_addf(&range1, "%s..%s", argv[0], argv[1]); strbuf_addf(&range2, "%s..%s", argv[0], argv[2]); - } else if (argc == 1) { - const char *b = strstr(argv[0], "..."), *a = argv[0]; + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 3 : dash_dash)); + } else if (dash_dash == 2 || + (dash_dash < 0 && argc > 1 && + is_range_diff_range(argv[0]) && + is_range_diff_range(argv[1]))) { + if (dash_dash < 0) + ; /* already validated arguments */ + else if (!is_range_diff_range(argv[0])) + usage_msg_optf(_("not a commit range: '%s'"), + builtin_range_diff_usage, options, + argv[0]); + else if (!is_range_diff_range(argv[1])) + usage_msg_optf(_("not a commit range: '%s'"), + builtin_range_diff_usage, options, + argv[1]); + + strbuf_addstr(&range1, argv[0]); + strbuf_addstr(&range2, argv[1]); + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 2 : dash_dash)); + } else if (dash_dash == 1 || + (dash_dash < 0 && argc > 0 && + (three_dots = strstr(argv[0], "...")))) { + const char *a, *b; int a_len; - if (!b) { - error(_("single arg format must be symmetric range")); - usage_with_options(builtin_range_diff_usage, options); - } + if (dash_dash < 0) + ; /* already validated arguments */ + else if (!(three_dots = strstr(argv[0], "..."))) + usage_msg_optf(_("not a symmetric range: '%s'"), + builtin_range_diff_usage, options, + argv[0]); - a_len = (int)(b - a); - if (!a_len) { + if (three_dots == argv[0]) { a = "HEAD"; a_len = strlen(a); + } else { + a = argv[0]; + a_len = (int)(three_dots - a); } - b += 3; - if (!*b) + + if (three_dots[3]) + b = three_dots + 3; + else b = "HEAD"; + strbuf_addf(&range1, "%s..%.*s", b, a_len, a); strbuf_addf(&range2, "%.*s..%s", a_len, a, b); - } else { - error(_("need two commit ranges")); - usage_with_options(builtin_range_diff_usage, options); - } + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 1 : dash_dash)); + } else + usage_msg_opt(_("need two commit ranges"), + builtin_range_diff_usage, options); FREE_AND_NULL(options); range_diff_opts.dual_color = simple_color < 1; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 31b48e728b..44bcea3a5b 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -291,7 +291,7 @@ static void show_ref(const char *path, const struct object_id *oid) } static int show_ref_cb(const char *path_full, const struct object_id *oid, - int flag, void *data) + int flag UNUSED, void *data) { struct oidset *seen = data; const char *path = strip_namespace(path_full); @@ -465,7 +465,7 @@ static void rp_error(const char *err, ...) va_end(params); } -static int copy_to_sideband(int in, int out, void *arg) +static int copy_to_sideband(int in, int out UNUSED, void *arg UNUSED) { char data[128]; int keepalive_active = 0; diff --git a/builtin/reflog.c b/builtin/reflog.c index 8123956847..57c5c0d061 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -56,7 +56,8 @@ struct worktree_reflogs { struct string_list reflogs; }; -static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data) +static int collect_reflog(const char *ref, const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct worktree_reflogs *cb = cb_data; struct worktree *worktree = cb->worktree; @@ -227,7 +228,7 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix) parse_options(argc, argv, prefix, options, reflog_show_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); return cmd_log_reflog(argc, argv, prefix); } @@ -408,40 +409,21 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) int cmd_reflog(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), + OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), + OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), + OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), OPT_END() }; argc = parse_options(argc, argv, prefix, options, reflog_usage, + PARSE_OPT_SUBCOMMAND_OPTIONAL | PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN | - PARSE_OPT_NO_INTERNAL_HELP); - - /* - * With "git reflog" we default to showing it. !argc is - * impossible with PARSE_OPT_KEEP_ARGV0. - */ - if (argc == 1) - goto log_reflog; - - if (!strcmp(argv[1], "-h")) - usage_with_options(reflog_usage, options); - else if (*argv[1] == '-') - goto log_reflog; - - if (!strcmp(argv[1], "show")) - return cmd_reflog_show(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "expire")) - return cmd_reflog_expire(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "delete")) - return cmd_reflog_delete(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "exists")) - return cmd_reflog_exists(argc - 1, argv + 1, prefix); - - /* - * Fall-through for e.g. "git reflog -1", "git reflog master", - * as well as the plain "git reflog" above goto above. - */ -log_reflog: - return cmd_log_reflog(argc, argv, prefix); + PARSE_OPT_KEEP_UNKNOWN_OPT); + if (fn) + return fn(argc - 1, argv + 1, prefix); + else + return cmd_log_reflog(argc, argv, prefix); } diff --git a/builtin/remote.c b/builtin/remote.c index c713463d89..985b845a18 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -150,7 +150,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) return 0; } -static int add(int argc, const char **argv) +static int add(int argc, const char **argv, const char *prefix) { int fetch = 0, fetch_tags = TAGS_DEFAULT; unsigned mirror = MIRROR_NONE; @@ -177,8 +177,8 @@ static int add(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_add_usage, 0); if (argc != 2) usage_with_options(builtin_remote_add_usage, options); @@ -264,7 +264,8 @@ static const char *abbrev_ref(const char *name, const char *prefix) } #define abbrev_branch(name) abbrev_ref((name), "refs/heads/") -static int config_read_branches(const char *key, const char *value, void *cb) +static int config_read_branches(const char *key, const char *value, + void *data UNUSED) { const char *orig_key = key; char *name; @@ -538,7 +539,8 @@ struct branches_for_remote { }; static int add_branch_for_removal(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct branches_for_remote *branches = cb_data; struct refspec_item refspec; @@ -580,7 +582,8 @@ struct rename_info { }; static int read_remote_branches(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct rename_info *rename = cb_data; struct strbuf buf = STRBUF_INIT; @@ -680,7 +683,7 @@ static void handle_push_default(const char* old_name, const char* new_name) } -static int mv(int argc, const char **argv) +static int mv(int argc, const char **argv, const char *prefix) { int show_progress = isatty(2); struct option options[] = { @@ -695,7 +698,7 @@ static int mv(int argc, const char **argv) int i, refs_renamed_nr = 0, refspec_updated = 0; struct progress *progress = NULL; - argc = parse_options(argc, argv, NULL, options, + argc = parse_options(argc, argv, prefix, options, builtin_remote_rename_usage, 0); if (argc != 2) @@ -844,7 +847,7 @@ static int mv(int argc, const char **argv) return 0; } -static int rm(int argc, const char **argv) +static int rm(int argc, const char **argv, const char *prefix) { struct option options[] = { OPT_END() @@ -862,12 +865,14 @@ static int rm(int argc, const char **argv) cb_data.skipped = &skipped; cb_data.keep = &known_remotes; - if (argc != 2) + argc = parse_options(argc, argv, prefix, options, + builtin_remote_rm_usage, 0); + if (argc != 1) usage_with_options(builtin_remote_rm_usage, options); - remote = remote_get(argv[1]); + remote = remote_get(argv[0]); if (!remote_is_configured(remote, 1)) { - error(_("No such remote: '%s'"), argv[1]); + error(_("No such remote: '%s'"), argv[0]); exit(2); } @@ -953,7 +958,8 @@ static void free_remote_ref_states(struct ref_states *states) } static int append_ref_to_tracked_list(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags, void *cb_data) { struct ref_states *states = cb_data; struct refspec_item refspec; @@ -1254,7 +1260,7 @@ static int show_all(void) return result; } -static int show(int argc, const char **argv) +static int show(int argc, const char **argv, const char *prefix) { int no_query = 0, result = 0, query_flag = 0; struct option options[] = { @@ -1263,7 +1269,8 @@ static int show(int argc, const char **argv) }; struct show_info info = SHOW_INFO_INIT; - argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_show_usage, 0); if (argc < 1) @@ -1357,7 +1364,7 @@ static int show(int argc, const char **argv) return result; } -static int set_head(int argc, const char **argv) +static int set_head(int argc, const char **argv, const char *prefix) { int i, opt_a = 0, opt_d = 0, result = 0; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; @@ -1370,8 +1377,8 @@ static int set_head(int argc, const char **argv) N_("delete refs/remotes/<name>/HEAD")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_sethead_usage, 0); if (argc) strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); @@ -1462,7 +1469,7 @@ static int prune_remote(const char *remote, int dry_run) return result; } -static int prune(int argc, const char **argv) +static int prune(int argc, const char **argv, const char *prefix) { int dry_run = 0, result = 0; struct option options[] = { @@ -1470,8 +1477,8 @@ static int prune(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_prune_usage, 0); if (argc < 1) usage_with_options(builtin_remote_prune_usage, options); @@ -1482,7 +1489,7 @@ static int prune(int argc, const char **argv) return result; } -static int get_remote_default(const char *key, const char *value, void *priv) +static int get_remote_default(const char *key, const char *value UNUSED, void *priv) { if (strcmp(key, "remotes.default") == 0) { int *found = priv; @@ -1491,7 +1498,7 @@ static int get_remote_default(const char *key, const char *value, void *priv) return 0; } -static int update(int argc, const char **argv) +static int update(int argc, const char **argv, const char *prefix) { int i, prune = -1; struct option options[] = { @@ -1503,7 +1510,8 @@ static int update(int argc, const char **argv) int default_defined = 0; int retval; - argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); strvec_push(&fetch_argv, "fetch"); @@ -1574,7 +1582,7 @@ static int set_remote_branches(const char *remotename, const char **branches, return 0; } -static int set_branches(int argc, const char **argv) +static int set_branches(int argc, const char **argv, const char *prefix) { int add_mode = 0; struct option options[] = { @@ -1582,7 +1590,7 @@ static int set_branches(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, + argc = parse_options(argc, argv, prefix, options, builtin_remote_setbranches_usage, 0); if (argc == 0) { error(_("no remote specified")); @@ -1593,7 +1601,7 @@ static int set_branches(int argc, const char **argv) return set_remote_branches(argv[0], argv + 1, add_mode); } -static int get_url(int argc, const char **argv) +static int get_url(int argc, const char **argv, const char *prefix) { int i, push_mode = 0, all_mode = 0; const char *remotename = NULL; @@ -1607,7 +1615,8 @@ static int get_url(int argc, const char **argv) N_("return all URLs")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_geturl_usage, 0); if (argc != 1) usage_with_options(builtin_remote_geturl_usage, options); @@ -1646,7 +1655,7 @@ static int get_url(int argc, const char **argv) return 0; } -static int set_url(int argc, const char **argv) +static int set_url(int argc, const char **argv, const char *prefix) { int i, push_mode = 0, add_mode = 0, delete_mode = 0; int matches = 0, negative_matches = 0; @@ -1667,7 +1676,8 @@ static int set_url(int argc, const char **argv) N_("delete URLs")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_seturl_usage, PARSE_OPT_KEEP_ARGV0); if (add_mode && delete_mode) @@ -1738,41 +1748,33 @@ out: int cmd_remote(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("rename", &fn, mv), + OPT_SUBCOMMAND_F("rm", &fn, rm, PARSE_OPT_NOCOMPLETE), + OPT_SUBCOMMAND("remove", &fn, rm), + OPT_SUBCOMMAND("set-head", &fn, set_head), + OPT_SUBCOMMAND("set-branches", &fn, set_branches), + OPT_SUBCOMMAND("get-url", &fn, get_url), + OPT_SUBCOMMAND("set-url", &fn, set_url), + OPT_SUBCOMMAND("show", &fn, show), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("update", &fn, update), OPT_END() }; - int result; argc = parse_options(argc, argv, prefix, options, builtin_remote_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + PARSE_OPT_SUBCOMMAND_OPTIONAL); - if (argc < 1) - result = show_all(); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv); - else if (!strcmp(argv[0], "rename")) - result = mv(argc, argv); - else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) - result = rm(argc, argv); - else if (!strcmp(argv[0], "set-head")) - result = set_head(argc, argv); - else if (!strcmp(argv[0], "set-branches")) - result = set_branches(argc, argv); - else if (!strcmp(argv[0], "get-url")) - result = get_url(argc, argv); - else if (!strcmp(argv[0], "set-url")) - result = set_url(argc, argv); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv); - else if (!strcmp(argv[0], "update")) - result = update(argc, argv); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_remote_usage, options); + if (fn) { + return !!fn(argc, argv, prefix); + } else { + if (argc) { + error(_("unknown subcommand: `%s'"), argv[0]); + usage_with_options(builtin_remote_usage, options); + } + return !!show_all(); } - - return result ? 1 : 0; } diff --git a/builtin/repack.c b/builtin/repack.c index 482b66f57d..a5bacc7797 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -514,9 +514,9 @@ struct midx_snapshot_ref_data { int preferred; }; -static int midx_snapshot_ref_one(const char *refname, +static int midx_snapshot_ref_one(const char *refname UNUSED, const struct object_id *oid, - int flag, void *_data) + int flag UNUSED, void *_data) { struct midx_snapshot_ref_data *data = _data; struct object_id peeled; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index b259d8990a..8f61050bde 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -195,7 +195,8 @@ static int show_default(void) return 0; } -static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) +static int show_reference(const char *refname, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { if (ref_excluded(ref_excludes, refname)) return 0; @@ -203,7 +204,8 @@ static int show_reference(const char *refname, const struct object_id *oid, int return 0; } -static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) +static int anti_reference(const char *refname, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { show_rev(REVERSED, oid, refname); return 0; @@ -479,6 +481,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) if (!s) s = help; + if (s == sb.buf) + die(_("missing opt-spec before option flags")); + if (s - sb.buf == 1) /* short option only */ o->short_name = *sb.buf; else if (sb.buf[1] != ',') /* long option only */ diff --git a/builtin/revert.c b/builtin/revert.c index 2554f9099c..ee2a0807f0 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -141,7 +141,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) argc = parse_options(argc, argv, NULL, options, usage_str, PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 086dfee45a..7a1e1fe7c0 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -381,6 +381,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: + case PARSE_OPT_SUBCOMMAND: exit(129); case PARSE_OPT_COMPLETE: exit(0); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 64c649c6a2..d3f5715e3e 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -404,7 +404,7 @@ static int append_ref(const char *refname, const struct object_id *oid, } static int append_head_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object_id tmp; int ofs = 11; @@ -419,7 +419,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid, } static int append_remote_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object_id tmp; int ofs = 13; @@ -434,7 +434,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid, } static int append_tag_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { if (!starts_with(refname, "refs/tags/")) return 0; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 5fa207a044..4856906108 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -47,7 +47,7 @@ static void show_one(const char *refname, const struct object_id *oid) } static int show_ref(const char *refname, const struct object_id *oid, - int flag, void *cbdata) + int flag UNUSED, void *cbdata UNUSED) { if (show_head && !strcmp(refname, "HEAD")) goto match; @@ -77,8 +77,9 @@ match: return 0; } -static int add_existing(const char *refname, const struct object_id *oid, - int flag, void *cbdata) +static int add_existing(const char *refname, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cbdata) { struct string_list *list = (struct string_list *)cbdata; string_list_insert(list, refname); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index f91e29b56a..287716db68 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -48,7 +48,7 @@ static char const * const builtin_sparse_checkout_list_usage[] = { NULL }; -static int sparse_checkout_list(int argc, const char **argv) +static int sparse_checkout_list(int argc, const char **argv, const char *prefix) { static struct option builtin_sparse_checkout_list_options[] = { OPT_END(), @@ -60,7 +60,7 @@ static int sparse_checkout_list(int argc, const char **argv) if (!core_apply_sparse_checkout) die(_("this worktree is not sparse")); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_list_options, builtin_sparse_checkout_list_usage, 0); @@ -431,7 +431,7 @@ static struct sparse_checkout_init_opts { int sparse_index; } init_opts; -static int sparse_checkout_init(int argc, const char **argv) +static int sparse_checkout_init(int argc, const char **argv, const char *prefix) { struct pattern_list pl; char *sparse_filename; @@ -452,7 +452,7 @@ static int sparse_checkout_init(int argc, const char **argv) init_opts.cone_mode = -1; init_opts.sparse_index = -1; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_init_options, builtin_sparse_checkout_init_usage, 0); @@ -767,7 +767,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_add_options, builtin_sparse_checkout_add_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); sanitize_paths(argc, argv, prefix, add_opts.skip_checks); @@ -813,7 +813,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_set_options, builtin_sparse_checkout_set_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index)) return 1; @@ -843,7 +843,8 @@ static struct sparse_checkout_reapply_opts { int sparse_index; } reapply_opts; -static int sparse_checkout_reapply(int argc, const char **argv) +static int sparse_checkout_reapply(int argc, const char **argv, + const char *prefix) { static struct option builtin_sparse_checkout_reapply_options[] = { OPT_BOOL(0, "cone", &reapply_opts.cone_mode, @@ -859,7 +860,7 @@ static int sparse_checkout_reapply(int argc, const char **argv) reapply_opts.cone_mode = -1; reapply_opts.sparse_index = -1; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_reapply_options, builtin_sparse_checkout_reapply_usage, 0); @@ -876,7 +877,8 @@ static char const * const builtin_sparse_checkout_disable_usage[] = { NULL }; -static int sparse_checkout_disable(int argc, const char **argv) +static int sparse_checkout_disable(int argc, const char **argv, + const char *prefix) { static struct option builtin_sparse_checkout_disable_options[] = { OPT_END(), @@ -895,7 +897,7 @@ static int sparse_checkout_disable(int argc, const char **argv) * forcibly return to a dense checkout regardless of initial state. */ - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_disable_options, builtin_sparse_checkout_disable_usage, 0); @@ -922,39 +924,25 @@ static int sparse_checkout_disable(int argc, const char **argv) int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) { - static struct option builtin_sparse_checkout_options[] = { + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_sparse_checkout_options[] = { + OPT_SUBCOMMAND("list", &fn, sparse_checkout_list), + OPT_SUBCOMMAND("init", &fn, sparse_checkout_init), + OPT_SUBCOMMAND("set", &fn, sparse_checkout_set), + OPT_SUBCOMMAND("add", &fn, sparse_checkout_add), + OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply), + OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable), OPT_END(), }; - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); - argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_options, - builtin_sparse_checkout_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + builtin_sparse_checkout_usage, 0); git_config(git_default_config, NULL); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - if (argc > 0) { - if (!strcmp(argv[0], "list")) - return sparse_checkout_list(argc, argv); - if (!strcmp(argv[0], "init")) - return sparse_checkout_init(argc, argv); - if (!strcmp(argv[0], "set")) - return sparse_checkout_set(argc, argv, prefix); - if (!strcmp(argv[0], "add")) - return sparse_checkout_add(argc, argv, prefix); - if (!strcmp(argv[0], "reapply")) - return sparse_checkout_reapply(argc, argv); - if (!strcmp(argv[0], "disable")) - return sparse_checkout_disable(argc, argv); - } - - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); + return fn(argc, argv, prefix); } diff --git a/builtin/stash.c b/builtin/stash.c index 30fa101460..2274aae255 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -638,9 +638,12 @@ cleanup: return ret; } -static int reject_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) +static int reject_reflog_ent(struct object_id *ooid UNUSED, + struct object_id *noid UNUSED, + const char *email UNUSED, + timestamp_t timestamp UNUSED, + int tz UNUSED, const char *message UNUSED, + void *cb_data UNUSED) { return 1; } @@ -782,7 +785,7 @@ static int list_stash(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_stash_list_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (!ref_exists(ref_stash)) return 0; @@ -873,7 +876,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) init_revisions(&rev, prefix); argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); strvec_push(&revision_args, argv[0]); @@ -979,7 +982,7 @@ static int store_stash(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_stash_store_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (argc != 1) { if (!quiet) @@ -1739,6 +1742,11 @@ static int push_stash(int argc, const char **argv, const char *prefix, include_untracked, only_staged); } +static int push_stash_unassumed(int argc, const char **argv, const char *prefix) +{ + return push_stash(argc, argv, prefix, 0); +} + static int save_stash(int argc, const char **argv, const char *prefix) { int keep_index = -1; @@ -1787,15 +1795,28 @@ int cmd_stash(int argc, const char **argv, const char *prefix) pid_t pid = getpid(); const char *index_file; struct strvec args = STRVEC_INIT; - + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("apply", &fn, apply_stash), + OPT_SUBCOMMAND("clear", &fn, clear_stash), + OPT_SUBCOMMAND("drop", &fn, drop_stash), + OPT_SUBCOMMAND("pop", &fn, pop_stash), + OPT_SUBCOMMAND("branch", &fn, branch_stash), + OPT_SUBCOMMAND("list", &fn, list_stash), + OPT_SUBCOMMAND("show", &fn, show_stash), + OPT_SUBCOMMAND("store", &fn, store_stash), + OPT_SUBCOMMAND("create", &fn, create_stash), + OPT_SUBCOMMAND("push", &fn, push_stash_unassumed), + OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; git_config(git_stash_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, - PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); + PARSE_OPT_SUBCOMMAND_OPTIONAL | + PARSE_OPT_KEEP_UNKNOWN_OPT | + PARSE_OPT_KEEP_DASHDASH); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1804,33 +1825,10 @@ int cmd_stash(int argc, const char **argv, const char *prefix) strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, (uintmax_t)pid); - if (!argc) - return !!push_stash(0, NULL, prefix, 0); - else if (!strcmp(argv[0], "apply")) - return !!apply_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "clear")) - return !!clear_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "drop")) - return !!drop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "pop")) - return !!pop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "branch")) - return !!branch_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "list")) - return !!list_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - return !!show_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "store")) - return !!store_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "create")) - return !!create_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "push")) - return !!push_stash(argc, argv, prefix, 0); - else if (!strcmp(argv[0], "save")) - return !!save_stash(argc, argv, prefix); - else if (*argv[0] != '-') - usage_msg_optf(_("unknown subcommand: %s"), - git_stash_usage, options, argv[0]); + if (fn) + return !!fn(argc, argv, prefix); + else if (!argc) + return !!push_stash_unassumed(0, NULL, prefix); /* Assume 'stash push' */ strvec_push(&args, "push"); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index b63f420ece..0b4acb442b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -31,45 +31,61 @@ typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); -static char *repo_get_default_remote(struct repository *repo) +static int repo_get_default_remote(struct repository *repo, char **default_remote) { - char *dest = NULL, *ret; + char *dest = NULL; struct strbuf sb = STRBUF_INIT; struct ref_store *store = get_main_ref_store(repo); const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL, NULL); if (!refname) - die(_("No such ref: %s"), "HEAD"); + return die_message(_("No such ref: %s"), "HEAD"); /* detached HEAD */ - if (!strcmp(refname, "HEAD")) - return xstrdup("origin"); + if (!strcmp(refname, "HEAD")) { + *default_remote = xstrdup("origin"); + return 0; + } if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); + return die_message(_("Expecting a full ref name, got %s"), + refname); strbuf_addf(&sb, "branch.%s.remote", refname); if (repo_config_get_string(repo, sb.buf, &dest)) - ret = xstrdup("origin"); + *default_remote = xstrdup("origin"); else - ret = dest; + *default_remote = dest; strbuf_release(&sb); - return ret; + return 0; } -static char *get_default_remote_submodule(const char *module_path) +static int get_default_remote_submodule(const char *module_path, char **default_remote) { struct repository subrepo; + int ret; + + if (repo_submodule_init(&subrepo, the_repository, module_path, + null_oid()) < 0) + return die_message(_("could not get a repository handle for submodule '%s'"), + module_path); + ret = repo_get_default_remote(&subrepo, default_remote); + repo_clear(&subrepo); - repo_submodule_init(&subrepo, the_repository, module_path, null_oid()); - return repo_get_default_remote(&subrepo); + return ret; } static char *get_default_remote(void) { - return repo_get_default_remote(the_repository); + char *default_remote; + int code = repo_get_default_remote(the_repository, &default_remote); + + if (code) + exit(code); + + return default_remote; } static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet) @@ -96,28 +112,6 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int return resolved_url; } -static int resolve_relative_url_test(int argc, const char **argv, const char *prefix) -{ - char *remoteurl, *res; - const char *up_path, *url; - - if (argc != 4) - die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>"); - - up_path = argv[1]; - remoteurl = xstrdup(argv[2]); - url = argv[3]; - - if (!strcmp(up_path, "(null)")) - up_path = NULL; - - res = relative_url(remoteurl, url, up_path); - puts(res); - free(res); - free(remoteurl); - return 0; -} - /* the result should be freed by the caller. */ static char *get_submodule_displaypath(const char *path, const char *prefix) { @@ -182,6 +176,11 @@ struct module_list { }; #define MODULE_LIST_INIT { 0 } +static void module_list_release(struct module_list *ml) +{ + free(ml->entries); +} + static int module_list_compute(int argc, const char **argv, const char *prefix, struct pathspec *pathspec, @@ -189,6 +188,7 @@ static int module_list_compute(int argc, const char **argv, { int i, result = 0; char *ps_matched = NULL; + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); @@ -243,7 +243,7 @@ static void module_list_active(struct module_list *list) active_modules.entries[active_modules.nr++] = ce; } - free(list->entries); + module_list_release(list); *list = active_modules; } @@ -266,49 +266,11 @@ static char *get_up_path(const char *path) return strbuf_detach(&sb, NULL); } -static int module_list(int argc, const char **argv, const char *prefix) -{ - int i; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - - struct option module_list_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("alternative anchor for relative paths")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper list [--prefix=<path>] [<path>...]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_list_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - for (i = 0; i < list.nr; i++) { - const struct cache_entry *ce = list.entries[i]; - - if (ce_stage(ce)) - printf("%06o %s U\t", ce->ce_mode, - oid_to_hex(null_oid())); - else - printf("%06o %s %d\t", ce->ce_mode, - oid_to_hex(&ce->oid), ce_stage(ce)); - - fprintf(stdout, "%s\n", ce->name); - } - return 0; -} - static void for_each_listed_submodule(const struct module_list *list, each_submodule_fn fn, void *cb_data) { int i; + for (i = 0; i < list->nr; i++) fn(list->entries[i], cb_data); } @@ -328,7 +290,6 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, struct foreach_cb *info = cb_data; const char *path = list_item->name; const struct object_id *ce_oid = &list_item->oid; - const struct submodule *sub; struct child_process cp = CHILD_PROCESS_INIT; char *displaypath; @@ -427,26 +388,25 @@ cleanup: static int module_foreach(int argc, const char **argv, const char *prefix) { struct foreach_cb info = FOREACH_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; - struct option module_foreach_options[] = { OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")), OPT_BOOL(0, "recursive", &info.recursive, N_("recurse into nested submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule foreach [--quiet] [--recursive] [--] <command>"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_foreach_options, git_submodule_helper_usage, 0); if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.argc = argc; info.argv = argv; @@ -454,7 +414,11 @@ static int module_foreach(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } static int starts_with_dot_slash(const char *const path) @@ -480,7 +444,8 @@ static void init_submodule(const char *path, const char *prefix, { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; - char *upd = NULL, *url = NULL, *displaypath; + const char *upd; + char *url = NULL, *displaypath; displaypath = get_submodule_displaypath(path, prefix); @@ -519,6 +484,7 @@ static void init_submodule(const char *path, const char *prefix, if (starts_with_dot_dot_slash(url) || starts_with_dot_slash(url)) { char *oldurl = url; + url = resolve_relative_url(oldurl, NULL, 0); free(oldurl); } @@ -535,14 +501,15 @@ static void init_submodule(const char *path, const char *prefix, /* Copy "update" setting when it is not set yet */ strbuf_addf(&sb, "submodule.%s.update", sub->name); - if (git_config_get_string(sb.buf, &upd) && + if (git_config_get_string_tmp(sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { if (sub->update_strategy.type == SM_UPDATE_COMMAND) { fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"), sub->name); - upd = xstrdup("none"); - } else - upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy)); + upd = "none"; + } else { + upd = submodule_update_type_to_string(sub->update_strategy.type); + } if (git_config_set_gently(sb.buf, upd)) die(_("Failed to register update mode for submodule path '%s'"), displaypath); @@ -550,37 +517,36 @@ static void init_submodule(const char *path, const char *prefix, strbuf_release(&sb); free(displaypath); free(url); - free(upd); } static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->flags); } static int module_init(int argc, const char **argv, const char *prefix) { struct init_cb info = INIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - struct option module_init_options[] = { OPT__QUIET(&quiet, N_("suppress output for initializing a submodule")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule init [<options>] [<path>]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_init_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; /* * If there are no path args and submodule.active is set then, @@ -595,7 +561,11 @@ static int module_init(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, init_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct status_cb { @@ -613,20 +583,23 @@ static void print_status(unsigned int flags, char state, const char *path, printf("%c%s %s", state, oid_to_hex(oid), displaypath); if (state == ' ' || state == '+') { - const char *name = compute_rev_name(path, oid_to_hex(oid)); + char *name = compute_rev_name(path, oid_to_hex(oid)); if (name) printf(" (%s)", name); + free(name); } printf("\n"); } -static int handle_submodule_head_ref(const char *refname, - const struct object_id *oid, int flags, +static int handle_submodule_head_ref(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct object_id *output = cb_data; + if (oid) oidcpy(output, oid); @@ -733,6 +706,7 @@ static void status_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct status_cb *info = cb_data; + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, info->prefix, info->flags); } @@ -740,27 +714,26 @@ static void status_submodule_cb(const struct cache_entry *list_item, static int module_status(int argc, const char **argv, const char *prefix) { struct status_cb info = STATUS_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - struct option module_status_options[] = { OPT__QUIET(&quiet, N_("suppress submodule status output")), OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_status_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -768,25 +741,11 @@ static int module_status(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, status_submodule_cb, &info); - return 0; -} - -static int module_name(int argc, const char **argv, const char *prefix) -{ - const struct submodule *sub; - - if (argc != 2) - usage(_("git submodule--helper name <path>")); - - sub = submodule_from_path(the_repository, null_oid(), argv[1]); - - if (!sub) - die(_("no submodule mapping found in .gitmodules for path '%s'"), - argv[1]); - - printf("%s\n", sub->name); - - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct module_cb { @@ -795,16 +754,34 @@ struct module_cb { struct object_id oid_src; struct object_id oid_dst; char status; - const char *sm_path; + char *sm_path; }; #define MODULE_CB_INIT { 0 } +static void module_cb_release(struct module_cb *mcb) +{ + free(mcb->sm_path); +} + struct module_cb_list { struct module_cb **entries; int alloc, nr; }; #define MODULE_CB_LIST_INIT { 0 } +static void module_cb_list_release(struct module_cb_list *mcbl) +{ + int i; + + for (i = 0; i < mcbl->nr; i++) { + struct module_cb *mcb = mcbl->entries[i]; + + module_cb_release(mcb); + free(mcb); + } + free(mcbl->entries); +} + struct summary_cb { int argc; const char **argv; @@ -841,7 +818,7 @@ static char *verify_submodule_committish(const char *sm_path, return strbuf_detach(&result, NULL); } -static void print_submodule_summary(struct summary_cb *info, char *errmsg, +static void print_submodule_summary(struct summary_cb *info, const char *errmsg, int total_commits, const char *displaypath, const char *src_abbrev, const char *dst_abbrev, struct module_cb *p) @@ -899,12 +876,13 @@ static void generate_submodule_summary(struct summary_cb *info, { char *displaypath, *src_abbrev = NULL, *dst_abbrev; int missing_src = 0, missing_dst = 0; - char *errmsg = NULL; + struct strbuf errmsg = STRBUF_INIT; int total_commits = -1; if (!info->cached && oideq(&p->oid_dst, null_oid())) { if (S_ISGITLINK(p->mod_dst)) { struct ref_store *refs = get_submodule_ref_store(p->sm_path); + if (refs) refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst); } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) { @@ -999,28 +977,27 @@ static void generate_submodule_summary(struct summary_cb *info, * submodule, i.e., deleted or changed to blob */ if (S_ISGITLINK(p->mod_dst)) { - struct strbuf errmsg_str = STRBUF_INIT; if (missing_src && missing_dst) { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n", + strbuf_addf(&errmsg, " Warn: %s doesn't contain commits %s and %s\n", displaypath, oid_to_hex(&p->oid_src), oid_to_hex(&p->oid_dst)); } else { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n", + strbuf_addf(&errmsg, " Warn: %s doesn't contain commit %s\n", displaypath, missing_src ? oid_to_hex(&p->oid_src) : oid_to_hex(&p->oid_dst)); } - errmsg = strbuf_detach(&errmsg_str, NULL); } } - print_submodule_summary(info, errmsg, total_commits, - displaypath, src_abbrev, + print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL, + total_commits, displaypath, src_abbrev, dst_abbrev, p); free(displaypath); free(src_abbrev); free(dst_abbrev); + strbuf_release(&errmsg); } static void prepare_submodule_summary(struct summary_cb *info, @@ -1151,6 +1128,7 @@ static int compute_summary_module_list(struct object_id *head_oid, cleanup: strvec_clear(&diff_args); release_revisions(&rev); + module_cb_list_release(&list); return ret; } @@ -1164,7 +1142,6 @@ static int module_summary(int argc, const char **argv, const char *prefix) enum diff_cmd diff_cmd = DIFF_INDEX; struct object_id head_oid; int ret; - struct option module_summary_options[] = { OPT_BOOL(0, "cached", &cached, N_("use the commit stored in the index instead of the submodule HEAD")), @@ -1176,7 +1153,6 @@ static int module_summary(int argc, const char **argv, const char *prefix) N_("limit the summary size")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule summary [<options>] [<commit>] [--] [<path>]"), NULL @@ -1238,6 +1214,7 @@ static void sync_submodule(const char *path, const char *prefix, char *sub_origin_url, *super_config_url, *displaypath, *default_remote; struct strbuf sb = STRBUF_INIT; char *sub_config_path = NULL; + int code; if (!is_submodule_active(the_repository, path)) return; @@ -1248,6 +1225,7 @@ static void sync_submodule(const char *path, const char *prefix, if (starts_with_dot_dot_slash(sub->url) || starts_with_dot_slash(sub->url)) { char *up_path = get_up_path(path); + sub_origin_url = resolve_relative_url(sub->url, up_path, 1); super_config_url = resolve_relative_url(sub->url, NULL, 1); free(up_path); @@ -1276,10 +1254,9 @@ static void sync_submodule(const char *path, const char *prefix, goto cleanup; strbuf_reset(&sb); - default_remote = get_default_remote_submodule(path); - if (!default_remote) - die(_("failed to get the default remote for submodule '%s'"), - path); + code = get_default_remote_submodule(path, &default_remote); + if (code) + exit(code); remote_key = xstrfmt("remote.%s.url", default_remote); free(default_remote); @@ -1323,34 +1300,34 @@ cleanup: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct sync_cb *info = cb_data; + sync_submodule(list_item->name, info->prefix, info->flags); } static int module_sync(int argc, const char **argv, const char *prefix) { struct sync_cb info = SYNC_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int recursive = 0; - struct option module_sync_options[] = { OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")), OPT_BOOL(0, "recursive", &recursive, N_("recurse into nested submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule sync [--quiet] [--recursive] [<path>]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_sync_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -1360,7 +1337,11 @@ static int module_sync(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, sync_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct deinit_cb { @@ -1404,6 +1385,7 @@ static void deinit_submodule(const char *path, const char *prefix, if (!(flags & OPT_FORCE)) { struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; strvec_pushl(&cp_rm.args, "rm", "-qn", path, NULL); @@ -1440,6 +1422,7 @@ static void deinit_submodule(const char *path, const char *prefix, /* remove the .git/config entries (unless the user already did it) */ if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) { char *sub_key = xstrfmt("submodule.%s", sub->name); + /* * remove the whole section so we have a clean state when * the user later decides to init this submodule again @@ -1467,23 +1450,22 @@ static void deinit_submodule_cb(const struct cache_entry *list_item, static int module_deinit(int argc, const char **argv, const char *prefix) { struct deinit_cb info = DEINIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int force = 0; int all = 0; - struct option module_deinit_options[] = { OPT__QUIET(&quiet, N_("suppress submodule status output")), OPT__FORCE(&force, N_("remove submodule working trees even if they contain local changes"), 0), OPT_BOOL(0, "all", &all, N_("unregister all submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_deinit_options, git_submodule_helper_usage, 0); @@ -1498,7 +1480,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) die(_("Use '--all' if you really want to deinitialize all submodules")); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; info.prefix = prefix; if (quiet) @@ -1508,7 +1490,11 @@ static int module_deinit(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, deinit_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct module_clone_data { @@ -1518,7 +1504,6 @@ struct module_clone_data { const char *url; const char *depth; struct list_objects_filter_options *filter_options; - struct string_list reference; unsigned int quiet: 1; unsigned int progress: 1; unsigned int dissociate: 1; @@ -1526,7 +1511,6 @@ struct module_clone_data { int single_branch; }; #define MODULE_CLONE_DATA_INIT { \ - .reference = STRING_LIST_INIT_NODUP, \ .single_branch = -1, \ } @@ -1567,7 +1551,9 @@ static int add_possible_reference_from_superproject( struct strbuf err = STRBUF_INIT; strbuf_add(&sb, odb->path, len); - repo_init(&alternate, sb.buf, NULL); + if (repo_init(&alternate, sb.buf, NULL) < 0) + die(_("could not get a repository handle for gitdir '%s'"), + sb.buf); /* * We need to end the new path with '/' to mark it as a dir, @@ -1582,7 +1568,9 @@ static int add_possible_reference_from_superproject( sm_alternate = compute_alternate_path(sb.buf, &err); if (sm_alternate) { - string_list_append(sas->reference, xstrdup(sb.buf)); + char *p = strbuf_detach(&sb, NULL); + + string_list_append(sas->reference, p)->util = p; free(sm_alternate); } else { switch (sas->error_mode) { @@ -1641,23 +1629,31 @@ static void prepare_possible_alternates(const char *sm_name, free(error_strategy); } -static int clone_submodule(struct module_clone_data *clone_data) +static char *clone_submodule_sm_gitdir(const char *name) { - char *p, *sm_gitdir; - char *sm_alternate = NULL, *error_strategy = NULL; struct strbuf sb = STRBUF_INIT; - struct child_process cp = CHILD_PROCESS_INIT; + char *sm_gitdir; - submodule_name_to_gitdir(&sb, the_repository, clone_data->name); + submodule_name_to_gitdir(&sb, the_repository, name); sm_gitdir = absolute_pathdup(sb.buf); - strbuf_reset(&sb); + strbuf_release(&sb); - if (!is_absolute_path(clone_data->path)) { - strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path); - clone_data->path = strbuf_detach(&sb, NULL); - } else { - clone_data->path = xstrdup(clone_data->path); - } + return sm_gitdir; +} + +static int clone_submodule(const struct module_clone_data *clone_data, + struct string_list *reference) +{ + char *p; + char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name); + char *sm_alternate = NULL, *error_strategy = NULL; + struct child_process cp = CHILD_PROCESS_INIT; + const char *clone_data_path = clone_data->path; + char *to_free = NULL; + + if (!is_absolute_path(clone_data->path)) + clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(), + clone_data->path); if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) die(_("refusing to create/use '%s' in another submodule's " @@ -1667,7 +1663,7 @@ static int clone_submodule(struct module_clone_data *clone_data) if (safe_create_leading_directories_const(sm_gitdir) < 0) die(_("could not create directory '%s'"), sm_gitdir); - prepare_possible_alternates(clone_data->name, &clone_data->reference); + prepare_possible_alternates(clone_data->name, reference); strvec_push(&cp.args, "clone"); strvec_push(&cp.args, "--no-checkout"); @@ -1677,9 +1673,10 @@ static int clone_submodule(struct module_clone_data *clone_data) strvec_push(&cp.args, "--progress"); if (clone_data->depth && *(clone_data->depth)) strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL); - if (clone_data->reference.nr) { + if (reference->nr) { struct string_list_item *item; - for_each_string_list_item(item, &clone_data->reference) + + for_each_string_list_item(item, reference) strvec_pushl(&cp.args, "--reference", item->string, NULL); } @@ -1698,7 +1695,7 @@ static int clone_submodule(struct module_clone_data *clone_data) strvec_push(&cp.args, "--"); strvec_push(&cp.args, clone_data->url); - strvec_push(&cp.args, clone_data->path); + strvec_push(&cp.args, clone_data_path); cp.git_cmd = 1; prepare_submodule_repo_env(&cp.env); @@ -1706,23 +1703,25 @@ static int clone_submodule(struct module_clone_data *clone_data) if(run_command(&cp)) die(_("clone of '%s' into submodule path '%s' failed"), - clone_data->url, clone_data->path); + clone_data->url, clone_data_path); } else { - if (clone_data->require_init && !access(clone_data->path, X_OK) && - !is_empty_dir(clone_data->path)) - die(_("directory not empty: '%s'"), clone_data->path); - if (safe_create_leading_directories_const(clone_data->path) < 0) - die(_("could not create directory '%s'"), clone_data->path); - strbuf_addf(&sb, "%s/index", sm_gitdir); - unlink_or_warn(sb.buf); - strbuf_reset(&sb); + char *path; + + if (clone_data->require_init && !access(clone_data_path, X_OK) && + !is_empty_dir(clone_data_path)) + die(_("directory not empty: '%s'"), clone_data_path); + if (safe_create_leading_directories_const(clone_data_path) < 0) + die(_("could not create directory '%s'"), clone_data_path); + path = xstrfmt("%s/index", sm_gitdir); + unlink_or_warn(path); + free(path); } - connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0); + connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0); - p = git_pathdup_submodule(clone_data->path, "config"); + p = git_pathdup_submodule(clone_data_path, "config"); if (!p) - die(_("could not get submodule directory for '%s'"), clone_data->path); + die(_("could not get submodule directory for '%s'"), clone_data_path); /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */ git_config_get_string("submodule.alternateLocation", &sm_alternate); @@ -1737,9 +1736,9 @@ static int clone_submodule(struct module_clone_data *clone_data) free(sm_alternate); free(error_strategy); - strbuf_release(&sb); free(sm_gitdir); free(p); + free(to_free); return 0; } @@ -1747,7 +1746,9 @@ static int module_clone(int argc, const char **argv, const char *prefix) { int dissociate = 0, quiet = 0, progress = 0, require_init = 0; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; - struct list_objects_filter_options filter_options; + struct string_list reference = STRING_LIST_INIT_NODUP; + struct list_objects_filter_options filter_options = + LIST_OBJECTS_FILTER_INIT; struct option module_clone_options[] = { OPT_STRING(0, "prefix", &clone_data.prefix, @@ -1762,7 +1763,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_STRING(0, "url", &clone_data.url, N_("string"), N_("url where to clone the submodule from")), - OPT_STRING_LIST(0, "reference", &clone_data.reference, + OPT_STRING_LIST(0, "reference", &reference, N_("repo"), N_("reference repository")), OPT_BOOL(0, "dissociate", &dissociate, @@ -1780,7 +1781,6 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule--helper clone [--prefix=<path>] [--quiet] " "[--reference <repository>] [--name <name>] [--depth <depth>] " @@ -1789,7 +1789,6 @@ static int module_clone(int argc, const char **argv, const char *prefix) NULL }; - memset(&filter_options, 0, sizeof(filter_options)); argc = parse_options(argc, argv, prefix, module_clone_options, git_submodule_helper_usage, 0); @@ -1803,29 +1802,33 @@ static int module_clone(int argc, const char **argv, const char *prefix) usage_with_options(git_submodule_helper_usage, module_clone_options); - clone_submodule(&clone_data); + clone_submodule(&clone_data, &reference); list_objects_filter_release(&filter_options); + string_list_clear(&reference, 1); return 0; } -static void determine_submodule_update_strategy(struct repository *r, - int just_cloned, - const char *path, - enum submodule_update_type update, - struct submodule_update_strategy *out) +static int determine_submodule_update_strategy(struct repository *r, + int just_cloned, + const char *path, + enum submodule_update_type update, + struct submodule_update_strategy *out) { const struct submodule *sub = submodule_from_path(r, null_oid(), path); char *key; const char *val; + int ret; key = xstrfmt("submodule.%s.update", sub->name); if (update) { out->type = update; } else if (!repo_config_get_string_tmp(r, key, &val)) { - if (parse_submodule_update_strategy(val, out) < 0) - die(_("Invalid update mode '%s' configured for submodule path '%s'"), - val, path); + if (parse_submodule_update_strategy(val, out) < 0) { + ret = die_message(_("Invalid update mode '%s' configured for submodule path '%s'"), + val, path); + goto cleanup; + } } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { if (sub->update_strategy.type == SM_UPDATE_COMMAND) BUG("how did we read update = !command from .gitmodules?"); @@ -1840,7 +1843,10 @@ static void determine_submodule_update_strategy(struct repository *r, out->type == SM_UPDATE_NONE)) out->type = SM_UPDATE_CHECKOUT; + ret = 0; +cleanup: free(key); + return ret; } struct update_clone_data { @@ -1854,7 +1860,7 @@ struct submodule_update_clone { int current; /* configuration parameters which are passed on to the children */ - struct update_data *update_data; + const struct update_data *update_data; /* to be consumed by update_submodule() */ struct update_clone_data *update_clone; @@ -1869,9 +1875,15 @@ struct submodule_update_clone { }; #define SUBMODULE_UPDATE_CLONE_INIT { 0 } +static void submodule_update_clone_release(struct submodule_update_clone *suc) +{ + free(suc->update_clone); + free(suc->failed_clones); +} + struct update_data { const char *prefix; - const char *displaypath; + char *displaypath; enum submodule_update_type update_default; struct object_id suboid; struct string_list references; @@ -1907,6 +1919,12 @@ struct update_data { .max_jobs = 1, \ } +static void update_data_release(struct update_data *ud) +{ + free(ud->displaypath); + module_list_release(&ud->list); +} + static void next_submodule_warn_missing(struct submodule_update_clone *suc, struct strbuf *out, const char *displaypath) { @@ -1939,7 +1957,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, const char *update_string; enum submodule_update_type update_type; char *key; - struct update_data *ud = suc->update_data; + const struct update_data *ud = suc->update_data; char *displaypath = get_submodule_displaypath(ce->name, ud->prefix); struct strbuf sb = STRBUF_INIT; int needs_cloning = 0; @@ -2031,6 +2049,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, strvec_pushl(&child->args, "--url", url, NULL); if (suc->update_data->references.nr) { struct string_list_item *item; + for_each_string_list_item(item, &suc->update_data->references) strvec_pushl(&child->args, "--reference", item->string, NULL); } @@ -2063,6 +2082,7 @@ static int update_clone_get_next_task(struct child_process *child, ce = suc->update_data->list.entries[suc->current]; if (prepare_to_clone_next_submodule(ce, child, suc, err)) { int *p = xmalloc(sizeof(*p)); + *p = suc->current; *idx_task_cb = p; suc->current++; @@ -2078,6 +2098,7 @@ static int update_clone_get_next_task(struct child_process *child, index = suc->current - suc->update_data->list.nr; if (index < suc->failed_clones_nr) { int *p; + ce = suc->failed_clones[index]; if (!prepare_to_clone_next_submodule(ce, child, suc, err)) { suc->current ++; @@ -2101,6 +2122,7 @@ static int update_clone_start_failure(struct strbuf *err, void *idx_task_cb) { struct submodule_update_clone *suc = suc_cb; + suc->quickstop = 1; return 1; } @@ -2112,9 +2134,9 @@ static int update_clone_task_finished(int result, { const struct cache_entry *ce; struct submodule_update_clone *suc = suc_cb; - int *idxP = idx_task_cb; int idx = *idxP; + free(idxP); if (!result) @@ -2147,19 +2169,20 @@ static int git_update_clone_config(const char *var, const char *value, void *cb) { int *max_jobs = cb; + if (!strcmp(var, "submodule.fetchjobs")) *max_jobs = parse_submodule_fetchjobs(var, value); return 0; } -static int is_tip_reachable(const char *path, struct object_id *oid) +static int is_tip_reachable(const char *path, const struct object_id *oid) { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf rev = STRBUF_INIT; char *hex = oid_to_hex(oid); cp.git_cmd = 1; - cp.dir = xstrdup(path); + cp.dir = path; cp.no_stderr = 1; strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL); @@ -2171,13 +2194,14 @@ static int is_tip_reachable(const char *path, struct object_id *oid) return 1; } -static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid) +static int fetch_in_submodule(const char *module_path, int depth, int quiet, + const struct object_id *oid) { struct child_process cp = CHILD_PROCESS_INIT; prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; - cp.dir = xstrdup(module_path); + cp.dir = module_path; strvec_push(&cp.args, "fetch"); if (quiet) @@ -2187,6 +2211,7 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str if (oid) { char *hex = oid_to_hex(oid); char *remote = get_default_remote(); + strvec_pushl(&cp.args, remote, hex, NULL); free(remote); } @@ -2194,11 +2219,11 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str return run_command(&cp); } -static int run_update_command(struct update_data *ud, int subforce) +static int run_update_command(const struct update_data *ud, int subforce) { struct child_process cp = CHILD_PROCESS_INIT; char *oid = oid_to_hex(&ud->oid); - int must_die_on_failure = 0; + int ret; switch (ud->update_strategy.type) { case SM_UPDATE_CHECKOUT: @@ -2212,55 +2237,50 @@ static int run_update_command(struct update_data *ud, int subforce) strvec_push(&cp.args, "rebase"); if (ud->quiet) strvec_push(&cp.args, "--quiet"); - must_die_on_failure = 1; break; case SM_UPDATE_MERGE: cp.git_cmd = 1; strvec_push(&cp.args, "merge"); if (ud->quiet) strvec_push(&cp.args, "--quiet"); - must_die_on_failure = 1; break; case SM_UPDATE_COMMAND: cp.use_shell = 1; strvec_push(&cp.args, ud->update_strategy.command); - must_die_on_failure = 1; break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } strvec_push(&cp.args, oid); - cp.dir = xstrdup(ud->sm_path); + cp.dir = ud->sm_path; prepare_submodule_repo_env(&cp.env); - if (run_command(&cp)) { + if ((ret = run_command(&cp))) { switch (ud->update_strategy.type) { case SM_UPDATE_CHECKOUT: die_message(_("Unable to checkout '%s' in submodule path '%s'"), oid, ud->displaypath); + /* No "ret" assignment, use "git checkout"'s */ break; case SM_UPDATE_REBASE: - die_message(_("Unable to rebase '%s' in submodule path '%s'"), - oid, ud->displaypath); + ret = die_message(_("Unable to rebase '%s' in submodule path '%s'"), + oid, ud->displaypath); break; case SM_UPDATE_MERGE: - die_message(_("Unable to merge '%s' in submodule path '%s'"), - oid, ud->displaypath); + ret = die_message(_("Unable to merge '%s' in submodule path '%s'"), + oid, ud->displaypath); break; case SM_UPDATE_COMMAND: - die_message(_("Execution of '%s %s' failed in submodule path '%s'"), - ud->update_strategy.command, oid, ud->displaypath); + ret = die_message(_("Execution of '%s %s' failed in submodule path '%s'"), + ud->update_strategy.command, oid, ud->displaypath); break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } - if (must_die_on_failure) - exit(128); - /* the command failed, but update must continue */ - return 1; + return ret; } if (ud->quiet) @@ -2284,14 +2304,14 @@ static int run_update_command(struct update_data *ud, int subforce) ud->displaypath, ud->update_strategy.command, oid); break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } return 0; } -static int run_update_procedure(struct update_data *ud) +static int run_update_procedure(const struct update_data *ud) { int subforce = is_null_oid(&ud->suboid) || ud->force; @@ -2313,59 +2333,67 @@ static int run_update_procedure(struct update_data *ud) */ if (!is_tip_reachable(ud->sm_path, &ud->oid) && fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid)) - die(_("Fetched in submodule path '%s', but it did not " - "contain %s. Direct fetching of that commit failed."), - ud->displaypath, oid_to_hex(&ud->oid)); + return die_message(_("Fetched in submodule path '%s', but it did not " + "contain %s. Direct fetching of that commit failed."), + ud->displaypath, oid_to_hex(&ud->oid)); } return run_update_command(ud, subforce); } -static const char *remote_submodule_branch(const char *path) +static int remote_submodule_branch(const char *path, const char **branch) { const struct submodule *sub; - const char *branch = NULL; char *key; + *branch = NULL; sub = submodule_from_path(the_repository, null_oid(), path); if (!sub) - return NULL; + return die_message(_("could not initialize submodule at path '%s'"), + path); key = xstrfmt("submodule.%s.branch", sub->name); - if (repo_config_get_string_tmp(the_repository, key, &branch)) - branch = sub->branch; + if (repo_config_get_string_tmp(the_repository, key, branch)) + *branch = sub->branch; free(key); - if (!branch) - return "HEAD"; + if (!*branch) { + *branch = "HEAD"; + return 0; + } - if (!strcmp(branch, ".")) { + if (!strcmp(*branch, ".")) { const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); if (!refname) - die(_("No such ref: %s"), "HEAD"); + return die_message(_("No such ref: %s"), "HEAD"); /* detached HEAD */ if (!strcmp(refname, "HEAD")) - die(_("Submodule (%s) branch configured to inherit " - "branch from superproject, but the superproject " - "is not on any branch"), sub->name); + return die_message(_("Submodule (%s) branch configured to inherit " + "branch from superproject, but the superproject " + "is not on any branch"), sub->name); if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); - return refname; + return die_message(_("Expecting a full ref name, got %s"), + refname); + + *branch = refname; + return 0; } - return branch; + /* Our "branch" is coming from repo_config_get_string_tmp() */ + return 0; } -static void ensure_core_worktree(const char *path) +static int ensure_core_worktree(const char *path) { const char *cw; struct repository subrepo; if (repo_submodule_init(&subrepo, the_repository, path, null_oid())) - die(_("could not get a repository handle for submodule '%s'"), path); + return die_message(_("could not get a repository handle for submodule '%s'"), + path); if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) { char *cfg_file, *abs_path; @@ -2383,6 +2411,9 @@ static void ensure_core_worktree(const char *path) free(abs_path); strbuf_release(&sb); } + + repo_clear(&subrepo); + return 0; } static const char *submodule_update_type_to_label(enum submodule_update_type type) @@ -2402,7 +2433,8 @@ static const char *submodule_update_type_to_label(enum submodule_update_type typ BUG("unreachable with type %d", type); } -static void update_data_to_args(struct update_data *update_data, struct strvec *args) +static void update_data_to_args(const struct update_data *update_data, + struct strvec *args) { enum submodule_update_type update_type = update_data->update_default; @@ -2436,6 +2468,7 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * if (update_data->references.nr) { struct string_list_item *item; + for_each_string_list_item(item, &update_data->references) strvec_pushl(args, "--reference", item->string, NULL); } @@ -2455,48 +2488,61 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * static int update_submodule(struct update_data *update_data) { - ensure_core_worktree(update_data->sm_path); - - update_data->displaypath = get_submodule_displaypath( - update_data->sm_path, update_data->prefix); + int ret; - determine_submodule_update_strategy(the_repository, update_data->just_cloned, - update_data->sm_path, update_data->update_default, - &update_data->update_strategy); + ret = determine_submodule_update_strategy(the_repository, + update_data->just_cloned, + update_data->sm_path, + update_data->update_default, + &update_data->update_strategy); + if (ret) + return ret; if (update_data->just_cloned) oidcpy(&update_data->suboid, null_oid()); else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid)) - die(_("Unable to find current revision in submodule path '%s'"), - update_data->displaypath); + return die_message(_("Unable to find current revision in submodule path '%s'"), + update_data->displaypath); if (update_data->remote) { - char *remote_name = get_default_remote_submodule(update_data->sm_path); - const char *branch = remote_submodule_branch(update_data->sm_path); - char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); + char *remote_name; + const char *branch; + char *remote_ref; + int code; + + code = get_default_remote_submodule(update_data->sm_path, &remote_name); + if (code) + return code; + code = remote_submodule_branch(update_data->sm_path, &branch); + if (code) + return code; + remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); + + free(remote_name); if (!update_data->nofetch) { if (fetch_in_submodule(update_data->sm_path, update_data->depth, 0, NULL)) - die(_("Unable to fetch in submodule path '%s'"), - update_data->sm_path); + return die_message(_("Unable to fetch in submodule path '%s'"), + update_data->sm_path); } if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid)) - die(_("Unable to find %s revision in submodule path '%s'"), - remote_ref, update_data->sm_path); + return die_message(_("Unable to find %s revision in submodule path '%s'"), + remote_ref, update_data->sm_path); free(remote_ref); } - if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) - if (run_update_procedure(update_data)) - return 1; + if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) { + ret = run_update_procedure(update_data); + if (ret) + return ret; + } if (update_data->recursive) { struct child_process cp = CHILD_PROCESS_INIT; struct update_data next = *update_data; - int res; next.prefix = NULL; oidcpy(&next.oid, null_oid()); @@ -2507,16 +2553,11 @@ static int update_submodule(struct update_data *update_data) prepare_submodule_repo_env(&cp.env); update_data_to_args(&next, &cp.args); - /* die() if child process die()'d */ - res = run_command(&cp); - if (!res) - return 0; - die_message(_("Failed to recurse into submodule path '%s'"), - update_data->displaypath); - if (res == 128) - exit(res); - else if (res) - return 1; + ret = run_command(&cp); + if (ret) + die_message(_("Failed to recurse into submodule path '%s'"), + update_data->displaypath); + return ret; } return 0; @@ -2524,7 +2565,7 @@ static int update_submodule(struct update_data *update_data) static int update_submodules(struct update_data *update_data) { - int i, res = 0; + int i, ret = 0; struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; suc.update_data = update_data; @@ -2542,33 +2583,48 @@ static int update_submodules(struct update_data *update_data) * - the listener can avoid doing any work if fetching failed. */ if (suc.quickstop) { - res = 1; + ret = 1; goto cleanup; } for (i = 0; i < suc.update_clone_nr; i++) { struct update_clone_data ucd = suc.update_clone[i]; + int code; oidcpy(&update_data->oid, &ucd.oid); update_data->just_cloned = ucd.just_cloned; update_data->sm_path = ucd.sub->path; - if (update_submodule(update_data)) - res = 1; + code = ensure_core_worktree(update_data->sm_path); + if (code) + goto fail; + + update_data->displaypath = get_submodule_displaypath( + update_data->sm_path, update_data->prefix); + code = update_submodule(update_data); + FREE_AND_NULL(update_data->displaypath); +fail: + if (!code) + continue; + ret = code; + if (ret == 128) + goto cleanup; } cleanup: + submodule_update_clone_release(&suc); string_list_clear(&update_data->references, 0); - return res; + return ret; } static int module_update(int argc, const char **argv, const char *prefix) { - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; + struct pathspec pathspec2 = { 0 }; struct update_data opt = UPDATE_DATA_INIT; - struct list_objects_filter_options filter_options; + struct list_objects_filter_options filter_options = + LIST_OBJECTS_FILTER_INIT; int ret; - struct option module_update_options[] = { OPT__FORCE(&opt.force, N_("force checkout updates"), 0), OPT_BOOL(0, "init", &opt.init, @@ -2612,7 +2668,6 @@ static int module_update(int argc, const char **argv, const char *prefix) OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule [--quiet] update" " [--init [--filter=<filter-spec>]] [--remote]" @@ -2626,7 +2681,6 @@ static int module_update(int argc, const char **argv, const char *prefix) update_clone_config_from_gitmodules(&opt.max_jobs); git_config(git_update_clone_config, &opt.max_jobs); - memset(&filter_options, 0, sizeof(filter_options)); argc = parse_options(argc, argv, prefix, module_update_options, git_submodule_helper_usage, 0); @@ -2644,8 +2698,8 @@ static int module_update(int argc, const char **argv, const char *prefix) opt.update_strategy.type = opt.update_default; if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) { - list_objects_filter_release(&filter_options); - return 1; + ret = 1; + goto cleanup; } if (pathspec.nr) @@ -2656,8 +2710,11 @@ static int module_update(int argc, const char **argv, const char *prefix) struct init_cb info = INIT_CB_INIT; if (module_list_compute(argc, argv, opt.prefix, - &pathspec, &list) < 0) - return 1; + &pathspec2, &list) < 0) { + module_list_release(&list); + ret = 1; + goto cleanup; + } /* * If there are no path args and submodule.active is set then, @@ -2671,10 +2728,15 @@ static int module_update(int argc, const char **argv, const char *prefix) info.flags |= OPT_QUIET; for_each_listed_submodule(&list, init_submodule_cb, &info); + module_list_release(&list); } ret = update_submodules(&opt); +cleanup: + update_data_release(&opt); list_objects_filter_release(&filter_options); + clear_pathspec(&pathspec); + clear_pathspec(&pathspec2); return ret; } @@ -2758,10 +2820,9 @@ static int push_check(int argc, const char **argv, const char *prefix) static int absorb_git_dirs(int argc, const char **argv, const char *prefix) { int i; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES; - struct option embed_gitdir_options[] = { OPT_STRING(0, "prefix", &prefix, N_("path"), @@ -2770,53 +2831,26 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) ABSORB_GITDIR_RECURSE_SUBMODULES), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule absorbgitdirs [<options>] [<path>...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, embed_gitdir_options, git_submodule_helper_usage, 0); if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + goto cleanup; for (i = 0; i < list.nr; i++) absorb_git_dir_into_superproject(list.entries[i]->name, flags); - return 0; -} - -static int is_active(int argc, const char **argv, const char *prefix) -{ - if (argc != 2) - die("submodule--helper is-active takes exactly 1 argument"); - - return !is_submodule_active(the_repository, argv[1]); -} - -/* - * Exit non-zero if any of the submodule names given on the command line is - * invalid. If no names are given, filter stdin to print only valid names - * (which is primarily intended for testing). - */ -static int check_name(int argc, const char **argv, const char *prefix) -{ - if (argc > 1) { - while (*++argv) { - if (check_submodule_name(*argv) < 0) - return 1; - } - } else { - struct strbuf buf = STRBUF_INIT; - while (strbuf_getline(&buf, stdin) != EOF) { - if (!check_submodule_name(buf.buf)) - printf("%s\n", buf.buf); - } - strbuf_release(&buf); - } - return 0; + ret = 0; +cleanup: + clear_pathspec(&pathspec); + module_list_release(&list); + return ret; } static int module_config(int argc, const char **argv, const char *prefix) @@ -2825,7 +2859,6 @@ static int module_config(int argc, const char **argv, const char *prefix) CHECK_WRITEABLE = 1, DO_UNSET = 2 } command = 0; - struct option module_config_options[] = { OPT_CMDMODE(0, "check-writeable", &command, N_("check if it is safe to write to the .gitmodules file"), @@ -2871,7 +2904,6 @@ static int module_set_url(int argc, const char **argv, const char *prefix) const char *newurl; const char *path; char *config_name; - struct option options[] = { OPT__QUIET(&quiet, N_("suppress output for setting url of a submodule")), OPT_END() @@ -2902,13 +2934,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) const char *opt_branch = NULL; const char *path; char *config_name; - - /* - * We accept the `quiet` option for uniformity across subcommands, - * though there is nothing to make less verbose in this subcommand. - */ struct option options[] = { + /* + * We accept the `quiet` option for uniformity across subcommands, + * though there is nothing to make less verbose in this subcommand. + */ OPT_NOOP_NOARG('q', "quiet"), + OPT_BOOL('d', "default", &opt_default, N_("set the default tracking branch to master")), OPT_STRING('b', "branch", &opt_branch, N_("branch"), @@ -2943,7 +2975,6 @@ static int module_create_branch(int argc, const char **argv, const char *prefix) { enum branch_track track; int quiet = 0, force = 0, reflog = 0, dry_run = 0; - struct option options[] = { OPT__QUIET(&quiet, N_("print only error messages")), OPT__FORCE(&force, N_("force creation"), 0), @@ -3006,8 +3037,10 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path) if (!capture_command(&cp_remote, &sb_remote_out, 0)) { char *next_line; char *line = sb_remote_out.buf; + while ((next_line = strchr(line, '\n')) != NULL) { size_t len = next_line - line; + if (strip_suffix_mem(line, &len, " (fetch)")) strbuf_addf(msg, " %.*s\n", (int)len, line); line = next_line + 1; @@ -3021,6 +3054,8 @@ static int add_submodule(const struct add_data *add_data) { char *submod_gitdir_path; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; + struct string_list reference = STRING_LIST_INIT_NODUP; + int ret = -1; /* perhaps the path already exists and is already a git repo, else clone it */ if (is_directory(add_data->sm_path)) { @@ -3037,6 +3072,7 @@ static int add_submodule(const struct add_data *add_data) free(submod_gitdir_path); } else { struct child_process cp = CHILD_PROCESS_INIT; + submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name); if (is_directory(submod_gitdir_path)) { @@ -3075,15 +3111,17 @@ static int add_submodule(const struct add_data *add_data) clone_data.url = add_data->realrepo; clone_data.quiet = add_data->quiet; clone_data.progress = add_data->progress; - if (add_data->reference_path) - string_list_append(&clone_data.reference, - xstrdup(add_data->reference_path)); + if (add_data->reference_path) { + char *p = xstrdup(add_data->reference_path); + + string_list_append(&reference, p)->util = p; + } clone_data.dissociate = add_data->dissociate; if (add_data->depth >= 0) clone_data.depth = xstrfmt("%d", add_data->depth); - if (clone_submodule(&clone_data)) - return -1; + if (clone_submodule(&clone_data, &reference)) + goto cleanup; prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; @@ -3102,7 +3140,10 @@ static int add_submodule(const struct add_data *add_data) if (run_command(&cp)) die(_("unable to checkout submodule '%s'"), add_data->sm_path); } - return 0; + ret = 0; +cleanup: + string_list_clear(&reference, 1); + return ret; } static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value) @@ -3123,7 +3164,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con static void configure_added_submodule(struct add_data *add_data) { char *key; - char *val = NULL; + const char *val; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; @@ -3168,7 +3209,7 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!git_config_get_string("submodule.active", &val) && val) { + if (!git_config_get_string_tmp("submodule.active", &val)) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag @@ -3242,7 +3283,6 @@ static int module_add(int argc, const char **argv, const char *prefix) int force = 0, quiet = 0, progress = 0, dissociate = 0; struct add_data add_data = ADD_DATA_INIT; char *to_free = NULL; - struct option options[] = { OPT_STRING('b', "branch", &add_data.branch, N_("branch"), N_("branch of repository to add as submodule")), @@ -3259,11 +3299,12 @@ static int module_add(int argc, const char **argv, const char *prefix) OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")), OPT_END() }; - const char *const usage[] = { N_("git submodule add [<options>] [--] <repository> [<path>]"), NULL }; + struct strbuf sb = STRBUF_INIT; + int ret = 1; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -3283,8 +3324,12 @@ static int module_add(int argc, const char **argv, const char *prefix) else add_data.sm_path = xstrdup(argv[1]); - if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) - add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path); + if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) { + char *sm_path = add_data.sm_path; + + add_data.sm_path = xstrfmt("%s%s", prefix, sm_path); + free(sm_path); + } if (starts_with_dot_dot_slash(add_data.repo) || starts_with_dot_slash(add_data.repo)) { @@ -3313,20 +3358,17 @@ static int module_add(int argc, const char **argv, const char *prefix) die_on_repo_without_commits(add_data.sm_path); if (!force) { - int exit_code = -1; - struct strbuf sb = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; + cp.git_cmd = 1; cp.no_stdout = 1; strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing", "--no-warn-embedded-repo", add_data.sm_path, NULL); - if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { + if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { strbuf_complete_line(&sb); fputs(sb.buf, stderr); - free(add_data.sm_path); - return exit_code; + goto cleanup; } - strbuf_release(&sb); } if(!add_data.sm_name) @@ -3341,15 +3383,17 @@ static int module_add(int argc, const char **argv, const char *prefix) add_data.progress = !!progress; add_data.dissociate = !!dissociate; - if (add_submodule(&add_data)) { - free(add_data.sm_path); - return 1; - } + if (add_submodule(&add_data)) + goto cleanup; configure_added_submodule(&add_data); + + ret = 0; +cleanup: free(add_data.sm_path); free(to_free); + strbuf_release(&sb); - return 0; + return ret; } #define SUPPORT_SUPER_PREFIX (1<<0) @@ -3361,12 +3405,9 @@ struct cmd_struct { }; static struct cmd_struct commands[] = { - {"list", module_list, 0}, - {"name", module_name, 0}, {"clone", module_clone, SUPPORT_SUPER_PREFIX}, {"add", module_add, 0}, {"update", module_update, SUPPORT_SUPER_PREFIX}, - {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, {"init", module_init, 0}, {"status", module_status, SUPPORT_SUPER_PREFIX}, @@ -3375,8 +3416,6 @@ static struct cmd_struct commands[] = { {"summary", module_summary, 0}, {"push-check", push_check, 0}, {"absorbgitdirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, - {"is-active", is_active, 0}, - {"check-name", check_name, 0}, {"config", module_config, 0}, {"set-url", module_set_url, 0}, {"set-branch", module_set_branch, 0}, diff --git a/builtin/worktree.c b/builtin/worktree.c index cd62eef240..c6710b2552 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1112,31 +1112,24 @@ static int repair(int ac, const char **av, const char *prefix) int cmd_worktree(int ac, const char **av, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("list", &fn, list), + OPT_SUBCOMMAND("lock", &fn, lock_worktree), + OPT_SUBCOMMAND("unlock", &fn, unlock_worktree), + OPT_SUBCOMMAND("move", &fn, move_worktree), + OPT_SUBCOMMAND("remove", &fn, remove_worktree), + OPT_SUBCOMMAND("repair", &fn, repair), OPT_END() }; git_config(git_worktree_config, NULL); - if (ac < 2) - usage_with_options(worktree_usage, options); if (!prefix) prefix = ""; - if (!strcmp(av[1], "add")) - return add(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "prune")) - return prune(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "list")) - return list(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "lock")) - return lock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "unlock")) - return unlock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "move")) - return move_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "remove")) - return remove_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "repair")) - return repair(ac - 1, av + 1, prefix); - usage_with_options(worktree_usage, options); + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + return fn(ac, av, prefix); } |