diff options
Diffstat (limited to 'builtin')
39 files changed, 860 insertions, 568 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/bisect--helper.c b/builtin/bisect--helper.c index 7097750fc6..28ef7ec2a4 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; @@ -764,11 +765,10 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a strbuf_read_file(&start_head, git_path_bisect_start(), 0); strbuf_trim(&start_head); if (!no_checkout) { - struct strvec argv = STRVEC_INIT; + const char *argv[] = { "checkout", start_head.buf, + "--", NULL }; - strvec_pushl(&argv, "checkout", start_head.buf, - "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + if (run_command_v_opt(argv, RUN_GIT_CMD)) { res = error(_("checking out '%s' failed." " Try 'git bisect start " "<valid-branch>'."), @@ -1134,8 +1134,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; diff --git a/builtin/branch.c b/builtin/branch.c index 658c9f8741..407517ba68 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -538,6 +538,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int die(_("Invalid branch name: '%s'"), oldname); } + if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) { + if (copy && !strcmp(head, oldname)) + die(_("No commit on branch '%s' yet."), oldname); + else + die(_("No branch named '%s'."), oldname); + } + /* * A command like "git branch -M currentbranch currentbranch" cannot * cause the worktree to become inconsistent with HEAD, so allow it. @@ -599,10 +606,11 @@ static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION") static int edit_branch_description(const char *branch_name) { + int exists; struct strbuf buf = STRBUF_INIT; struct strbuf name = STRBUF_INIT; - read_branch_desc(&buf, branch_name); + exists = !read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); strbuf_commented_addf(&buf, @@ -619,7 +627,8 @@ static int edit_branch_description(const char *branch_name) strbuf_stripspace(&buf, 1); strbuf_addf(&name, "branch.%s.description", branch_name); - git_config_set(name.buf, buf.len ? buf.buf : NULL); + if (buf.len || exists) + git_config_set(name.buf, buf.len ? buf.buf : NULL); strbuf_release(&name); strbuf_release(&buf); @@ -807,7 +816,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); if (!ref_exists(branch_ref.buf)) - ret = error(!argc + ret = error((!argc || !strcmp(head, branch_name)) ? _("No commit on branch '%s' yet.") : _("No branch named '%s'."), branch_name); @@ -856,8 +865,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("no such branch '%s'"), argv[0]); } - if (!ref_exists(branch->refname)) + if (!ref_exists(branch->refname)) { + if (!argc || !strcmp(head, branch->name)) + die(_("No commit on branch '%s' yet."), branch->name); die(_("branch '%s' does not exist"), branch->name); + } dwim_and_setup_tracking(the_repository, branch->name, new_upstream, BRANCH_TRACK_OVERRIDE, 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 e21d42dfee..547d6464b3 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -73,7 +73,7 @@ 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; @@ -320,13 +320,11 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, int src_len, dest_len; struct dir_iterator *iter; int iter_status; - unsigned int flags; struct strbuf realpath = STRBUF_INIT; mkdir_if_missing(dest->buf, 0777); - flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS; - iter = dir_iterator_begin(src->buf, flags); + iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC); if (!iter) die_errno(_("failed to start iterator over '%s'"), src->buf); @@ -342,6 +340,10 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, strbuf_setlen(dest, dest_len); strbuf_addstr(dest, iter->relative_path); + if (S_ISLNK(iter->st.st_mode)) + die(_("symlink '%s' exists, refusing to clone with --local"), + iter->relative_path); + if (S_ISDIR(iter->st.st_mode)) { mkdir_if_missing(dest->buf, 0777); continue; @@ -929,9 +931,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_bare = 1; if (option_bare) { - if (option_origin) - die(_("options '%s' and '%s %s' cannot be used together"), - "--bare", "--origin", option_origin); if (real_git_dir) die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir"); option_no_checkout = 1; diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index dc3cc35394..51557fe786 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -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); diff --git a/builtin/commit.c b/builtin/commit.c index fcf9c85947..d9de4ef008 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -139,7 +139,7 @@ static int opt_pass_trailer(const struct option *opt, const char *arg, int unset { BUG_ON_OPT_NEG(unset); - strvec_pushl(&trailer_args, "--trailer", arg, NULL); + strvec_pushl(opt->value, "--trailer", arg, NULL); return 0; } @@ -1633,7 +1633,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer), + OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer), OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), 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/diagnose.c b/builtin/diagnose.c index cd260c2015..576e0e8e38 100644 --- a/builtin/diagnose.c +++ b/builtin/diagnose.c @@ -22,7 +22,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix) N_("specify a destination for the diagnostics archive")), OPT_STRING('s', "suffix", &option_suffix, N_("format"), N_("specify a strftime format suffix for the filename")), - OPT_CALLBACK_F(0, "mode", &mode, N_("(stats|all)"), + OPT_CALLBACK_F(0, "mode", &mode, "(stats|all)", N_("specify the content of the diagnostic archive"), PARSE_OPT_NONEG, option_parse_diagnose), OPT_END() diff --git a/builtin/difftool.c b/builtin/difftool.c index 8706f68492..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) diff --git a/builtin/fast-export.c b/builtin/fast-export.c index bf3c20dea2..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) 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..41acbc229e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -228,6 +228,8 @@ static void mark_unreachable_referents(const struct object_id *oid) options.walk = mark_used; fsck_walk(obj, NULL, &options); + if (obj->type == OBJ_TREE) + free_tree_buffer((struct tree *)obj); } static int mark_loose_unreachable_referents(const struct object_id *oid, @@ -437,9 +439,6 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) out: if (obj->type == OBJ_TREE) free_tree_buffer((struct tree *)obj); - if (obj->type == OBJ_COMMIT) - free_commit_buffer(the_repository->parsed_objects, - (struct commit *)obj); return err; } @@ -488,8 +487,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 +502,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 +515,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; @@ -851,6 +852,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) errors_found = 0; read_replace_refs = 0; + save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 2c109cf8b3..6f30a4f93a 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -3,6 +3,7 @@ #include "parse-options.h" #include "fsmonitor.h" #include "fsmonitor-ipc.h" +#include "fsmonitor-path-utils.h" #include "compat/fsmonitor/fsm-health.h" #include "compat/fsmonitor/fsm-listen.h" #include "fsmonitor--daemon.h" @@ -13,8 +14,8 @@ static const char * const builtin_fsmonitor__daemon_usage[] = { N_("git fsmonitor--daemon start [<options>]"), N_("git fsmonitor--daemon run [<options>]"), - N_("git fsmonitor--daemon stop"), - N_("git fsmonitor--daemon status"), + "git fsmonitor--daemon stop", + "git fsmonitor--daemon status", NULL }; @@ -1282,6 +1283,11 @@ static int fsmonitor_run_daemon(void) strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree())); state.nr_paths_watching = 1; + strbuf_init(&state.alias.alias, 0); + strbuf_init(&state.alias.points_to, 0); + if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias))) + goto done; + /* * We create and delete cookie files somewhere inside the .git * directory to help us keep sync with the file system. If @@ -1343,7 +1349,8 @@ static int fsmonitor_run_daemon(void) * directory.) */ strbuf_init(&state.path_ipc, 0); - strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path())); + strbuf_addstr(&state.path_ipc, + absolute_path(fsmonitor_ipc__get_path(the_repository))); /* * Confirm that we can create platform-specific resources for the @@ -1390,6 +1397,8 @@ done: strbuf_release(&state.path_gitdir_watch); strbuf_release(&state.path_cookie_prefix); strbuf_release(&state.path_ipc); + strbuf_release(&state.alias.alias); + strbuf_release(&state.alias.points_to); return err; } diff --git a/builtin/gc.c b/builtin/gc.c index 48eeb2d260..243ee85d28 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -167,16 +167,9 @@ static void gc_config(void) struct maintenance_run_opts; static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts) { - struct strvec pack_refs_cmd = STRVEC_INIT; - int ret; + const char *argv[] = { "pack-refs", "--all", "--prune", NULL }; - strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); - - ret = run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD); - - strvec_clear(&pack_refs_cmd); - - return ret; + return run_command_v_opt(argv, RUN_GIT_CMD); } static int too_many_loose_objects(void) @@ -782,8 +775,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; @@ -1460,7 +1454,7 @@ static char *get_maintpath(void) } static char const * const builtin_maintenance_register_usage[] = { - N_("git maintenance register"), + "git maintenance register", NULL }; @@ -1469,11 +1463,12 @@ static int maintenance_register(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_END(), }; - int rc; + int found = 0; + const char *key = "maintenance.repo"; char *config_value; - struct child_process config_set = CHILD_PROCESS_INIT; - struct child_process config_get = CHILD_PROCESS_INIT; char *maintpath = get_maintpath(); + struct string_list_item *item; + const struct string_list *list; argc = parse_options(argc, argv, prefix, options, builtin_maintenance_register_usage, 0); @@ -1490,46 +1485,56 @@ static int maintenance_register(int argc, const char **argv, const char *prefix) else git_config_set("maintenance.strategy", "incremental"); - config_get.git_cmd = 1; - strvec_pushl(&config_get.args, "config", "--global", "--get", - "--fixed-value", "maintenance.repo", maintpath, NULL); - config_get.out = -1; - - if (start_command(&config_get)) { - rc = error(_("failed to run 'git config'")); - goto done; + list = git_config_get_value_multi(key); + if (list) { + for_each_string_list_item(item, list) { + if (!strcmp(maintpath, item->string)) { + found = 1; + break; + } + } } - /* We already have this value in our config! */ - if (!finish_command(&config_get)) { - rc = 0; - goto done; + if (!found) { + int rc; + char *user_config, *xdg_config; + git_global_config(&user_config, &xdg_config); + if (!user_config) + die(_("$HOME not set")); + rc = git_config_set_multivar_in_file_gently( + user_config, "maintenance.repo", maintpath, + CONFIG_REGEX_NONE, 0); + free(user_config); + free(xdg_config); + + if (rc) + die(_("unable to add '%s' value of '%s'"), + key, maintpath); } - config_set.git_cmd = 1; - strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo", - maintpath, NULL); - - rc = run_command(&config_set); - -done: free(maintpath); - return rc; + return 0; } static char const * const builtin_maintenance_unregister_usage[] = { - N_("git maintenance unregister"), + "git maintenance unregister [--force]", NULL }; static int maintenance_unregister(int argc, const char **argv, const char *prefix) { + int force = 0; struct option options[] = { + OPT__FORCE(&force, + N_("return success even if repository was not registered"), + PARSE_OPT_NOCOMPLETE), OPT_END(), }; - int rc; - struct child_process config_unset = CHILD_PROCESS_INIT; + const char *key = "maintenance.repo"; char *maintpath = get_maintpath(); + int found = 0; + struct string_list_item *item; + const struct string_list *list; argc = parse_options(argc, argv, prefix, options, builtin_maintenance_unregister_usage, 0); @@ -1537,13 +1542,38 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi 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); + list = git_config_get_value_multi(key); + if (list) { + for_each_string_list_item(item, list) { + if (!strcmp(maintpath, item->string)) { + found = 1; + break; + } + } + } + + if (found) { + int rc; + char *user_config, *xdg_config; + git_global_config(&user_config, &xdg_config); + if (!user_config) + die(_("$HOME not set")); + rc = git_config_set_multivar_in_file_gently( + user_config, key, NULL, maintpath, + CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE); + free(user_config); + free(xdg_config); + + if (rc && + (!force || rc == CONFIG_NOTHING_SET)) + die(_("unable to unset '%s' value of '%s'"), + key, maintpath); + } else if (!force) { + die(_("repository '%s' is not registered"), maintpath); + } - rc = run_command(&config_unset); free(maintpath); - return rc; + return 0; } static const char *get_frequency(enum schedule_priority schedule) @@ -2541,7 +2571,7 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) } static const char *const builtin_maintenance_stop_usage[] = { - N_("git maintenance stop"), + "git maintenance stop", NULL }; diff --git a/builtin/grep.c b/builtin/grep.c index e6bcdf860c..5fa927d4e2 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -458,6 +458,33 @@ static int grep_submodule(struct grep_opt *opt, * subrepo's odbs to the in-memory alternates list. */ obj_read_lock(); + + /* + * NEEDSWORK: when reading a submodule, the sparsity settings in the + * superproject are incorrectly forgotten or misused. For example: + * + * 1. "command_requires_full_index" + * When this setting is turned on for `grep`, only the superproject + * knows it. All the submodules are read with their own configs + * and get prepare_repo_settings()'d. Therefore, these submodules + * "forget" the sparse-index feature switch. As a result, the index + * of these submodules are expanded unexpectedly. + * + * 2. "core_apply_sparse_checkout" + * When running `grep` in the superproject, this setting is + * populated using the superproject's configs. However, once + * initialized, this config is globally accessible and is read by + * prepare_repo_settings() for the submodules. For instance, if a + * submodule is using a sparse-checkout, however, the superproject + * is not, the result is that the config from the superproject will + * dictate the behavior for the submodule, making it "forget" its + * sparse-checkout state. + * + * 3. "core_sparse_checkout_cone" + * ditto. + * + * Note that this list is not exhaustive. + */ repo_read_gitmodules(subrepo, 0); /* @@ -520,8 +547,6 @@ static int grep_cache(struct grep_opt *opt, if (repo_read_index(repo) < 0) die(_("index file corrupt")); - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(repo->index); for (nr = 0; nr < repo->index->cache_nr; nr++) { const struct cache_entry *ce = repo->index->cache[nr]; @@ -530,8 +555,20 @@ static int grep_cache(struct grep_opt *opt, strbuf_setlen(&name, name_base_len); strbuf_addstr(&name, ce->name); + if (S_ISSPARSEDIR(ce->ce_mode)) { + enum object_type type; + struct tree_desc tree; + void *data; + unsigned long size; - if (S_ISREG(ce->ce_mode) && + data = read_object_file(&ce->oid, &type, &size); + init_tree_desc(&tree, data, size); + + hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0); + strbuf_setlen(&name, name_base_len); + strbuf_addstr(&name, ce->name); + free(data); + } else if (S_ISREG(ce->ce_mode) && match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL, S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) { @@ -984,6 +1021,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION); + if (the_repository->gitdir) { + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + } + if (use_index && !startup_info->have_repository) { int fallback = 0; git_config_get_bool("grep.fallbacktonoindex", &fallback); diff --git a/builtin/help.c b/builtin/help.c index 09ac4289f1..6f2796f211 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -440,6 +440,8 @@ static const char *cmd_to_page(const char *git_cmd) return git_cmd; else if (is_git_command(git_cmd)) return xstrfmt("git-%s", git_cmd); + else if (!strcmp("scalar", git_cmd)) + return xstrdup(git_cmd); else return xstrfmt("git%s", git_cmd); } diff --git a/builtin/log.c b/builtin/log.c index 047f9e5278..ee19dc5d45 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -698,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) ? "/" : ""); 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/multi-pack-index.c b/builtin/multi-pack-index.c index 3bfad9149d..9a18a82b05 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -56,11 +56,12 @@ static struct opts_multi_pack_index { static int parse_object_dir(const struct option *opt, const char *arg, int unset) { - free(opts.object_dir); + char **value = opt->value; + free(*value); if (unset) - opts.object_dir = xstrdup(get_object_directory()); + *value = xstrdup(get_object_directory()); else - opts.object_dir = real_pathdup(arg, 1); + *value = real_pathdup(arg, 1); return 0; } @@ -78,7 +79,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)) 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 42cbae4659..be51f69225 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -995,7 +995,7 @@ static int get_ref(int argc, const char **argv, const char *prefix) int cmd_notes(int argc, const char **argv, const char *prefix) { const char *override_notes_ref = NULL; - parse_opt_subcommand_fn *fn = list; + 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>")), @@ -1015,9 +1015,12 @@ int cmd_notes(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_notes_usage, PARSE_OPT_SUBCOMMAND_OPTIONAL); - if (fn == list && argc && strcmp(argv[0], "list")) { - error(_("unknown subcommand: %s"), argv[0]); - usage_with_options(git_notes_usage, options); + if (!fn) { + if (argc) { + error(_("unknown subcommand: `%s'"), argv[0]); + usage_with_options(git_notes_usage, options); + } + fn = list; } if (override_notes_ref) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 46e2677496..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; @@ -3958,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/push.c b/builtin/push.c index df0d68e599..f0329c62a2 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -169,8 +169,8 @@ static NORETURN void die_push_simple(struct branch *branch, if (git_branch_track != BRANCH_TRACK_SIMPLE) advice_automergesimple_maybe = _("\n" "To avoid automatically configuring " - "upstream branches when their name\n" - "doesn't match the local branch, see option " + "an upstream branch when its name\n" + "won't match the local branch, see option " "'simple' of branch.autoSetupMerge\n" "in 'git help config'.\n"); die(_("The upstream branch of your current branch does not match\n" 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 461c94f1b2..270681dcdf 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; @@ -66,7 +67,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus * Avoid collecting the same shared ref multiple times because * they are available via all worktrees. */ - if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL) + if (!worktree->is_current && + parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED) return 0; strbuf_worktree_ref(worktree, &newref, ref); diff --git a/builtin/remote.c b/builtin/remote.c index 272c7b8d9e..910f7b9316 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -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; @@ -730,29 +733,31 @@ static int mv(int argc, const char **argv, const char *prefix) return error(_("Could not rename config section '%s' to '%s'"), buf.buf, buf2.buf); - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); - git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.raw_nr; i++) { - char *ptr; - - strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch.raw[i]); - ptr = strstr(buf2.buf, old_remote_context.buf); - if (ptr) { - refspec_updated = 1; - strbuf_splice(&buf2, - ptr-buf2.buf + strlen(":refs/remotes/"), - strlen(rename.old_name), rename.new_name, - strlen(rename.new_name)); - } else - warning(_("Not updating non-default fetch refspec\n" - "\t%s\n" - "\tPlease update the configuration manually if necessary."), - buf2.buf); - - git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); + if (oldremote->fetch.raw_nr) { + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); + git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); + for (i = 0; i < oldremote->fetch.raw_nr; i++) { + char *ptr; + + strbuf_reset(&buf2); + strbuf_addstr(&buf2, oldremote->fetch.raw[i]); + ptr = strstr(buf2.buf, old_remote_context.buf); + if (ptr) { + refspec_updated = 1; + strbuf_splice(&buf2, + ptr-buf2.buf + strlen(":refs/remotes/"), + strlen(rename.old_name), rename.new_name, + strlen(rename.new_name)); + } else + warning(_("Not updating non-default fetch refspec\n" + "\t%s\n" + "\tPlease update the configuration manually if necessary."), + buf2.buf); + + git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); + } } read_branches(); @@ -955,7 +960,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; @@ -1485,7 +1491,7 @@ static int prune(int argc, const char **argv, const char *prefix) 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; @@ -1768,7 +1774,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix) return !!fn(argc, argv, prefix); } else { if (argc) { - error(_("unknown subcommand: %s"), argv[0]); + error(_("unknown subcommand: `%s'"), argv[0]); usage_with_options(builtin_remote_usage, options); } return !!show_all(); 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/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/stash.c b/builtin/stash.c index 1ba24c1173..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; } 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/symbolic-ref.c b/builtin/symbolic-ref.c index 1b0f10225f..590ed17dd3 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -6,14 +6,17 @@ static const char * const git_symbolic_ref_usage[] = { N_("git symbolic-ref [<options>] <name> [<ref>]"), - N_("git symbolic-ref -d [-q] <name>"), + N_("git symbolic-ref -d [-q] [--no-recurse] <name>"), NULL }; -static int check_symref(const char *HEAD, int quiet, int shorten, int print) +static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print) { - int flag; - const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag); + int resolve_flags, flag; + const char *refname; + + resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE); + refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag); if (!refname) die("No such ref: %s", HEAD); @@ -35,13 +38,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print) int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { - int quiet = 0, delete = 0, shorten = 0, ret = 0; + int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0; const char *msg = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("suppress error message for non-symbolic (detached) refs")), OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")), OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), + OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")), OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_END(), }; @@ -55,7 +59,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) if (delete) { if (argc != 1) usage_with_options(git_symbolic_ref_usage, options); - ret = check_symref(argv[0], 1, 0, 0); + ret = check_symref(argv[0], 1, 0, 0, 0); if (ret) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) @@ -65,7 +69,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) switch (argc) { case 1: - ret = check_symref(argv[0], quiet, shorten, 1); + ret = check_symref(argv[0], quiet, shorten, recurse, 1); break; case 2: if (!strcmp(argv[0], "HEAD") && |
