diff options
Diffstat (limited to 'builtin')
65 files changed, 1881 insertions, 1325 deletions
diff --git a/builtin/add.c b/builtin/add.c index 3dfcfc5fba..40b61ef90d 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -150,11 +150,7 @@ static int refresh(int verbose, const struct pathspec *pathspec) int interactive_add(const char **argv, const char *prefix, int patch) { struct pathspec pathspec; - int unused, ret; - - if (!git_config_get_bool("add.interactive.usebuiltin", &unused)) - warning(_("the add.interactive.useBuiltin setting has been removed!\n" - "See its entry in 'git help config' for details.")); + int ret; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | diff --git a/builtin/am.c b/builtin/am.c index 36839029d2..370f5593f2 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -408,7 +408,7 @@ static void am_load(struct am_state *state) read_commit_msg(state); if (read_state_file(&sb, state, "original-commit", 1) < 0) - oidclr(&state->orig_commit); + oidclr(&state->orig_commit, the_repository->hash_algo); else if (get_oid_hex(sb.buf, &state->orig_commit) < 0) die(_("could not parse %s"), am_path(state, "original-commit")); @@ -1121,7 +1121,7 @@ static void am_next(struct am_state *state) unlink(am_path(state, "author-script")); unlink(am_path(state, "final-commit")); - oidclr(&state->orig_commit); + oidclr(&state->orig_commit, the_repository->hash_algo); unlink(am_path(state, "original-commit")); refs_delete_ref(get_main_ref_store(the_repository), NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); @@ -1573,8 +1573,8 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f */ static int fall_back_threeway(const struct am_state *state, const char *index_path) { - struct object_id orig_tree, their_tree, our_tree; - const struct object_id *bases[1] = { &orig_tree }; + struct object_id their_tree, our_tree; + struct object_id bases[1] = { 0 }; struct merge_options o; struct commit *result; char *their_tree_name; @@ -1588,7 +1588,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa discard_index(the_repository->index); read_index_from(the_repository->index, index_path, get_git_dir()); - if (write_index_as_tree(&orig_tree, the_repository->index, index_path, 0, NULL)) + if (write_index_as_tree(&bases[0], the_repository->index, index_path, 0, NULL)) return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); say(state, stdout, _("Using index info to reconstruct a base tree...")); @@ -1718,6 +1718,7 @@ static void do_commit(const struct am_state *state) run_hooks("post-applypatch"); + free_commit_list(parents); strbuf_release(&sb); } @@ -2151,11 +2152,11 @@ static int safe_to_abort(const struct am_state *state) if (get_oid_hex(sb.buf, &abort_safety)) die(_("could not parse %s"), am_path(state, "abort-safety")); } else - oidclr(&abort_safety); + oidclr(&abort_safety, the_repository->hash_algo); strbuf_release(&sb); if (repo_get_oid(the_repository, "HEAD", &head)) - oidclr(&head); + oidclr(&head, the_repository->hash_algo); if (oideq(&head, &abort_safety)) return 1; @@ -2393,6 +2394,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) N_("show the patch being applied"), PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW }, + OPT_CMDMODE(0, "retry", &resume_mode, + N_("try to apply current patch again"), + RESUME_APPLY), OPT_CMDMODE(0, "allow-empty", &resume_mode, N_("record the empty patch as an empty commit"), RESUME_ALLOW_EMPTY), diff --git a/builtin/apply.c b/builtin/apply.c index 861a01910c..d623c52f78 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "gettext.h" #include "repository.h" +#include "hash.h" #include "apply.h" static const char * const apply_usage[] = { @@ -18,6 +19,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (init_apply_state(&state, the_repository, prefix)) exit(128); + /* + * We could to redo the "apply.c" machinery to make this + * arbitrary fallback unnecessary, but it is dubious that it + * is worth the effort. + * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/ + */ + if (!the_hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + argc = apply_parse_options(argc, argv, &state, &force_apply, &options, apply_usage); diff --git a/builtin/archive.c b/builtin/archive.c index 15ee1ec7bb..b50981504f 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -31,9 +31,7 @@ static int run_remote_archiver(int argc, const char **argv, struct packet_reader reader; _remote = remote_get(remote); - if (!_remote->url[0]) - die(_("git archive: Remote with no URL")); - transport = transport_get(_remote, _remote->url[0]); + transport = transport_get(_remote, _remote->url.v[0]); transport_connect(transport, "git-upload-archive", exec, fd); /* @@ -92,6 +90,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix) N_("path to the remote git-upload-archive command")), OPT_END() }; + int ret; argc = parse_options(argc, argv, prefix, local_opts, NULL, PARSE_OPT_KEEP_ALL); @@ -106,6 +105,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - UNLEAK(output); - return write_archive(argc, argv, prefix, the_repository, output, 0); + ret = write_archive(argc, argv, prefix, the_repository, output, 0); + + free(output); + return ret; } diff --git a/builtin/bisect.c b/builtin/bisect.c index a58432b9d9..dabce9b542 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c @@ -262,7 +262,8 @@ static int bisect_reset(const char *commit) return bisect_clean_state(); } -static void log_commit(FILE *fp, char *fmt, const char *state, +static void log_commit(FILE *fp, + const char *fmt, const char *state, struct commit *commit) { struct pretty_print_context pp = {0}; diff --git a/builtin/blame.c b/builtin/blame.c index 6bc7aa6085..35e975fb13 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -5,7 +5,7 @@ * See COPYING for licensing conditions */ -#include "git-compat-util.h" +#include "builtin.h" #include "config.h" #include "color.h" #include "builtin.h" @@ -67,7 +67,7 @@ static int no_whole_file_rename; static int show_progress; static char repeated_meta_color[COLOR_MAXLEN]; static int coloring_mode; -static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP; +static struct string_list ignore_revs_file_list = STRING_LIST_INIT_DUP; static int mark_unblamable_lines; static int mark_ignored_lines; @@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what, { struct ident_split ident; size_t len, maillen, namelen; - char *tmp, *endp; + const char *tmp, *endp; const char *namebuf, *mailbuf; tmp = strstr(inbuf, what); @@ -687,7 +687,7 @@ static unsigned parse_score(const char *arg) return score; } -static const char *add_prefix(const char *prefix, const char *path) +static char *add_prefix(const char *prefix, const char *path) { return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } @@ -718,13 +718,14 @@ static int git_blame_config(const char *var, const char *value, return 0; } if (!strcmp(var, "blame.ignorerevsfile")) { - const char *str; + char *str; int ret; ret = git_config_pathname(&str, var, value); if (ret) return ret; string_list_insert(&ignore_revs_file_list, str); + free(str); return 0; } if (!strcmp(var, "blame.markunblamablelines")) { @@ -852,6 +853,7 @@ static void build_ignorelist(struct blame_scoreboard *sb, oidset_clear(&sb->ignore_list); else oidset_parse_file_carefully(&sb->ignore_list, i->string, + the_repository->hash_algo, peel_to_commit_oid, sb); } for_each_string_list_item(i, ignore_rev_list) { @@ -865,7 +867,7 @@ static void build_ignorelist(struct blame_scoreboard *sb, int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; - const char *path; + char *path = NULL; struct blame_scoreboard sb; struct blame_origin *o; struct blame_entry *ent = NULL; @@ -915,7 +917,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) struct range_set ranges; unsigned int range_i; long anchor; - const int hexsz = the_hash_algo->hexsz; long num_lines = 0; const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage; const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage; @@ -973,11 +974,11 @@ parse_done: } else if (show_progress < 0) show_progress = isatty(2); - if (0 < abbrev && abbrev < hexsz) + if (0 < abbrev && abbrev < (int)the_hash_algo->hexsz) /* one more abbrev length is needed for the boundary commit */ abbrev++; else if (!abbrev) - abbrev = hexsz; + abbrev = the_hash_algo->hexsz; if (revs_file && read_ancestry(revs_file)) die_errno("reading graft file '%s' failed", revs_file); @@ -1227,6 +1228,7 @@ parse_done: } cleanup: + free(path); cleanup_scoreboard(&sb); release_revisions(&revs); return 0; diff --git a/builtin/bugreport.c b/builtin/bugreport.c index 25f860a0d9..b3cc77af53 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) struct tm tm; enum diagnose_mode diagnose = DIAGNOSE_NONE; char *option_output = NULL; - char *option_suffix = "%Y-%m-%d-%H%M"; + const char *option_suffix = "%Y-%m-%d-%H%M"; const char *user_relative_path = NULL; char *prefixed_filename; size_t output_path_len; diff --git a/builtin/bundle.c b/builtin/bundle.c index 3ad11dc5d0..d5d41a8f67 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -140,6 +140,11 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { builtin_bundle_verify_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ + if (!startup_info->have_repository) { + ret = error(_("need a repository to verify a bundle")); + goto cleanup; + } + if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) { ret = 1; goto cleanup; diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 43a1d7ac49..18fe58d6b8 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -102,7 +102,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, enum object_type type; char *buf; unsigned long size; - struct object_context obj_context; + struct object_context obj_context = {0}; struct object_info oi = OBJECT_INFO_INIT; struct strbuf sb = STRBUF_INIT; unsigned flags = OBJECT_INFO_LOOKUP_REPLACE; @@ -163,7 +163,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, goto cleanup; case 'e': - return !repo_has_object_file(the_repository, &oid); + ret = !repo_has_object_file(the_repository, &oid); + goto cleanup; case 'w': @@ -268,7 +269,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, ret = 0; cleanup: free(buf); - free(obj_context.path); + object_context_release(&obj_context); return ret; } @@ -520,7 +521,7 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt, struct expand_data *data) { - struct object_context ctx; + struct object_context ctx = {0}; int flags = GET_OID_HASH_ANY | (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0); @@ -557,7 +558,8 @@ static void batch_one_object(const char *obj_name, break; } fflush(stdout); - return; + + goto out; } if (ctx.mode == 0) { @@ -565,10 +567,13 @@ static void batch_one_object(const char *obj_name, (uintmax_t)ctx.symlink_path.len, opt->output_delim, ctx.symlink_path.buf, opt->output_delim); fflush(stdout); - return; + goto out; } batch_object_write(obj_name, scratch, opt, data, NULL, 0); + +out: + object_context_release(&ctx); } struct object_cb_data { diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 6c43430ec4..2bda6a1d46 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = { static void output_pattern(const char *path, struct path_pattern *pattern) { - char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : ""; - char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : ""; + const char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : ""; + const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : ""; if (!nul_term_line) { if (!verbose) { write_name_quoted(path, stdout, '\n'); diff --git a/builtin/checkout.c b/builtin/checkout.c index f90a4ca4b7..3cf44b4683 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1275,12 +1275,12 @@ static void setup_new_branch_info_and_source_tree( } } -static const char *parse_remote_branch(const char *arg, - struct object_id *rev, - int could_be_checkout_paths) +static char *parse_remote_branch(const char *arg, + struct object_id *rev, + int could_be_checkout_paths) { int num_matches = 0; - const char *remote = unique_tracking_name(arg, rev, &num_matches); + char *remote = unique_tracking_name(arg, rev, &num_matches); if (remote && could_be_checkout_paths) { die(_("'%s' could be both a local file and a tracking branch.\n" @@ -1316,6 +1316,7 @@ static int parse_branchname_arg(int argc, const char **argv, const char **new_branch = &opts->new_branch; int argcount = 0; const char *arg; + char *remote = NULL; int dash_dash_pos; int has_dash_dash = 0; int i; @@ -1416,8 +1417,8 @@ static int parse_branchname_arg(int argc, const char **argv, recover_with_dwim = 0; if (recover_with_dwim) { - const char *remote = parse_remote_branch(arg, rev, - could_be_checkout_paths); + remote = parse_remote_branch(arg, rev, + could_be_checkout_paths); if (remote) { *new_branch = arg; arg = remote; @@ -1459,6 +1460,7 @@ static int parse_branchname_arg(int argc, const char **argv, argc--; } + free(remote); return argcount; } diff --git a/builtin/clone.c b/builtin/clone.c index e808e02017..af6017d41a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -71,7 +71,7 @@ static char *option_branch = NULL; static struct string_list option_not = STRING_LIST_INIT_NODUP; static const char *real_git_dir; static const char *ref_format; -static char *option_upload_pack = "git-upload-pack"; +static const char *option_upload_pack = "git-upload-pack"; static int option_verbosity; static int option_progress = -1; static int option_sparse_checkout; @@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = { static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) { - static char *suffix[] = { "/.git", "", ".git/.git", ".git" }; - static char *bundle_suffix[] = { ".bundle", "" }; + static const char *suffix[] = { "/.git", "", ".git/.git", ".git" }; + static const char *bundle_suffix[] = { ".bundle", "" }; size_t baselen = path->len; struct stat st; int i; @@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs, struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); struct ref *local_refs = head; struct ref **tail = head ? &head->next : &local_refs; + struct refspec_item tag_refspec; + + refspec_item_init(&tag_refspec, TAG_REFSPEC, 0); if (option_single_branch) { struct ref *remote_head = NULL; @@ -530,7 +533,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs, if (!option_branch) remote_head = guess_remote_head(head, refs, 0); else { - local_refs = NULL; + free_one_ref(head); + local_refs = head = NULL; tail = &local_refs; remote_head = copy_ref(find_remote_branch(refs, option_branch)); } @@ -545,7 +549,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs, &tail, 0); /* if --branch=tag, pull the requested tag explicitly */ - get_fetch_map(remote_head, tag_refspec, &tail, 0); + get_fetch_map(remote_head, &tag_refspec, &tail, 0); } free_refs(remote_head); } else { @@ -555,8 +559,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs, } if (!option_mirror && !option_single_branch && !option_no_tags) - get_fetch_map(refs, tag_refspec, &tail, 0); + get_fetch_map(refs, &tag_refspec, &tail, 0); + refspec_item_clear(&tag_refspec); return local_refs; } @@ -576,7 +581,7 @@ static void write_remote_refs(const struct ref *local_refs) if (!r->peer_ref) continue; if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid, - 0, NULL, &err)) + NULL, 0, NULL, &err)) die("%s", err.buf); } @@ -972,8 +977,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int hash_algo; enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; const int do_not_override_repo_unix_permissions = -1; - const char *template_dir; - char *template_dir_dup = NULL; struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; @@ -993,13 +996,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); - xsetenv("GIT_CLONE_PROTECTION_ACTIVE", "true", 0 /* allow user override */); - template_dir = get_template_dir(option_template); - if (*template_dir && !is_absolute_path(template_dir)) - template_dir = template_dir_dup = - absolute_pathdup(template_dir); - xsetenv("GIT_CLONE_TEMPLATE_DIR", template_dir, 1); - if (option_depth || option_since || option_not.nr) deepen = 1; if (option_single_branch == -1) @@ -1161,7 +1157,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) * repository, and reference backends may persist that information into * their on-disk data structures. */ - init_db(git_dir, real_git_dir, template_dir, GIT_HASH_UNKNOWN, + init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, ref_storage_format, NULL, do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB); @@ -1299,7 +1295,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, branch_top.buf); - path = get_repo_path(remote->url[0], &is_bundle); + path = get_repo_path(remote->url.v[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; if (is_local) { if (option_depth) @@ -1321,7 +1317,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_local > 0 && !is_local) warning(_("--local is ignored")); - transport = transport_get(remote, path ? path : remote->url[0]); + transport = transport_get(remote, path ? path : remote->url.v[0]); transport_set_verbosity(transport, option_verbosity, option_progress); transport->family = family; transport->cloning = 1; @@ -1553,7 +1549,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) free(dir); free(path); free(repo_to_free); - free(template_dir_dup); + UNLEAK(repo); junk_mode = JUNK_LEAVE_ALL; transport_ls_refs_options_release(&transport_ls_refs_options); diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 1bb7819839..84bb450222 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -111,6 +111,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_END() }; + int ret; git_config(git_default_config, NULL); @@ -132,11 +133,15 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid, NULL, sign_commit)) { - strbuf_release(&buffer); - return 1; + ret = 1; + goto out; } printf("%s\n", oid_to_hex(&commit_oid)); + ret = 0; + +out: + free_commit_list(parents); strbuf_release(&buffer); - return 0; + return ret; } diff --git a/builtin/commit.c b/builtin/commit.c index 78bfae2164..dec78dfb86 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -106,14 +106,15 @@ static enum { COMMIT_PARTIAL } commit_style; -static const char *logfile, *force_author; -static const char *template_file; +static const char *force_author; +static char *logfile; +static char *template_file; /* * The _message variables are commit names from which to take * the commit message and/or authorship. */ static const char *author_message, *author_message_buffer; -static char *edit_message, *use_message; +static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; @@ -121,8 +122,8 @@ static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ static int no_post_rewrite, allow_empty_message, pathspec_file_nul; -static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit, *pathspec_from_file; +static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; +static const char *sign_commit, *pathspec_from_file; static struct strvec trailer_args = STRVEC_INIT; /* @@ -133,7 +134,7 @@ static struct strvec trailer_args = STRVEC_INIT; * is specified explicitly. */ static enum commit_msg_cleanup_mode cleanup_mode; -static const char *cleanup_arg; +static char *cleanup_arg; static enum commit_whence whence; static int use_editor = 1, include_status = 1; @@ -1309,7 +1310,7 @@ static int parse_and_validate_options(int argc, const char *argv[], !!use_message, "-C", !!logfile, "-F"); if (use_message || edit_message || logfile ||fixup_message || have_option_m) - template_file = NULL; + FREE_AND_NULL(template_file); if (edit_message) use_message = edit_message; if (amend && !use_message && !fixup_message) @@ -1847,7 +1848,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) rollback_index_files(); die(_("failed to write commit object")); } - free_commit_extra_headers(extra); if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb, &err)) { @@ -1889,8 +1889,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) apply_autostash_ref(the_repository, "MERGE_AUTOSTASH"); cleanup: + free_commit_extra_headers(extra); + free_commit_list(parents); strbuf_release(&author_ident); strbuf_release(&err); strbuf_release(&sb); + free(logfile); + free(template_file); return ret; } diff --git a/builtin/config.c b/builtin/config.c index 80aa9d8a66..20a0b64090 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -62,56 +62,66 @@ static const char *const builtin_config_edit_usage[] = { NULL }; -static char *key; -static regex_t *key_regexp; -static const char *value_pattern; -static regex_t *regexp; -static int show_keys; -static int omit_values; -static int use_key_regexp; -static int do_all; -static int do_not_match; -static char delim = '='; -static char key_delim = ' '; -static char term = '\n'; - -static parse_opt_subcommand_fn *subcommand; -static int use_global_config, use_system_config, use_local_config; -static int use_worktree_config; -static struct git_config_source given_config_source; -static int actions, type; -static char *default_value; -static int end_nul; -static int respect_includes_opt = -1; -static struct config_options config_options; -static int show_origin; -static int show_scope; -static int fixed_value; -static const char *comment_arg; - -#define ACTION_GET (1<<0) -#define ACTION_GET_ALL (1<<1) -#define ACTION_GET_REGEXP (1<<2) -#define ACTION_REPLACE_ALL (1<<3) -#define ACTION_ADD (1<<4) -#define ACTION_UNSET (1<<5) -#define ACTION_UNSET_ALL (1<<6) -#define ACTION_RENAME_SECTION (1<<7) -#define ACTION_REMOVE_SECTION (1<<8) -#define ACTION_LIST (1<<9) -#define ACTION_EDIT (1<<10) -#define ACTION_SET (1<<11) -#define ACTION_SET_ALL (1<<12) -#define ACTION_GET_COLOR (1<<13) -#define ACTION_GET_COLORBOOL (1<<14) -#define ACTION_GET_URLMATCH (1<<15) - -/* - * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than - * one line of output and which should therefore be paged. - */ -#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \ - ACTION_GET_REGEXP | ACTION_GET_URLMATCH) +#define CONFIG_LOCATION_OPTIONS(opts) \ + OPT_GROUP(N_("Config file location")), \ + OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \ + OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \ + OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \ + OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \ + OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \ + OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object")) + +struct config_location_options { + struct git_config_source source; + struct config_options options; + char *file_to_free; + int use_global_config; + int use_system_config; + int use_local_config; + int use_worktree_config; + int respect_includes_opt; +}; +#define CONFIG_LOCATION_OPTIONS_INIT { \ + .respect_includes_opt = -1, \ +} + +#define CONFIG_TYPE_OPTIONS(type) \ + OPT_GROUP(N_("Type")), \ + OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \ + OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \ + OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \ + OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \ + OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \ + OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \ + OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE) + +#define CONFIG_DISPLAY_OPTIONS(opts) \ + OPT_GROUP(N_("Display options")), \ + OPT_BOOL('z', "null", &opts.end_nul, N_("terminate values with NUL byte")), \ + OPT_BOOL(0, "name-only", &opts.omit_values, N_("show variable names only")), \ + OPT_BOOL(0, "show-origin", &opts.show_origin, N_("show origin of config (file, standard input, blob, command line)")), \ + OPT_BOOL(0, "show-scope", &opts.show_scope, N_("show scope of config (worktree, local, global, system, command)")), \ + OPT_BOOL(0, "show-names", &opts.show_keys, N_("show config keys in addition to their values")), \ + CONFIG_TYPE_OPTIONS(opts.type) + +struct config_display_options { + int end_nul; + int omit_values; + int show_origin; + int show_scope; + int show_keys; + int type; + char *default_value; + /* Populated via `display_options_init()`. */ + int term; + int delim; + int key_delim; +}; +#define CONFIG_DISPLAY_OPTIONS_INIT { \ + .term = '\n', \ + .delim = '=', \ + .key_delim = ' ', \ +} #define TYPE_BOOL 1 #define TYPE_INT 2 @@ -125,8 +135,6 @@ static const char *comment_arg; { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ PARSE_OPT_NONEG, option_parse_type, (i) } -static NORETURN void usage_builtin_config(void); - static int option_parse_type(const struct option *opt, const char *arg, int unset) { @@ -171,7 +179,7 @@ static int option_parse_type(const struct option *opt, const char *arg, * --type=int'. */ error(_("only one type at a time")); - usage_builtin_config(); + exit(129); } *to_type = new_type; @@ -187,27 +195,29 @@ static void check_argc(int argc, int min, int max) else error(_("wrong number of arguments, should be from %d to %d"), min, max); - usage_builtin_config(); + exit(129); } -static void show_config_origin(const struct key_value_info *kvi, +static void show_config_origin(const struct config_display_options *opts, + const struct key_value_info *kvi, struct strbuf *buf) { - const char term = end_nul ? '\0' : '\t'; + const char term = opts->end_nul ? '\0' : '\t'; strbuf_addstr(buf, config_origin_type_name(kvi->origin_type)); strbuf_addch(buf, ':'); - if (end_nul) + if (opts->end_nul) strbuf_addstr(buf, kvi->filename ? kvi->filename : ""); else quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0); strbuf_addch(buf, term); } -static void show_config_scope(const struct key_value_info *kvi, +static void show_config_scope(const struct config_display_options *opts, + const struct key_value_info *kvi, struct strbuf *buf) { - const char term = end_nul ? '\0' : '\t'; + const char term = opts->end_nul ? '\0' : '\t'; const char *scope = config_scope_name(kvi->scope); strbuf_addstr(buf, N_(scope)); @@ -216,24 +226,25 @@ static void show_config_scope(const struct key_value_info *kvi, static int show_all_config(const char *key_, const char *value_, const struct config_context *ctx, - void *cb UNUSED) + void *cb) { + const struct config_display_options *opts = cb; const struct key_value_info *kvi = ctx->kvi; - if (show_origin || show_scope) { + if (opts->show_origin || opts->show_scope) { struct strbuf buf = STRBUF_INIT; - if (show_scope) - show_config_scope(kvi, &buf); - if (show_origin) - show_config_origin(kvi, &buf); + if (opts->show_scope) + show_config_scope(opts, kvi, &buf); + if (opts->show_origin) + show_config_origin(opts, kvi, &buf); /* Use fwrite as "buf" can contain \0's if "end_null" is set. */ fwrite(buf.buf, 1, buf.len, stdout); strbuf_release(&buf); } - if (!omit_values && value_) - printf("%s%c%s%c", key_, delim, value_, term); + if (!opts->omit_values && value_) + printf("%s%c%s%c", key_, opts->delim, value_, opts->term); else - printf("%s%c", key_, term); + printf("%s%c", key_, opts->term); return 0; } @@ -243,26 +254,27 @@ struct strbuf_list { int alloc; }; -static int format_config(struct strbuf *buf, const char *key_, +static int format_config(const struct config_display_options *opts, + struct strbuf *buf, const char *key_, const char *value_, const struct key_value_info *kvi) { - if (show_scope) - show_config_scope(kvi, buf); - if (show_origin) - show_config_origin(kvi, buf); - if (show_keys) + if (opts->show_scope) + show_config_scope(opts, kvi, buf); + if (opts->show_origin) + show_config_origin(opts, kvi, buf); + if (opts->show_keys) strbuf_addstr(buf, key_); - if (!omit_values) { - if (show_keys) - strbuf_addch(buf, key_delim); + if (!opts->omit_values) { + if (opts->show_keys) + strbuf_addch(buf, opts->key_delim); - if (type == TYPE_INT) + if (opts->type == TYPE_INT) strbuf_addf(buf, "%"PRId64, git_config_int64(key_, value_ ? value_ : "", kvi)); - else if (type == TYPE_BOOL) + else if (opts->type == TYPE_BOOL) strbuf_addstr(buf, git_config_bool(key_, value_) ? "true" : "false"); - else if (type == TYPE_BOOL_OR_INT) { + else if (opts->type == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, kvi, &is_bool); @@ -270,24 +282,24 @@ static int format_config(struct strbuf *buf, const char *key_, strbuf_addstr(buf, v ? "true" : "false"); else strbuf_addf(buf, "%d", v); - } else if (type == TYPE_BOOL_OR_STR) { + } else if (opts->type == TYPE_BOOL_OR_STR) { int v = git_parse_maybe_bool(value_); if (v < 0) strbuf_addstr(buf, value_); else strbuf_addstr(buf, v ? "true" : "false"); - } else if (type == TYPE_PATH) { - const char *v; + } else if (opts->type == TYPE_PATH) { + char *v; if (git_config_pathname(&v, key_, value_) < 0) return -1; strbuf_addstr(buf, v); free((char *)v); - } else if (type == TYPE_EXPIRY_DATE) { + } else if (opts->type == TYPE_EXPIRY_DATE) { timestamp_t t; if (git_config_expiry_date(&t, key_, value_) < 0) return -1; strbuf_addf(buf, "%"PRItime, t); - } else if (type == TYPE_COLOR) { + } else if (opts->type == TYPE_COLOR) { char v[COLOR_MAXLEN]; if (git_config_color(v, key_, value_) < 0) return -1; @@ -296,43 +308,73 @@ static int format_config(struct strbuf *buf, const char *key_, strbuf_addstr(buf, value_); } else { /* Just show the key name; back out delimiter */ - if (show_keys) + if (opts->show_keys) strbuf_setlen(buf, buf->len - 1); } } - strbuf_addch(buf, term); + strbuf_addch(buf, opts->term); return 0; } +#define GET_VALUE_ALL (1 << 0) +#define GET_VALUE_KEY_REGEXP (1 << 1) + +struct collect_config_data { + const struct config_display_options *display_opts; + struct strbuf_list *values; + const char *value_pattern; + const char *key; + regex_t *regexp; + regex_t *key_regexp; + int do_not_match; + unsigned get_value_flags; + unsigned flags; +}; + static int collect_config(const char *key_, const char *value_, const struct config_context *ctx, void *cb) { - struct strbuf_list *values = cb; + struct collect_config_data *data = cb; + struct strbuf_list *values = data->values; const struct key_value_info *kvi = ctx->kvi; - if (!use_key_regexp && strcmp(key_, key)) + if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) && + strcmp(key_, data->key)) return 0; - if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) + if ((data->get_value_flags & GET_VALUE_KEY_REGEXP) && + regexec(data->key_regexp, key_, 0, NULL, 0)) return 0; - if (fixed_value && strcmp(value_pattern, (value_?value_:""))) + if ((data->flags & CONFIG_FLAGS_FIXED_VALUE) && + strcmp(data->value_pattern, (value_?value_:""))) return 0; - if (regexp != NULL && - (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) + if (data->regexp && + (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:""), 0, NULL, 0))) return 0; ALLOC_GROW(values->items, values->nr + 1, values->alloc); strbuf_init(&values->items[values->nr], 0); - return format_config(&values->items[values->nr++], key_, value_, kvi); + return format_config(data->display_opts, &values->items[values->nr++], + key_, value_, kvi); } -static int get_value(const char *key_, const char *regex_, unsigned flags) +static int get_value(const struct config_location_options *opts, + const struct config_display_options *display_opts, + const char *key_, const char *regex_, + unsigned get_value_flags, unsigned flags) { int ret = CONFIG_GENERIC_ERROR; struct strbuf_list values = {NULL}; + struct collect_config_data data = { + .display_opts = display_opts, + .values = &values, + .get_value_flags = get_value_flags, + .flags = flags, + }; + char *key = NULL; int i; - if (use_key_regexp) { + if (get_value_flags & GET_VALUE_KEY_REGEXP) { char *tl; /* @@ -349,10 +391,10 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) for (tl = key; *tl && *tl != '.'; tl++) *tl = tolower(*tl); - key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(key_regexp, key, REG_EXTENDED)) { + data.key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); + if (regcomp(data.key_regexp, key, REG_EXTENDED)) { error(_("invalid key pattern: %s"), key_); - FREE_AND_NULL(key_regexp); + FREE_AND_NULL(data.key_regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; } @@ -361,30 +403,32 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) ret = CONFIG_INVALID_KEY; goto free_strings; } + + data.key = key; } if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE)) - value_pattern = regex_; + data.value_pattern = regex_; else if (regex_) { if (regex_[0] == '!') { - do_not_match = 1; + data.do_not_match = 1; regex_++; } - regexp = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(regexp, regex_, REG_EXTENDED)) { + data.regexp = (regex_t*)xmalloc(sizeof(regex_t)); + if (regcomp(data.regexp, regex_, REG_EXTENDED)) { error(_("invalid pattern: %s"), regex_); - FREE_AND_NULL(regexp); + FREE_AND_NULL(data.regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; } } - config_with_options(collect_config, &values, - &given_config_source, the_repository, - &config_options); + config_with_options(collect_config, &data, + &opts->source, the_repository, + &opts->options); - if (!values.nr && default_value) { + if (!values.nr && display_opts->default_value) { struct key_value_info kvi = KVI_INIT; struct strbuf *item; @@ -392,16 +436,17 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) ALLOC_GROW(values.items, values.nr + 1, values.alloc); item = &values.items[values.nr++]; strbuf_init(item, 0); - if (format_config(item, key_, default_value, &kvi) < 0) + if (format_config(display_opts, item, key_, + display_opts->default_value, &kvi) < 0) die(_("failed to format default config value: %s"), - default_value); + display_opts->default_value); } ret = !values.nr; for (i = 0; i < values.nr; i++) { struct strbuf *buf = values.items + i; - if (do_all || i == values.nr - 1) + if ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1) fwrite(buf->buf, 1, buf->len, stdout); strbuf_release(buf); } @@ -409,20 +454,20 @@ static int get_value(const char *key_, const char *regex_, unsigned flags) free_strings: free(key); - if (key_regexp) { - regfree(key_regexp); - free(key_regexp); + if (data.key_regexp) { + regfree(data.key_regexp); + free(data.key_regexp); } - if (regexp) { - regfree(regexp); - free(regexp); + if (data.regexp) { + regfree(data.regexp); + free(data.regexp); } return ret; } static char *normalize_value(const char *key, const char *value, - struct key_value_info *kvi) + int type, struct key_value_info *kvi) { if (!value) return NULL; @@ -473,97 +518,113 @@ static char *normalize_value(const char *key, const char *value, BUG("cannot normalize type %d", type); } -static int get_color_found; -static const char *get_color_slot; -static const char *get_colorbool_slot; -static char parsed_color[COLOR_MAXLEN]; +struct get_color_config_data { + int get_color_found; + const char *get_color_slot; + char parsed_color[COLOR_MAXLEN]; +}; static int git_get_color_config(const char *var, const char *value, const struct config_context *ctx UNUSED, - void *cb UNUSED) + void *cb) { - if (!strcmp(var, get_color_slot)) { + struct get_color_config_data *data = cb; + + if (!strcmp(var, data->get_color_slot)) { if (!value) config_error_nonbool(var); - if (color_parse(value, parsed_color) < 0) + if (color_parse(value, data->parsed_color) < 0) return -1; - get_color_found = 1; + data->get_color_found = 1; } return 0; } -static void get_color(const char *var, const char *def_color) +static void get_color(const struct config_location_options *opts, + const char *var, const char *def_color) { - get_color_slot = var; - get_color_found = 0; - parsed_color[0] = '\0'; - config_with_options(git_get_color_config, NULL, - &given_config_source, the_repository, - &config_options); - - if (!get_color_found && def_color) { - if (color_parse(def_color, parsed_color) < 0) + struct get_color_config_data data = { + .get_color_slot = var, + .parsed_color[0] = '\0', + }; + + config_with_options(git_get_color_config, &data, + &opts->source, the_repository, + &opts->options); + + if (!data.get_color_found && def_color) { + if (color_parse(def_color, data.parsed_color) < 0) die(_("unable to parse default color value")); } - fputs(parsed_color, stdout); + fputs(data.parsed_color, stdout); } -static int get_colorbool_found; -static int get_diff_color_found; -static int get_color_ui_found; +struct get_colorbool_config_data { + int get_colorbool_found; + int get_diff_color_found; + int get_color_ui_found; + const char *get_colorbool_slot; +}; + static int git_get_colorbool_config(const char *var, const char *value, const struct config_context *ctx UNUSED, - void *data UNUSED) + void *cb) { - if (!strcmp(var, get_colorbool_slot)) - get_colorbool_found = git_config_colorbool(var, value); + struct get_colorbool_config_data *data = cb; + + if (!strcmp(var, data->get_colorbool_slot)) + data->get_colorbool_found = git_config_colorbool(var, value); else if (!strcmp(var, "diff.color")) - get_diff_color_found = git_config_colorbool(var, value); + data->get_diff_color_found = git_config_colorbool(var, value); else if (!strcmp(var, "color.ui")) - get_color_ui_found = git_config_colorbool(var, value); + data->get_color_ui_found = git_config_colorbool(var, value); return 0; } -static int get_colorbool(const char *var, int print) +static int get_colorbool(const struct config_location_options *opts, + const char *var, int print) { - get_colorbool_slot = var; - get_colorbool_found = -1; - get_diff_color_found = -1; - get_color_ui_found = -1; - config_with_options(git_get_colorbool_config, NULL, - &given_config_source, the_repository, - &config_options); - - if (get_colorbool_found < 0) { - if (!strcmp(get_colorbool_slot, "color.diff")) - get_colorbool_found = get_diff_color_found; - if (get_colorbool_found < 0) - get_colorbool_found = get_color_ui_found; + struct get_colorbool_config_data data = { + .get_colorbool_slot = var, + .get_colorbool_found = -1, + .get_diff_color_found = -1, + .get_color_ui_found = -1, + }; + + config_with_options(git_get_colorbool_config, &data, + &opts->source, the_repository, + &opts->options); + + if (data.get_colorbool_found < 0) { + if (!strcmp(data.get_colorbool_slot, "color.diff")) + data.get_colorbool_found = data.get_diff_color_found; + if (data.get_colorbool_found < 0) + data.get_colorbool_found = data.get_color_ui_found; } - if (get_colorbool_found < 0) + if (data.get_colorbool_found < 0) /* default value if none found in config */ - get_colorbool_found = GIT_COLOR_AUTO; + data.get_colorbool_found = GIT_COLOR_AUTO; - get_colorbool_found = want_color(get_colorbool_found); + data.get_colorbool_found = want_color(data.get_colorbool_found); if (print) { - printf("%s\n", get_colorbool_found ? "true" : "false"); + printf("%s\n", data.get_colorbool_found ? "true" : "false"); return 0; } else - return get_colorbool_found ? 0 : 1; + return data.get_colorbool_found ? 0 : 1; } -static void check_write(void) +static void check_write(const struct git_config_source *source) { - if (!given_config_source.file && !startup_info->have_repository) + if (!source->file && !startup_info->have_repository) die(_("not in a git directory")); - if (given_config_source.use_stdin) + if (source->use_stdin) die(_("writing to stdin is not supported")); - if (given_config_source.blob) + if (source->blob) die(_("writing config blobs is not supported")); } @@ -600,10 +661,13 @@ static int urlmatch_collect_fn(const char *var, const char *value, return 0; } -static int get_urlmatch(const char *var, const char *url) +static int get_urlmatch(const struct config_location_options *opts, + const struct config_display_options *_display_opts, + const char *var, const char *url) { int ret; char *section_tail; + struct config_display_options display_opts = *_display_opts; struct string_list_item *item; struct urlmatch_config config = URLMATCH_CONFIG_INIT; struct string_list values = STRING_LIST_INIT_DUP; @@ -620,15 +684,15 @@ static int get_urlmatch(const char *var, const char *url) if (section_tail) { *section_tail = '\0'; config.key = section_tail + 1; - show_keys = 0; + display_opts.show_keys = 0; } else { config.key = NULL; - show_keys = 1; + display_opts.show_keys = 1; } config_with_options(urlmatch_config_entry, &config, - &given_config_source, the_repository, - &config_options); + &opts->source, the_repository, + &opts->options); ret = !values.nr; @@ -636,7 +700,7 @@ static int get_urlmatch(const char *var, const char *url) struct urlmatch_current_candidate_value *matched = item->util; struct strbuf buf = STRBUF_INIT; - format_config(&buf, item->string, + format_config(&display_opts, &buf, item->string, matched->value_is_null ? NULL : matched->value.buf, &matched->kvi); fwrite(buf.buf, 1, buf.len, stdout); @@ -666,34 +730,39 @@ static char *default_user_config(void) return strbuf_detach(&buf, NULL); } -static void handle_config_location(const char *prefix) +static void location_options_init(struct config_location_options *opts, + const char *prefix) { - if (use_global_config + use_system_config + use_local_config + - use_worktree_config + - !!given_config_source.file + !!given_config_source.blob > 1) { + if (!opts->source.file) + opts->source.file = opts->file_to_free = + xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); + + if (opts->use_global_config + opts->use_system_config + + opts->use_local_config + opts->use_worktree_config + + !!opts->source.file + !!opts->source.blob > 1) { error(_("only one config file at a time")); - usage_builtin_config(); + exit(129); } if (!startup_info->have_repository) { - if (use_local_config) + if (opts->use_local_config) die(_("--local can only be used inside a git repository")); - if (given_config_source.blob) + if (opts->source.blob) die(_("--blob can only be used inside a git repository")); - if (use_worktree_config) + if (opts->use_worktree_config) die(_("--worktree can only be used inside a git repository")); } - if (given_config_source.file && - !strcmp(given_config_source.file, "-")) { - given_config_source.file = NULL; - given_config_source.use_stdin = 1; - given_config_source.scope = CONFIG_SCOPE_COMMAND; + if (opts->source.file && + !strcmp(opts->source.file, "-")) { + opts->source.file = NULL; + opts->source.use_stdin = 1; + opts->source.scope = CONFIG_SCOPE_COMMAND; } - if (use_global_config) { - given_config_source.file = git_global_config(); - if (!given_config_source.file) + if (opts->use_global_config) { + opts->source.file = opts->file_to_free = git_global_config(); + if (!opts->source.file) /* * It is unknown if HOME/.gitconfig exists, so * we do not know if we should write to XDG @@ -701,17 +770,18 @@ static void handle_config_location(const char *prefix) * is set and points at a sane location. */ die(_("$HOME not set")); - given_config_source.scope = CONFIG_SCOPE_GLOBAL; - } else if (use_system_config) { - given_config_source.file = git_system_config(); - given_config_source.scope = CONFIG_SCOPE_SYSTEM; - } else if (use_local_config) { - given_config_source.file = git_pathdup("config"); - given_config_source.scope = CONFIG_SCOPE_LOCAL; - } else if (use_worktree_config) { + opts->source.scope = CONFIG_SCOPE_GLOBAL; + } else if (opts->use_system_config) { + opts->source.file = opts->file_to_free = git_system_config(); + opts->source.scope = CONFIG_SCOPE_SYSTEM; + } else if (opts->use_local_config) { + opts->source.file = opts->file_to_free = git_pathdup("config"); + opts->source.scope = CONFIG_SCOPE_LOCAL; + } else if (opts->use_worktree_config) { struct worktree **worktrees = get_worktrees(); if (the_repository->repository_format_worktree_config) - given_config_source.file = git_pathdup("config.worktree"); + opts->source.file = opts->file_to_free = + git_pathdup("config.worktree"); else if (worktrees[0] && worktrees[1]) die(_("--worktree cannot be used with multiple " "working trees unless the config\n" @@ -719,145 +789,102 @@ static void handle_config_location(const char *prefix) "Please read \"CONFIGURATION FILE\"\n" "section in \"git help worktree\" for details")); else - given_config_source.file = git_pathdup("config"); - given_config_source.scope = CONFIG_SCOPE_LOCAL; + opts->source.file = opts->file_to_free = + git_pathdup("config"); + opts->source.scope = CONFIG_SCOPE_LOCAL; free_worktrees(worktrees); - } else if (given_config_source.file) { - if (!is_absolute_path(given_config_source.file) && prefix) - given_config_source.file = - prefix_filename(prefix, given_config_source.file); - given_config_source.scope = CONFIG_SCOPE_COMMAND; - } else if (given_config_source.blob) { - given_config_source.scope = CONFIG_SCOPE_COMMAND; + } else if (opts->source.file) { + if (!is_absolute_path(opts->source.file) && prefix) + opts->source.file = opts->file_to_free = + prefix_filename(prefix, opts->source.file); + opts->source.scope = CONFIG_SCOPE_COMMAND; + } else if (opts->source.blob) { + opts->source.scope = CONFIG_SCOPE_COMMAND; } - if (respect_includes_opt == -1) - config_options.respect_includes = !given_config_source.file; + if (opts->respect_includes_opt == -1) + opts->options.respect_includes = !opts->source.file; else - config_options.respect_includes = respect_includes_opt; + opts->options.respect_includes = opts->respect_includes_opt; if (startup_info->have_repository) { - config_options.commondir = get_git_common_dir(); - config_options.git_dir = get_git_dir(); + opts->options.commondir = get_git_common_dir(); + opts->options.git_dir = get_git_dir(); } } -static void handle_nul(void) { - if (end_nul) { - term = '\0'; - delim = '\n'; - key_delim = '\n'; - } +static void location_options_release(struct config_location_options *opts) +{ + free(opts->file_to_free); } -#define CONFIG_LOCATION_OPTIONS \ - OPT_GROUP(N_("Config file location")), \ - OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), \ - OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), \ - OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), \ - OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), \ - OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), \ - OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")) - -#define CONFIG_TYPE_OPTIONS \ - OPT_GROUP(N_("Type")), \ - OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \ - OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \ - OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \ - OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \ - OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \ - OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \ - OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE) - -#define CONFIG_DISPLAY_OPTIONS \ - OPT_GROUP(N_("Display options")), \ - OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")), \ - OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), \ - OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), \ - OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")) - -static struct option builtin_config_options[] = { - CONFIG_LOCATION_OPTIONS, - OPT_GROUP(N_("Action")), - OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET), - OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL), - OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP), - OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), - OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL), - OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), - OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET), - OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL), - OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), - OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), - OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST), - OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT), - OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR), - OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL), - CONFIG_TYPE_OPTIONS, - CONFIG_DISPLAY_OPTIONS, - OPT_GROUP(N_("Other")), - OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), - OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")), - OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")), - OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), - OPT_END(), -}; - -static NORETURN void usage_builtin_config(void) +static void display_options_init(struct config_display_options *opts) { - usage_with_options(builtin_config_usage, builtin_config_options); + if (opts->end_nul) { + opts->term = '\0'; + opts->delim = '\n'; + opts->key_delim = '\n'; + } } static int cmd_config_list(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; + struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, - CONFIG_DISPLAY_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), + CONFIG_DISPLAY_OPTIONS(display_opts), OPT_GROUP(N_("Other")), - OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), + OPT_BOOL(0, "includes", &location_opts.respect_includes_opt, + N_("respect include directives on lookup")), OPT_END(), }; argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0); check_argc(argc, 0, 0); - handle_config_location(prefix); - handle_nul(); + location_options_init(&location_opts, prefix); + display_options_init(&display_opts); setup_auto_pager("config", 1); - if (config_with_options(show_all_config, NULL, - &given_config_source, the_repository, - &config_options) < 0) { - if (given_config_source.file) + if (config_with_options(show_all_config, &display_opts, + &location_opts.source, the_repository, + &location_opts.options) < 0) { + if (location_opts.source.file) die_errno(_("unable to read config file '%s'"), - given_config_source.file); + location_opts.source.file); else die(_("error processing config file(s)")); } + location_options_release(&location_opts); return 0; } static int cmd_config_get(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; + struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT; const char *value_pattern = NULL, *url = NULL; int flags = 0; + unsigned get_value_flags = 0; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, - CONFIG_TYPE_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), OPT_GROUP(N_("Filter options")), - OPT_BOOL(0, "all", &do_all, N_("return all values for multi-valued config options")), - OPT_BOOL(0, "regexp", &use_key_regexp, N_("interpret the name as a regular expression")), + OPT_BIT(0, "all", &get_value_flags, N_("return all values for multi-valued config options"), GET_VALUE_ALL), + OPT_BIT(0, "regexp", &get_value_flags, N_("interpret the name as a regular expression"), GET_VALUE_KEY_REGEXP), OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")), OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE), OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")), - CONFIG_DISPLAY_OPTIONS, - OPT_BOOL(0, "show-names", &show_keys, N_("show config keys in addition to their values")), + CONFIG_DISPLAY_OPTIONS(display_opts), OPT_GROUP(N_("Other")), - OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), - OPT_STRING(0, "default", &default_value, N_("value"), N_("use default value when missing entry")), + OPT_BOOL(0, "includes", &location_opts.respect_includes_opt, + N_("respect include directives on lookup")), + OPT_STRING(0, "default", &display_opts.default_value, + N_("value"), N_("use default value when missing entry")), OPT_END(), }; + int ret; argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -865,29 +892,38 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix) if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern) die(_("--fixed-value only applies with 'value-pattern'")); - if (default_value && (do_all || url)) + if (display_opts.default_value && + ((get_value_flags & GET_VALUE_ALL) || url)) die(_("--default= cannot be used with --all or --url=")); - if (url && (do_all || use_key_regexp || value_pattern)) + if (url && ((get_value_flags & GET_VALUE_ALL) || + (get_value_flags & GET_VALUE_KEY_REGEXP) || + value_pattern)) die(_("--url= cannot be used with --all, --regexp or --value")); - handle_config_location(prefix); - handle_nul(); + location_options_init(&location_opts, prefix); + display_options_init(&display_opts); setup_auto_pager("config", 1); if (url) - return get_urlmatch(argv[0], url); - return get_value(argv[0], value_pattern, flags); + ret = get_urlmatch(&location_opts, &display_opts, argv[0], url); + else + ret = get_value(&location_opts, &display_opts, argv[0], value_pattern, + get_value_flags, flags); + + location_options_release(&location_opts); + return ret; } static int cmd_config_set(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; const char *value_pattern = NULL, *comment_arg = NULL; char *comment = NULL; - int flags = 0, append = 0; + int flags = 0, append = 0, type = 0; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, - CONFIG_TYPE_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), + CONFIG_TYPE_OPTIONS(type), OPT_GROUP(N_("Filter")), OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE), OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")), @@ -903,7 +939,6 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage, PARSE_OPT_STOP_AT_NON_OPTION); - check_write(); check_argc(argc, 2, 2); if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern) @@ -915,22 +950,24 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix) comment = git_config_prepare_comment_string(comment_arg); - handle_config_location(prefix); + location_options_init(&location_opts, prefix); + check_write(&location_opts.source); - value = normalize_value(argv[0], argv[1], &default_kvi); + value = normalize_value(argv[0], argv[1], type, &default_kvi); if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) { - ret = git_config_set_multivar_in_file_gently(given_config_source.file, + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, argv[0], value, value_pattern, comment, flags); } else { - ret = git_config_set_in_file_gently(given_config_source.file, + ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s."), argv[0]); } + location_options_release(&location_opts); free(comment); free(value); return ret; @@ -938,101 +975,114 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix) static int cmd_config_unset(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; const char *value_pattern = NULL; int flags = 0; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), OPT_GROUP(N_("Filter")), OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE), OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")), OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE), OPT_END(), }; + int ret; argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage, PARSE_OPT_STOP_AT_NON_OPTION); - check_write(); check_argc(argc, 1, 1); if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern) die(_("--fixed-value only applies with 'value-pattern'")); - handle_config_location(prefix); + location_options_init(&location_opts, prefix); + check_write(&location_opts.source); if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, value_pattern, - NULL, flags); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, + argv[0], NULL, value_pattern, + NULL, flags); else - return git_config_set_in_file_gently(given_config_source.file, argv[0], - NULL, NULL); + ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], + NULL, NULL); + + location_options_release(&location_opts); + return ret; } static int cmd_config_rename_section(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), OPT_END(), }; int ret; argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage, PARSE_OPT_STOP_AT_NON_OPTION); - check_write(); check_argc(argc, 2, 2); - handle_config_location(prefix); + location_options_init(&location_opts, prefix); + check_write(&location_opts.source); - ret = git_config_rename_section_in_file(given_config_source.file, + ret = git_config_rename_section_in_file(location_opts.source.file, argv[0], argv[1]); if (ret < 0) - return ret; + goto out; else if (!ret) die(_("no such section: %s"), argv[0]); + ret = 0; - return 0; +out: + location_options_release(&location_opts); + return ret; } static int cmd_config_remove_section(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), OPT_END(), }; int ret; argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage, PARSE_OPT_STOP_AT_NON_OPTION); - check_write(); check_argc(argc, 1, 1); - handle_config_location(prefix); + location_options_init(&location_opts, prefix); + check_write(&location_opts.source); - ret = git_config_rename_section_in_file(given_config_source.file, + ret = git_config_rename_section_in_file(location_opts.source.file, argv[0], NULL); if (ret < 0) - return ret; + goto out; else if (!ret) die(_("no such section: %s"), argv[0]); + ret = 0; - return 0; +out: + location_options_release(&location_opts); + return ret; } -static int show_editor(void) +static int show_editor(struct config_location_options *opts) { char *config_file; - if (!given_config_source.file && !startup_info->have_repository) + if (!opts->source.file && !startup_info->have_repository) die(_("not in a git directory")); - if (given_config_source.use_stdin) + if (opts->source.use_stdin) die(_("editing stdin is not supported")); - if (given_config_source.blob) + if (opts->source.blob) die(_("editing blobs is not supported")); git_config(git_default_config, NULL); - config_file = given_config_source.file ? - xstrdup(given_config_source.file) : + config_file = opts->source.file ? + xstrdup(opts->source.file) : git_pathdup("config"); - if (use_global_config) { + if (opts->use_global_config) { int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666); if (fd >= 0) { char *content = default_user_config(); @@ -1051,66 +1101,90 @@ static int show_editor(void) static int cmd_config_edit(int argc, const char **argv, const char *prefix) { + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { - CONFIG_LOCATION_OPTIONS, + CONFIG_LOCATION_OPTIONS(location_opts), OPT_END(), }; + int ret; argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0); - check_write(); check_argc(argc, 0, 0); - handle_config_location(prefix); + location_options_init(&location_opts, prefix); + check_write(&location_opts.source); - return show_editor(); + ret = show_editor(&location_opts); + location_options_release(&location_opts); + return ret; } -static struct option builtin_subcommand_options[] = { - OPT_SUBCOMMAND("list", &subcommand, cmd_config_list), - OPT_SUBCOMMAND("get", &subcommand, cmd_config_get), - OPT_SUBCOMMAND("set", &subcommand, cmd_config_set), - OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset), - OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section), - OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section), - OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit), - OPT_END(), -}; - -int cmd_config(int argc, const char **argv, const char *prefix) +static int cmd_config_actions(int argc, const char **argv, const char *prefix) { + enum { + ACTION_GET = (1<<0), + ACTION_GET_ALL = (1<<1), + ACTION_GET_REGEXP = (1<<2), + ACTION_REPLACE_ALL = (1<<3), + ACTION_ADD = (1<<4), + ACTION_UNSET = (1<<5), + ACTION_UNSET_ALL = (1<<6), + ACTION_RENAME_SECTION = (1<<7), + ACTION_REMOVE_SECTION = (1<<8), + ACTION_LIST = (1<<9), + ACTION_EDIT = (1<<10), + ACTION_SET = (1<<11), + ACTION_SET_ALL = (1<<12), + ACTION_GET_COLOR = (1<<13), + ACTION_GET_COLORBOOL = (1<<14), + ACTION_GET_URLMATCH = (1<<15), + }; + struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; + struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT; + const char *comment_arg = NULL; + int actions = 0; + unsigned flags = 0; + struct option opts[] = { + CONFIG_LOCATION_OPTIONS(location_opts), + OPT_GROUP(N_("Action")), + OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET), + OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL), + OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP), + OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), + OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL), + OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), + OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET), + OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL), + OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), + OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), + OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST), + OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT), + OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR), + OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL), + CONFIG_DISPLAY_OPTIONS(display_opts), + OPT_GROUP(N_("Other")), + OPT_STRING(0, "default", &display_opts.default_value, + N_("value"), N_("with --get, use default value when missing entry")), + OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")), + OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE), + OPT_BOOL(0, "includes", &location_opts.respect_includes_opt, + N_("respect include directives on lookup")), + OPT_END(), + }; char *value = NULL, *comment = NULL; - int flags = 0; int ret = 0; struct key_value_info default_kvi = KVI_INIT; - given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); - - /* - * This is somewhat hacky: we first parse the command line while - * keeping all args intact in order to determine whether a subcommand - * has been specified. If so, we re-parse it a second time, but this - * time we drop KEEP_ARGV0. This is so that we don't munge the command - * line in case no subcommand was given, which would otherwise confuse - * us when parsing the legacy-style modes that don't use subcommands. - */ - argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage, - PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT); - if (subcommand) { - argc = parse_options(argc, argv, prefix, builtin_subcommand_options, builtin_config_usage, - PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT); - return subcommand(argc, argv, prefix); - } - - argc = parse_options(argc, argv, prefix, builtin_config_options, + argc = parse_options(argc, argv, prefix, opts, builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); - handle_config_location(prefix); - handle_nul(); + location_options_init(&location_opts, prefix); + display_options_init(&display_opts); - if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) { + if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) { error(_("--get-color and variable type are incoherent")); - usage_builtin_config(); + exit(129); } if (actions == 0) @@ -1119,34 +1193,35 @@ int cmd_config(int argc, const char **argv, const char *prefix) case 2: actions = ACTION_SET; break; case 3: actions = ACTION_SET_ALL; break; default: - usage_builtin_config(); + error(_("no action specified")); + exit(129); } - if (omit_values && + if (display_opts.omit_values && !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) { error(_("--name-only is only applicable to --list or --get-regexp")); - usage_builtin_config(); + exit(129); } - if (show_origin && !(actions & + if (display_opts.show_origin && !(actions & (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { error(_("--show-origin is only applicable to --get, --get-all, " "--get-regexp, and --list")); - usage_builtin_config(); + exit(129); } - if (default_value && !(actions & ACTION_GET)) { + if (display_opts.default_value && !(actions & ACTION_GET)) { error(_("--default is only applicable to --get")); - usage_builtin_config(); + exit(129); } if (comment_arg && !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) { error(_("--comment is only applicable to add/set/replace operations")); - usage_builtin_config(); + exit(129); } /* check usage of --fixed-value */ - if (fixed_value) { + if (flags & CONFIG_FLAGS_FIXED_VALUE) { int allowed_usage = 0; switch (actions) { @@ -1175,123 +1250,125 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (!allowed_usage) { error(_("--fixed-value only applies with 'value-pattern'")); - usage_builtin_config(); + exit(129); } - - flags |= CONFIG_FLAGS_FIXED_VALUE; } comment = git_config_prepare_comment_string(comment_arg); - if (actions & PAGING_ACTIONS) + /* + * The following actions may produce more than one line of output and + * should therefore be paged. + */ + if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH)) setup_auto_pager("config", 1); if (actions == ACTION_LIST) { check_argc(argc, 0, 0); - if (config_with_options(show_all_config, NULL, - &given_config_source, the_repository, - &config_options) < 0) { - if (given_config_source.file) + if (config_with_options(show_all_config, &display_opts, + &location_opts.source, the_repository, + &location_opts.options) < 0) { + if (location_opts.source.file) die_errno(_("unable to read config file '%s'"), - given_config_source.file); + location_opts.source.file); else die(_("error processing config file(s)")); } } else if (actions == ACTION_EDIT) { - ret = show_editor(); + ret = show_editor(&location_opts); } else if (actions == ACTION_SET) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1], &default_kvi); - ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value); + value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); + ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s."), argv[0]); } else if (actions == ACTION_SET_ALL) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1], &default_kvi); - ret = git_config_set_multivar_in_file_gently(given_config_source.file, + value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, argv[0], value, argv[2], comment, flags); } else if (actions == ACTION_ADD) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1], &default_kvi); - ret = git_config_set_multivar_in_file_gently(given_config_source.file, + value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, argv[0], value, CONFIG_REGEX_NONE, comment, flags); } else if (actions == ACTION_REPLACE_ALL) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1], &default_kvi); - ret = git_config_set_multivar_in_file_gently(given_config_source.file, + value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, argv[0], value, argv[2], comment, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); - return get_value(argv[0], argv[1], flags); + ret = get_value(&location_opts, &display_opts, argv[0], argv[1], + 0, flags); } else if (actions == ACTION_GET_ALL) { - do_all = 1; check_argc(argc, 1, 2); - return get_value(argv[0], argv[1], flags); + ret = get_value(&location_opts, &display_opts, argv[0], argv[1], + GET_VALUE_ALL, flags); } else if (actions == ACTION_GET_REGEXP) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; + display_opts.show_keys = 1; check_argc(argc, 1, 2); - return get_value(argv[0], argv[1], flags); + ret = get_value(&location_opts, &display_opts, argv[0], argv[1], + GET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags); } else if (actions == ACTION_GET_URLMATCH) { check_argc(argc, 2, 2); - return get_urlmatch(argv[0], argv[1]); + ret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]); } else if (actions == ACTION_UNSET) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 1, 2); if (argc == 2) - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], - NULL, flags); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, + argv[0], NULL, argv[1], + NULL, flags); else - return git_config_set_in_file_gently(given_config_source.file, - argv[0], NULL, NULL); + ret = git_config_set_in_file_gently(location_opts.source.file, + argv[0], NULL, NULL); } else if (actions == ACTION_UNSET_ALL) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 1, 2); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], - NULL, flags | CONFIG_FLAGS_MULTI_REPLACE); + ret = git_config_set_multivar_in_file_gently(location_opts.source.file, + argv[0], NULL, argv[1], + NULL, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 2, 2); - ret = git_config_rename_section_in_file(given_config_source.file, + ret = git_config_rename_section_in_file(location_opts.source.file, argv[0], argv[1]); if (ret < 0) - return ret; + goto out; else if (!ret) die(_("no such section: %s"), argv[0]); else ret = 0; } else if (actions == ACTION_REMOVE_SECTION) { - check_write(); + check_write(&location_opts.source); check_argc(argc, 1, 1); - ret = git_config_rename_section_in_file(given_config_source.file, + ret = git_config_rename_section_in_file(location_opts.source.file, argv[0], NULL); if (ret < 0) - return ret; + goto out; else if (!ret) die(_("no such section: %s"), argv[0]); else @@ -1299,16 +1376,51 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_GET_COLOR) { check_argc(argc, 1, 2); - get_color(argv[0], argv[1]); + get_color(&location_opts, argv[0], argv[1]); } else if (actions == ACTION_GET_COLORBOOL) { check_argc(argc, 1, 2); if (argc == 2) color_stdout_is_tty = git_config_bool("command line", argv[1]); - return get_colorbool(argv[0], argc == 2); + ret = get_colorbool(&location_opts, argv[0], argc == 2); } +out: + location_options_release(&location_opts); free(comment); free(value); return ret; } + +int cmd_config(int argc, const char **argv, const char *prefix) +{ + parse_opt_subcommand_fn *subcommand = NULL; + struct option subcommand_opts[] = { + OPT_SUBCOMMAND("list", &subcommand, cmd_config_list), + OPT_SUBCOMMAND("get", &subcommand, cmd_config_get), + OPT_SUBCOMMAND("set", &subcommand, cmd_config_set), + OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset), + OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section), + OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section), + OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit), + OPT_END(), + }; + + /* + * This is somewhat hacky: we first parse the command line while + * keeping all args intact in order to determine whether a subcommand + * has been specified. If so, we re-parse it a second time, but this + * time we drop KEEP_ARGV0. This is so that we don't munge the command + * line in case no subcommand was given, which would otherwise confuse + * us when parsing the legacy-style modes that don't use subcommands. + */ + argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage, + PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT); + if (subcommand) { + argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage, + PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT); + return subcommand(argc, argv, prefix); + } + + return cmd_config_actions(argc, argv, prefix); +} diff --git a/builtin/credential.c b/builtin/credential.c index 5100d441f2..b72e76dd9a 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -39,5 +39,7 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED) } else { usage(usage_msg); } + + credential_clear(&c); return 0; } diff --git a/builtin/describe.c b/builtin/describe.c index e5287eddf2..cf8edc4222 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -53,6 +53,10 @@ static const char *diff_index_args[] = { "diff-index", "--quiet", "HEAD", "--", NULL }; +static const char *update_index_args[] = { + "update-index", "--unmerged", "-q", "--refresh", NULL +}; + struct commit_name { struct hashmap_entry entry; struct object_id peeled; @@ -645,6 +649,14 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (argc == 0) { if (broken) { struct child_process cp = CHILD_PROCESS_INIT; + + strvec_pushv(&cp.args, update_index_args); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.no_stdout = 1; + run_command(&cp); + + child_process_init(&cp); strvec_pushv(&cp.args, diff_index_args); cp.git_cmd = 1; cp.no_stdin = 1; diff --git a/builtin/diagnose.c b/builtin/diagnose.c index 4f22eb2b55..4857a4395b 100644 --- a/builtin/diagnose.c +++ b/builtin/diagnose.c @@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix) struct tm tm; enum diagnose_mode mode = DIAGNOSE_STATS; char *option_output = NULL; - char *option_suffix = "%Y-%m-%d-%H%M"; + const char *option_suffix = "%Y-%m-%d-%H%M"; char *prefixed_filename; const struct option diagnose_options[] = { diff --git a/builtin/diff.c b/builtin/diff.c index efc37483b3..9b6cdabe15 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -465,6 +465,15 @@ int cmd_diff(int argc, const char **argv, const char *prefix) no_index = DIFF_NO_INDEX_IMPLICIT; } + /* + * When operating outside of a Git repository we need to have a hash + * algorithm at hand so that we can generate the blob hashes. We + * default to SHA1 here, but may eventually want to change this to be + * configurable via a command line option. + */ + if (nongit) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); prefix = precompose_argv_prefix(argc, argv, prefix); diff --git a/builtin/difftool.c b/builtin/difftool.c index a130faae4f..dcc68e190c 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -662,6 +662,9 @@ finish: free(lbase_dir); free(rbase_dir); + strbuf_release(&info); + strbuf_release(&lpath); + strbuf_release(&rpath); strbuf_release(&ldir); strbuf_release(&rdir); strbuf_release(&wtdir); @@ -674,19 +677,15 @@ finish: static int run_file_diff(int prompt, const char *prefix, struct child_process *child) { - const char *env[] = { - "GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL, - NULL - }; - + strvec_push(&child->env, "GIT_PAGER="); + strvec_push(&child->env, "GIT_EXTERNAL_DIFF=git-difftool--helper"); if (prompt > 0) - env[2] = "GIT_DIFFTOOL_PROMPT=true"; + strvec_push(&child->env, "GIT_DIFFTOOL_PROMPT=true"); else if (!prompt) - env[2] = "GIT_DIFFTOOL_NO_PROMPT=true"; + strvec_push(&child->env, "GIT_DIFFTOOL_NO_PROMPT=true"); child->git_cmd = 1; child->dir = prefix; - strvec_pushv(&child->env, env); return run_command(child); } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 4693d18cc9..4b6e8c6832 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -415,7 +415,7 @@ static char *generate_fake_oid(void) struct object_id oid; char *hex = xmallocz(GIT_MAX_HEXSZ); - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); put_be32(oid.hash + hashsz - 4, counter++); return oid_to_hex_r(hex, &oid); } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index d1c0243d04..d21c4053a7 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1279,8 +1279,10 @@ static void load_tree(struct tree_entry *root) e->versions[0].mode = e->versions[1].mode; e->name = to_atom(c, strlen(c)); c += e->name->str_len + 1; - oidread(&e->versions[0].oid, (unsigned char *)c); - oidread(&e->versions[1].oid, (unsigned char *)c); + oidread(&e->versions[0].oid, (unsigned char *)c, + the_repository->hash_algo); + oidread(&e->versions[1].oid, (unsigned char *)c, + the_repository->hash_algo); c += the_hash_algo->rawsz; } free(buf); @@ -1386,7 +1388,7 @@ static void tree_content_replace( { if (!S_ISDIR(mode)) die("Root cannot be a non-directory"); - oidclr(&root->versions[0].oid); + oidclr(&root->versions[0].oid, the_repository->hash_algo); oidcpy(&root->versions[1].oid, oid); if (root->tree) release_tree_content_recursive(root->tree); @@ -1445,7 +1447,7 @@ static int tree_content_set( if (S_ISDIR(e->versions[0].mode)) e->versions[0].mode |= NO_DELTA; - oidclr(&root->versions[1].oid); + oidclr(&root->versions[1].oid, the_repository->hash_algo); return 1; } if (!S_ISDIR(e->versions[1].mode)) { @@ -1455,7 +1457,7 @@ static int tree_content_set( if (!e->tree) load_tree(e); if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) { - oidclr(&root->versions[1].oid); + oidclr(&root->versions[1].oid, the_repository->hash_algo); return 1; } return 0; @@ -1467,7 +1469,7 @@ static int tree_content_set( e = new_tree_entry(); e->name = to_atom(p, n); e->versions[0].mode = 0; - oidclr(&e->versions[0].oid); + oidclr(&e->versions[0].oid, the_repository->hash_algo); t->entries[t->entry_count++] = e; if (*slash1) { e->tree = new_tree_content(8); @@ -1478,7 +1480,7 @@ static int tree_content_set( e->versions[1].mode = mode; oidcpy(&e->versions[1].oid, oid); } - oidclr(&root->versions[1].oid); + oidclr(&root->versions[1].oid, the_repository->hash_algo); return 1; } @@ -1523,7 +1525,8 @@ static int tree_content_remove( if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { for (n = 0; n < e->tree->entry_count; n++) { if (e->tree->entries[n]->versions[1].mode) { - oidclr(&root->versions[1].oid); + oidclr(&root->versions[1].oid, + the_repository->hash_algo); return 1; } } @@ -1542,8 +1545,8 @@ del_entry: release_tree_content_recursive(e->tree); e->tree = NULL; e->versions[1].mode = 0; - oidclr(&e->versions[1].oid); - oidclr(&root->versions[1].oid); + oidclr(&e->versions[1].oid, the_repository->hash_algo); + oidclr(&root->versions[1].oid, the_repository->hash_algo); return 1; } @@ -1609,7 +1612,7 @@ static int update_branch(struct branch *b) return 0; } if (refs_read_ref(get_main_ref_store(the_repository), b->name, &old_oid)) - oidclr(&old_oid); + oidclr(&old_oid, the_repository->hash_algo); if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; int ret; @@ -2358,7 +2361,9 @@ static void file_change_m(const char *p, struct branch *b) parse_path_eol(&path, p, "path"); /* Git does not track empty, non-toplevel directories. */ - if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *path.buf) { + if (S_ISDIR(mode) && + is_empty_tree_oid(&oid, the_repository->hash_algo) && + *path.buf) { tree_content_remove(&b->branch_tree, path.buf, NULL, 0); return; } @@ -2550,8 +2555,8 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa static void file_change_deleteall(struct branch *b) { release_tree_content_recursive(b->branch_tree.tree); - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); + oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo); + oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo); load_tree(&b->branch_tree); b->num_notes = 0; } @@ -2570,8 +2575,8 @@ static void parse_from_commit(struct branch *b, char *buf, unsigned long size) static void parse_from_existing(struct branch *b) { if (is_null_oid(&b->oid)) { - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); + oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo); + oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo); } else { unsigned long size; char *buf; @@ -2894,9 +2899,9 @@ static void parse_reset_branch(const char *arg) b = lookup_branch(arg); if (b) { - oidclr(&b->oid); - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); + oidclr(&b->oid, the_repository->hash_algo); + oidclr(&b->branch_tree.versions[0].oid, the_repository->hash_algo); + oidclr(&b->branch_tree.versions[1].oid, the_repository->hash_algo); if (b->branch_tree.tree) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 44c05ee86c..af329e8d5c 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -29,11 +29,11 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, ; /* <oid>, leave oid as name */ } else { /* <ref>, clear cruft from oid */ - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); } } else { /* <ref>, clear cruft from get_oid_hex */ - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); } ref = alloc_ref(name); diff --git a/builtin/fetch.c b/builtin/fetch.c index a319954f9f..693f02b958 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote, } } - if (tags == TAGS_SET) + if (tags == TAGS_SET) { + struct refspec_item tag_refspec; + /* also fetch all tags */ - get_fetch_map(remote_refs, tag_refspec, &tail, 0); - else if (tags == TAGS_DEFAULT && *autotags) + refspec_item_init(&tag_refspec, TAG_REFSPEC, 0); + get_fetch_map(remote_refs, &tag_refspec, &tail, 0); + refspec_item_clear(&tag_refspec); + } else if (tags == TAGS_DEFAULT && *autotags) { find_non_local_tags(remote_refs, NULL, &ref_map, &tail); + } /* Now append any refs to be updated opportunistically: */ *tail = orefs; @@ -1386,8 +1391,8 @@ static int prune_refs(struct display_state *display_state, if (!dry_run) { if (transaction) { for (ref = stale_refs; ref; ref = ref->next) { - result = ref_transaction_delete(transaction, ref->name, NULL, 0, - "fetch: prune", &err); + result = ref_transaction_delete(transaction, ref->name, NULL, + NULL, 0, "fetch: prune", &err); if (result) goto cleanup; } diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 0f9855b680..957786d1b3 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -11,7 +11,7 @@ static const char * const fmt_merge_msg_usage[] = { int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) { - const char *inpath = NULL; + char *inpath = NULL; const char *message = NULL; char *into_name = NULL; int shortlog_len = -1; @@ -66,5 +66,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) if (ret) return ret; write_in_full(STDOUT_FILENO, output.buf, output.len); + + free(inpath); return 0; } diff --git a/builtin/grep.c b/builtin/grep.c index 5777ba82a9..dfc3c3e8bd 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1114,7 +1114,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) for (i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; - struct object_context oc; + struct object_context oc = {0}; struct object *object; if (!strcmp(arg, "--")) { @@ -1140,7 +1140,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); - free(oc.path); + object_context_release(&oc); } /* diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 82ca6d2bfd..c767414a0c 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -123,6 +123,9 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix) else prefix = setup_git_directory_gently(&nongit); + if (nongit && !the_hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + if (vpath && prefix) { vpath_free = prefix_filename(prefix, vpath); vpath = vpath_free; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 856428fef9..fd968d673d 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -528,7 +528,8 @@ static void *unpack_raw_entry(struct object_entry *obj, switch (obj->type) { case OBJ_REF_DELTA: - oidread(ref_oid, fill(the_hash_algo->rawsz)); + oidread(ref_oid, fill(the_hash_algo->rawsz), + the_repository->hash_algo); use(the_hash_algo->rawsz); break; case OBJ_OFS_DELTA: @@ -1204,7 +1205,7 @@ static void parse_pack_objects(unsigned char *hash) the_hash_algo->init_fn(&tmp_ctx); the_hash_algo->clone_fn(&tmp_ctx, &input_ctx); the_hash_algo->final_fn(hash, &tmp_ctx); - if (!hasheq(fill(the_hash_algo->rawsz), hash)) + if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo)) die(_("pack is corrupted (SHA1 mismatch)")); use(the_hash_algo->rawsz); @@ -1307,11 +1308,11 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha stop_progress_msg(&progress, msg.buf); strbuf_release(&msg); finalize_hashfile(f, tail_hash, FSYNC_COMPONENT_PACK, 0); - hashcpy(read_hash, pack_hash); + hashcpy(read_hash, pack_hash, the_repository->hash_algo); fixup_pack_header_footer(output_fd, pack_hash, curr_pack, nr_objects, read_hash, consumed_bytes-the_hash_algo->rawsz); - if (!hasheq(read_hash, tail_hash)) + if (!hasheq(read_hash, tail_hash, the_repository->hash_algo)) die(_("Unexpected tail checksum for %s " "(disk corruption?)"), curr_pack); } @@ -1372,7 +1373,7 @@ static struct object_entry *append_obj_to_pack(struct hashfile *f, obj[1].idx.offset += write_compressed(f, buf, size); obj[0].idx.crc32 = crc32_end(f); hashflush(f); - oidread(&obj->idx.oid, sha1); + oidread(&obj->idx.oid, sha1, the_repository->hash_algo); return obj; } diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 8768bfea3c..1d969494cf 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -141,7 +141,7 @@ static void interpret_trailers(const struct process_trailer_options *opts, LIST_HEAD(head); struct strbuf sb = STRBUF_INIT; struct strbuf trailer_block = STRBUF_INIT; - struct trailer_info info; + struct trailer_info *info; FILE *outfile = stdout; trailer_config_init(); @@ -151,13 +151,13 @@ static void interpret_trailers(const struct process_trailer_options *opts, if (opts->in_place) outfile = create_in_place_tempfile(file); - parse_trailers(opts, &info, sb.buf, &head); + info = parse_trailers(opts, sb.buf, &head); /* Print the lines before the trailers */ if (!opts->only_trailers) - fwrite(sb.buf, 1, info.trailer_block_start, outfile); + fwrite(sb.buf, 1, trailer_block_start(info), outfile); - if (!opts->only_trailers && !info.blank_line_before_trailer) + if (!opts->only_trailers && !blank_line_before_trailer_block(info)) fprintf(outfile, "\n"); @@ -178,8 +178,8 @@ static void interpret_trailers(const struct process_trailer_options *opts, /* Print the lines after the trailers as is */ if (!opts->only_trailers) - fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile); - trailer_info_release(&info); + fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile); + trailer_info_release(info); if (opts->in_place) if (rename_tempfile(&trailers_tempfile, file)) diff --git a/builtin/log.c b/builtin/log.c index b17dd8b40a..4d4b60caa7 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -4,7 +4,7 @@ * (C) Copyright 2006 Linus Torvalds * 2006 Junio Hamano */ -#include "git-compat-util.h" +#include "builtin.h" #include "abspath.h" #include "config.h" #include "environment.h" @@ -48,22 +48,8 @@ #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100 #define FORMAT_PATCH_NAME_MAX_DEFAULT 64 -/* Set a default date-time format for git log ("log.date" config variable) */ -static const char *default_date_mode = NULL; - -static int default_abbrev_commit; -static int default_show_root = 1; -static int default_follow; -static int default_show_signature; -static int default_encode_email_headers = 1; -static int decoration_style; -static int decoration_given; -static int use_mailmap_config = 1; static unsigned int force_in_body_from; static int stdout_mboxrd; -static const char *fmt_patch_subject_prefix = "PATCH"; -static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; -static const char *fmt_pretty; static int format_no_prefix; static const char * const builtin_log_usage[] = { @@ -111,6 +97,39 @@ static int parse_decoration_style(const char *value) return -1; } +struct log_config { + int default_abbrev_commit; + int default_show_root; + int default_follow; + int default_show_signature; + int default_encode_email_headers; + int decoration_style; + int decoration_given; + int use_mailmap_config; + char *fmt_patch_subject_prefix; + int fmt_patch_name_max; + char *fmt_pretty; + char *default_date_mode; +}; + +static void log_config_init(struct log_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + cfg->default_show_root = 1; + cfg->default_encode_email_headers = 1; + cfg->use_mailmap_config = 1; + cfg->fmt_patch_subject_prefix = xstrdup("PATCH"); + cfg->fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; + cfg->decoration_style = auto_decoration_style(); +} + +static void log_config_release(struct log_config *cfg) +{ + free(cfg->default_date_mode); + free(cfg->fmt_pretty); + free(cfg->fmt_patch_subject_prefix); +} + static int use_default_decoration_filter = 1; static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; @@ -127,20 +146,22 @@ static int clear_decorations_callback(const struct option *opt UNUSED, return 0; } -static int decorate_callback(const struct option *opt UNUSED, const char *arg, +static int decorate_callback(const struct option *opt, const char *arg, int unset) { + struct log_config *cfg = opt->value; + if (unset) - decoration_style = 0; + cfg->decoration_style = 0; else if (arg) - decoration_style = parse_decoration_style(arg); + cfg->decoration_style = parse_decoration_style(arg); else - decoration_style = DECORATE_SHORT_REFS; + cfg->decoration_style = DECORATE_SHORT_REFS; - if (decoration_style < 0) + if (cfg->decoration_style < 0) die(_("invalid --decorate option: %s"), arg); - decoration_given = 1; + cfg->decoration_given = 1; return 0; } @@ -160,32 +181,26 @@ static int log_line_range_callback(const struct option *option, const char *arg, return 0; } -static void init_log_defaults(void) -{ - init_diff_ui_defaults(); - - decoration_style = auto_decoration_style(); -} - -static void cmd_log_init_defaults(struct rev_info *rev) +static void cmd_log_init_defaults(struct rev_info *rev, + struct log_config *cfg) { - if (fmt_pretty) - get_commit_format(fmt_pretty, rev); - if (default_follow) + if (cfg->fmt_pretty) + get_commit_format(cfg->fmt_pretty, rev); + if (cfg->default_follow) rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; init_diffstat_widths(&rev->diffopt); rev->diffopt.flags.recursive = 1; rev->diffopt.flags.allow_textconv = 1; - rev->abbrev_commit = default_abbrev_commit; - rev->show_root_diff = default_show_root; - rev->subject_prefix = fmt_patch_subject_prefix; - rev->patch_name_max = fmt_patch_name_max; - rev->show_signature = default_show_signature; - rev->encode_email_headers = default_encode_email_headers; + rev->abbrev_commit = cfg->default_abbrev_commit; + rev->show_root_diff = cfg->default_show_root; + rev->subject_prefix = cfg->fmt_patch_subject_prefix; + rev->patch_name_max = cfg->fmt_patch_name_max; + rev->show_signature = cfg->default_show_signature; + rev->encode_email_headers = cfg->default_encode_email_headers; - if (default_date_mode) - parse_date_format(default_date_mode, &rev->date_mode); + if (cfg->default_date_mode) + parse_date_format(cfg->default_date_mode, &rev->date_mode); } static void set_default_decoration_filter(struct decoration_filter *decoration_filter) @@ -233,7 +248,8 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f } static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) + struct rev_info *rev, struct setup_revision_opt *opt, + struct log_config *cfg) { struct userformat_want w; int quiet = 0, source = 0, mailmap; @@ -258,7 +274,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, N_("pattern"), N_("only decorate refs that match <pattern>")), OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude, N_("pattern"), N_("do not decorate refs that match <pattern>")), - OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"), + OPT_CALLBACK_F(0, "decorate", cfg, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback), OPT_CALLBACK('L', NULL, &line_cb, "range:file", N_("trace the evolution of line range <start>,<end> or function :<funcname> in <file>"), @@ -269,7 +285,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, line_cb.rev = rev; line_cb.prefix = prefix; - mailmap = use_mailmap_config; + mailmap = cfg->use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | @@ -314,8 +330,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, * "log --pretty=raw" is special; ignore UI oriented * configuration variables such as decoration. */ - if (!decoration_given) - decoration_style = 0; + if (!cfg->decoration_given) + cfg->decoration_style = 0; if (!rev->abbrev_commit_given) rev->abbrev_commit = 0; } @@ -326,24 +342,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, * Disable decoration loading if the format will not * show them anyway. */ - decoration_style = 0; - } else if (!decoration_style) { + cfg->decoration_style = 0; + } else if (!cfg->decoration_style) { /* * If we are going to show them, make sure we do load * them here, but taking care not to override a * specific style set by config or --decorate. */ - decoration_style = DECORATE_SHORT_REFS; + cfg->decoration_style = DECORATE_SHORT_REFS; } } - if (decoration_style || rev->simplify_by_decoration) { + if (cfg->decoration_style || rev->simplify_by_decoration) { set_default_decoration_filter(&decoration_filter); - if (decoration_style) + if (cfg->decoration_style) rev->show_decorations = 1; - load_ref_decorations(&decoration_filter, decoration_style); + load_ref_decorations(&decoration_filter, cfg->decoration_style); } if (rev->line_level_traverse) @@ -353,16 +369,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, } static void cmd_log_init(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) + struct rev_info *rev, struct setup_revision_opt *opt, + struct log_config *cfg) { - cmd_log_init_defaults(rev); - cmd_log_init_finish(argc, argv, prefix, rev, opt); -} - -static int cmd_log_deinit(int ret, struct rev_info *rev) -{ - release_revisions(rev); - return ret; + cmd_log_init_defaults(rev, cfg); + cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg); } /* @@ -566,30 +577,37 @@ static int cmd_log_walk(struct rev_info *rev) static int git_log_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + struct log_config *cfg = cb; const char *slot_name; - if (!strcmp(var, "format.pretty")) - return git_config_string(&fmt_pretty, var, value); - if (!strcmp(var, "format.subjectprefix")) - return git_config_string(&fmt_patch_subject_prefix, var, value); + if (!strcmp(var, "format.pretty")) { + FREE_AND_NULL(cfg->fmt_pretty); + return git_config_string(&cfg->fmt_pretty, var, value); + } + if (!strcmp(var, "format.subjectprefix")) { + FREE_AND_NULL(cfg->fmt_patch_subject_prefix); + return git_config_string(&cfg->fmt_patch_subject_prefix, var, value); + } if (!strcmp(var, "format.filenamemaxlength")) { - fmt_patch_name_max = git_config_int(var, value, ctx->kvi); + cfg->fmt_patch_name_max = git_config_int(var, value, ctx->kvi); return 0; } if (!strcmp(var, "format.encodeemailheaders")) { - default_encode_email_headers = git_config_bool(var, value); + cfg->default_encode_email_headers = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.abbrevcommit")) { - default_abbrev_commit = git_config_bool(var, value); + cfg->default_abbrev_commit = git_config_bool(var, value); return 0; } - if (!strcmp(var, "log.date")) - return git_config_string(&default_date_mode, var, value); + if (!strcmp(var, "log.date")) { + FREE_AND_NULL(cfg->default_date_mode); + return git_config_string(&cfg->default_date_mode, var, value); + } if (!strcmp(var, "log.decorate")) { - decoration_style = parse_decoration_style(value); - if (decoration_style < 0) - decoration_style = 0; /* maybe warn? */ + cfg->decoration_style = parse_decoration_style(value); + if (cfg->decoration_style < 0) + cfg->decoration_style = 0; /* maybe warn? */ return 0; } if (!strcmp(var, "log.diffmerges")) { @@ -598,21 +616,21 @@ static int git_log_config(const char *var, const char *value, return diff_merges_config(value); } if (!strcmp(var, "log.showroot")) { - default_show_root = git_config_bool(var, value); + cfg->default_show_root = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.follow")) { - default_follow = git_config_bool(var, value); + cfg->default_follow = git_config_bool(var, value); return 0; } if (skip_prefix(var, "color.decorate.", &slot_name)) return parse_decorate_color_config(var, slot_name, value); if (!strcmp(var, "log.mailmap")) { - use_mailmap_config = git_config_bool(var, value); + cfg->use_mailmap_config = git_config_bool(var, value); return 0; } if (!strcmp(var, "log.showsignature")) { - default_show_signature = git_config_bool(var, value); + cfg->default_show_signature = git_config_bool(var, value); return 0; } @@ -621,11 +639,14 @@ static int git_log_config(const char *var, const char *value, int cmd_whatchanged(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); @@ -635,10 +656,15 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; - cmd_log_init(argc, argv, prefix, &rev, &opt); + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + + ret = cmd_log_walk(&rev); + + release_revisions(&rev); + log_config_release(&cfg); + return ret; } static void show_tagger(const char *buf, struct rev_info *rev) @@ -656,7 +682,7 @@ static void show_tagger(const char *buf, struct rev_info *rev) static int show_blob_object(const struct object_id *oid, struct rev_info *rev, const char *obj_name) { struct object_id oidc; - struct object_context obj_context; + struct object_context obj_context = {0}; char *buf; unsigned long size; @@ -672,7 +698,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c if (!obj_context.path || !textconv_object(the_repository, obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) { - free(obj_context.path); + object_context_release(&obj_context); return stream_blob_to_fd(1, oid, NULL, 0); } @@ -680,7 +706,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c die(_("git show %s: bad file"), obj_name); write_or_die(1, buf, size); - free(obj_context.path); + object_context_release(&obj_context); return 0; } @@ -733,14 +759,16 @@ static void show_setup_revisions_tweak(struct rev_info *rev) int cmd_show(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; unsigned int i; struct setup_revision_opt opt; struct pathspec match_all; int ret = 0; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); if (the_repository->gitdir) { prepare_repo_settings(the_repository); @@ -759,10 +787,14 @@ int cmd_show(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.tweak = show_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); - if (!rev.no_walk) - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + if (!rev.no_walk) { + ret = cmd_log_walk(&rev); + release_revisions(&rev); + log_config_release(&cfg); + return ret; + } rev.diffopt.no_free = 1; for (i = 0; i < rev.pending.nr && !ret; i++) { @@ -832,8 +864,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) rev.diffopt.no_free = 0; diff_free(&rev.diffopt); + release_revisions(&rev); + log_config_release(&cfg); - return cmd_log_deinit(ret, &rev); + return ret; } /* @@ -841,11 +875,14 @@ int cmd_show(int argc, const char **argv, const char *prefix) */ int cmd_log_reflog(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); init_reflog_walk(&rev.reflog_info); @@ -854,14 +891,18 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) rev.verbose_header = 1; memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; - cmd_log_init_defaults(&rev); + cmd_log_init_defaults(&rev, &cfg); rev.abbrev_commit = 1; rev.commit_format = CMIT_FMT_ONELINE; rev.use_terminator = 1; rev.always_show_header = 1; - cmd_log_init_finish(argc, argv, prefix, &rev, &opt); + cmd_log_init_finish(argc, argv, prefix, &rev, &opt, &cfg); - return cmd_log_deinit(cmd_log_walk(&rev), &rev); + ret = cmd_log_walk(&rev); + + release_revisions(&rev); + log_config_release(&cfg); + return ret; } static void log_setup_revisions_tweak(struct rev_info *rev) @@ -876,11 +917,14 @@ static void log_setup_revisions_tweak(struct rev_info *rev) int cmd_log(int argc, const char **argv, const char *prefix) { + struct log_config cfg; struct rev_info rev; struct setup_revision_opt opt; + int ret; - init_log_defaults(); - git_config(git_log_config, NULL); + log_config_init(&cfg); + init_diff_ui_defaults(); + git_config(git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); @@ -890,42 +934,17 @@ int cmd_log(int argc, const char **argv, const char *prefix) opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; opt.tweak = log_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); - return cmd_log_deinit(cmd_log_walk(&rev), &rev); -} - -/* format-patch */ - -static const char *fmt_patch_suffix = ".patch"; -static int numbered = 0; -static int auto_number = 1; - -static char *default_attach = NULL; - -static struct string_list extra_hdr = STRING_LIST_INIT_NODUP; -static struct string_list extra_to = STRING_LIST_INIT_NODUP; -static struct string_list extra_cc = STRING_LIST_INIT_NODUP; + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); -static void add_header(const char *value) -{ - struct string_list_item *item; - int len = strlen(value); - while (len && value[len - 1] == '\n') - len--; - - if (!strncasecmp(value, "to: ", 4)) { - item = string_list_append(&extra_to, value + 4); - len -= 4; - } else if (!strncasecmp(value, "cc: ", 4)) { - item = string_list_append(&extra_cc, value + 4); - len -= 4; - } else { - item = string_list_append(&extra_hdr, value); - } + ret = cmd_log_walk(&rev); - item->string[len] = '\0'; + release_revisions(&rev); + log_config_release(&cfg); + return ret; } +/* format-patch */ + enum cover_setting { COVER_UNSET, COVER_OFF, @@ -952,17 +971,61 @@ enum auto_base_setting { AUTO_BASE_WHEN_ABLE }; -static enum thread_level thread; -static int do_signoff; -static enum auto_base_setting auto_base; -static char *from; -static const char *signature = git_version_string; -static const char *signature_file; -static enum cover_setting config_cover_letter; -static const char *config_output_directory; -static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE; -static int show_notes; -static struct display_notes_opt notes_opt; +struct format_config { + struct log_config log; + enum thread_level thread; + int do_signoff; + enum auto_base_setting auto_base; + char *base_commit; + char *from; + char *signature; + char *signature_file; + enum cover_setting config_cover_letter; + char *config_output_directory; + enum cover_from_description cover_from_description_mode; + int show_notes; + struct display_notes_opt notes_opt; + int numbered_cmdline_opt; + int numbered; + int auto_number; + char *default_attach; + struct string_list extra_hdr; + struct string_list extra_to; + struct string_list extra_cc; + int keep_subject; + int subject_prefix; + struct strbuf sprefix; + char *fmt_patch_suffix; +}; + +static void format_config_init(struct format_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + log_config_init(&cfg->log); + cfg->cover_from_description_mode = COVER_FROM_MESSAGE; + cfg->auto_number = 1; + string_list_init_dup(&cfg->extra_hdr); + string_list_init_dup(&cfg->extra_to); + string_list_init_dup(&cfg->extra_cc); + strbuf_init(&cfg->sprefix, 0); + cfg->fmt_patch_suffix = xstrdup(".patch"); +} + +static void format_config_release(struct format_config *cfg) +{ + log_config_release(&cfg->log); + free(cfg->base_commit); + free(cfg->from); + free(cfg->signature); + free(cfg->signature_file); + free(cfg->config_output_directory); + free(cfg->default_attach); + string_list_clear(&cfg->extra_hdr, 0); + string_list_clear(&cfg->extra_to, 0); + string_list_clear(&cfg->extra_cc, 0); + strbuf_release(&cfg->sprefix); + free(cfg->fmt_patch_suffix); +} static enum cover_from_description parse_cover_from_description(const char *arg) { @@ -980,27 +1043,51 @@ static enum cover_from_description parse_cover_from_description(const char *arg) die(_("%s: invalid cover from description mode"), arg); } +static void add_header(struct format_config *cfg, const char *value) +{ + struct string_list_item *item; + int len = strlen(value); + while (len && value[len - 1] == '\n') + len--; + + if (!strncasecmp(value, "to: ", 4)) { + item = string_list_append(&cfg->extra_to, value + 4); + len -= 4; + } else if (!strncasecmp(value, "cc: ", 4)) { + item = string_list_append(&cfg->extra_cc, value + 4); + len -= 4; + } else { + item = string_list_append(&cfg->extra_hdr, value); + } + + item->string[len] = '\0'; +} + static int git_format_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + struct format_config *cfg = cb; + if (!strcmp(var, "format.headers")) { if (!value) die(_("format.headers without value")); - add_header(value); + add_header(cfg, value); return 0; } - if (!strcmp(var, "format.suffix")) - return git_config_string(&fmt_patch_suffix, var, value); + if (!strcmp(var, "format.suffix")) { + FREE_AND_NULL(cfg->fmt_patch_suffix); + return git_config_string(&cfg->fmt_patch_suffix, var, value); + } if (!strcmp(var, "format.to")) { if (!value) return config_error_nonbool(var); - string_list_append(&extra_to, value); + string_list_append(&cfg->extra_to, value); return 0; } if (!strcmp(var, "format.cc")) { if (!value) return config_error_nonbool(var); - string_list_append(&extra_cc, value); + string_list_append(&cfg->extra_cc, value); return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") || @@ -1009,69 +1096,76 @@ static int git_format_config(const char *var, const char *value, } if (!strcmp(var, "format.numbered")) { if (value && !strcasecmp(value, "auto")) { - auto_number = 1; + cfg->auto_number = 1; return 0; } - numbered = git_config_bool(var, value); - auto_number = auto_number && numbered; + cfg->numbered = git_config_bool(var, value); + cfg->auto_number = cfg->auto_number && cfg->numbered; return 0; } if (!strcmp(var, "format.attach")) { - if (value && *value) - default_attach = xstrdup(value); - else if (value && !*value) - FREE_AND_NULL(default_attach); - else - default_attach = xstrdup(git_version_string); + if (value && *value) { + FREE_AND_NULL(cfg->default_attach); + cfg->default_attach = xstrdup(value); + } else if (value && !*value) { + FREE_AND_NULL(cfg->default_attach); + } else { + FREE_AND_NULL(cfg->default_attach); + cfg->default_attach = xstrdup(git_version_string); + } return 0; } if (!strcmp(var, "format.thread")) { if (value && !strcasecmp(value, "deep")) { - thread = THREAD_DEEP; + cfg->thread = THREAD_DEEP; return 0; } if (value && !strcasecmp(value, "shallow")) { - thread = THREAD_SHALLOW; + cfg->thread = THREAD_SHALLOW; return 0; } - thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET; + cfg->thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET; return 0; } if (!strcmp(var, "format.signoff")) { - do_signoff = git_config_bool(var, value); + cfg->do_signoff = git_config_bool(var, value); return 0; } - if (!strcmp(var, "format.signature")) - return git_config_string(&signature, var, value); - if (!strcmp(var, "format.signaturefile")) - return git_config_pathname(&signature_file, var, value); + if (!strcmp(var, "format.signature")) { + FREE_AND_NULL(cfg->signature); + return git_config_string(&cfg->signature, var, value); + } + if (!strcmp(var, "format.signaturefile")) { + FREE_AND_NULL(cfg->signature_file); + return git_config_pathname(&cfg->signature_file, var, value); + } if (!strcmp(var, "format.coverletter")) { if (value && !strcasecmp(value, "auto")) { - config_cover_letter = COVER_AUTO; + cfg->config_cover_letter = COVER_AUTO; return 0; } - config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; + cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; return 0; } - if (!strcmp(var, "format.outputdirectory")) - return git_config_string(&config_output_directory, var, value); + if (!strcmp(var, "format.outputdirectory")) { + FREE_AND_NULL(cfg->config_output_directory); + return git_config_string(&cfg->config_output_directory, var, value); + } if (!strcmp(var, "format.useautobase")) { if (value && !strcasecmp(value, "whenAble")) { - auto_base = AUTO_BASE_WHEN_ABLE; + cfg->auto_base = AUTO_BASE_WHEN_ABLE; return 0; } - auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER; + cfg->auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER; return 0; } if (!strcmp(var, "format.from")) { int b = git_parse_maybe_bool(value); - free(from); + FREE_AND_NULL(cfg->from); if (b < 0) - from = xstrdup(value); + cfg->from = xstrdup(value); else if (b) - from = xstrdup(git_committer_info(IDENT_NO_DATE)); - else - from = NULL; + cfg->from = xstrdup(git_committer_info(IDENT_NO_DATE)); return 0; } if (!strcmp(var, "format.forceinbodyfrom")) { @@ -1081,15 +1175,15 @@ static int git_format_config(const char *var, const char *value, if (!strcmp(var, "format.notes")) { int b = git_parse_maybe_bool(value); if (b < 0) - enable_ref_display_notes(¬es_opt, &show_notes, value); + enable_ref_display_notes(&cfg->notes_opt, &cfg->show_notes, value); else if (b) - enable_default_display_notes(¬es_opt, &show_notes); + enable_default_display_notes(&cfg->notes_opt, &cfg->show_notes); else - disable_display_notes(¬es_opt, &show_notes); + disable_display_notes(&cfg->notes_opt, &cfg->show_notes); return 0; } if (!strcmp(var, "format.coverfromdescription")) { - cover_from_description_mode = parse_cover_from_description(value); + cfg->cover_from_description_mode = parse_cover_from_description(value); return 0; } if (!strcmp(var, "format.mboxrd")) { @@ -1110,7 +1204,7 @@ static int git_format_config(const char *var, const char *value, if (!strcmp(var, "diff.noprefix")) return 0; - return git_log_config(var, value, ctx, cb); + return git_log_config(var, value, ctx, &cfg->log); } static const char *output_directory = NULL; @@ -1189,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) o2->flags = flags2; } -static void gen_message_id(struct rev_info *info, char *base) +static void gen_message_id(struct rev_info *info, const char *base) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s.%"PRItime".git.%s", base, @@ -1198,7 +1292,7 @@ static void gen_message_id(struct rev_info *info, char *base) info->message_id = strbuf_detach(&buf, NULL); } -static void print_signature(FILE *file) +static void print_signature(const char *signature, FILE *file) { if (!signature || !*signature) return; @@ -1268,14 +1362,15 @@ static void prepare_cover_text(struct pretty_print_context *pp, const char *branch_name, struct strbuf *sb, const char *encoding, - int need_8bit_cte) + int need_8bit_cte, + const struct format_config *cfg) { const char *subject = "*** SUBJECT HERE ***"; const char *body = "*** BLURB HERE ***"; struct strbuf description_sb = STRBUF_INIT; struct strbuf subject_sb = STRBUF_INIT; - if (cover_from_description_mode == COVER_FROM_NONE) + if (cfg->cover_from_description_mode == COVER_FROM_NONE) goto do_pp; if (description_file && *description_file) @@ -1285,13 +1380,13 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (!description_sb.len) goto do_pp; - if (cover_from_description_mode == COVER_FROM_SUBJECT || - cover_from_description_mode == COVER_FROM_AUTO) + if (cfg->cover_from_description_mode == COVER_FROM_SUBJECT || + cfg->cover_from_description_mode == COVER_FROM_AUTO) body = format_subject(&subject_sb, description_sb.buf, " "); - if (cover_from_description_mode == COVER_FROM_MESSAGE || - (cover_from_description_mode == COVER_FROM_AUTO && - subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN)) + if (cfg->cover_from_description_mode == COVER_FROM_MESSAGE || + (cfg->cover_from_description_mode == COVER_FROM_AUTO && + subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN)) body = description_sb.buf; else subject = subject_sb.buf; @@ -1328,7 +1423,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, int nr, struct commit **list, const char *description_file, const char *branch_name, - int quiet) + int quiet, + const struct format_config *cfg) { const char *committer; struct shortlog log; @@ -1367,7 +1463,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.encode_email_headers = rev->encode_email_headers; pp_user_info(&pp, NULL, &sb, committer, encoding); prepare_cover_text(&pp, description_file, branch_name, &sb, - encoding, need_8bit_cte); + encoding, need_8bit_cte, cfg); fprintf(rev->diffopt.file, "%s\n", sb.buf); free(pp.after_subject); @@ -1468,29 +1564,30 @@ static const char * const builtin_format_patch_usage[] = { NULL }; -static int keep_subject = 0; +struct keep_callback_data { + struct format_config *cfg; + struct rev_info *revs; +}; static int keep_callback(const struct option *opt, const char *arg, int unset) { + struct keep_callback_data *data = opt->value; BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - ((struct rev_info *)opt->value)->total = -1; - keep_subject = 1; + data->revs->total = -1; + data->cfg->keep_subject = 1; return 0; } -static int subject_prefix = 0; - static int subject_prefix_callback(const struct option *opt, const char *arg, int unset) { - struct strbuf *sprefix; + struct format_config *cfg = opt->value; BUG_ON_OPT_NEG(unset); - sprefix = opt->value; - subject_prefix = 1; - strbuf_reset(sprefix); - strbuf_addstr(sprefix, arg); + cfg->subject_prefix = 1; + strbuf_reset(&cfg->sprefix); + strbuf_addstr(&cfg->sprefix, arg); return 0; } @@ -1507,15 +1604,14 @@ static int rfc_callback(const struct option *opt, const char *arg, return 0; } -static int numbered_cmdline_opt = 0; - static int numbered_callback(const struct option *opt, const char *arg, int unset) { + struct format_config *cfg = opt->value; BUG_ON_OPT_ARG(arg); - *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1; + cfg->numbered = cfg->numbered_cmdline_opt = unset ? 0 : 1; if (unset) - auto_number = 0; + cfg->auto_number = 0; return 0; } @@ -1539,13 +1635,14 @@ static int output_directory_callback(const struct option *opt, const char *arg, static int thread_callback(const struct option *opt, const char *arg, int unset) { - enum thread_level *thread = (enum thread_level *)opt->value; + struct format_config *cfg = opt->value; + if (unset) - *thread = THREAD_UNSET; + cfg->thread = THREAD_UNSET; else if (!arg || !strcmp(arg, "shallow")) - *thread = THREAD_SHALLOW; + cfg->thread = THREAD_SHALLOW; else if (!strcmp(arg, "deep")) - *thread = THREAD_DEEP; + cfg->thread = THREAD_DEEP; /* * Please update _git_formatpatch() in git-completion.bash * when you add new options. @@ -1581,15 +1678,17 @@ static int inline_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int header_callback(const struct option *opt UNUSED, const char *arg, +static int header_callback(const struct option *opt, const char *arg, int unset) { + struct format_config *cfg = opt->value; + if (unset) { - string_list_clear(&extra_hdr, 0); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); + string_list_clear(&cfg->extra_hdr, 0); + string_list_clear(&cfg->extra_to, 0); + string_list_clear(&cfg->extra_cc, 0); } else { - add_header(arg); + add_header(cfg, arg); } return 0; } @@ -1611,17 +1710,17 @@ static int from_callback(const struct option *opt, const char *arg, int unset) static int base_callback(const struct option *opt, const char *arg, int unset) { - const char **base_commit = opt->value; + struct format_config *cfg = opt->value; if (unset) { - auto_base = AUTO_BASE_NEVER; - *base_commit = NULL; + cfg->auto_base = AUTO_BASE_NEVER; + FREE_AND_NULL(cfg->base_commit); } else if (!strcmp(arg, "auto")) { - auto_base = AUTO_BASE_ALWAYS; - *base_commit = NULL; + cfg->auto_base = AUTO_BASE_ALWAYS; + FREE_AND_NULL(cfg->base_commit); } else { - auto_base = AUTO_BASE_NEVER; - *base_commit = arg; + cfg->auto_base = AUTO_BASE_NEVER; + cfg->base_commit = xstrdup(arg); } return 0; } @@ -1632,7 +1731,7 @@ struct base_tree_info { struct object_id *patch_id; }; -static struct commit *get_base_commit(const char *base_commit, +static struct commit *get_base_commit(const struct format_config *cfg, struct commit **list, int total) { @@ -1640,9 +1739,9 @@ static struct commit *get_base_commit(const char *base_commit, struct commit **rev; int i = 0, rev_nr = 0, auto_select, die_on_failure, ret; - switch (auto_base) { + switch (cfg->auto_base) { case AUTO_BASE_NEVER: - if (base_commit) { + if (cfg->base_commit) { auto_select = 0; die_on_failure = 1; } else { @@ -1652,11 +1751,11 @@ static struct commit *get_base_commit(const char *base_commit, break; case AUTO_BASE_ALWAYS: case AUTO_BASE_WHEN_ABLE: - if (base_commit) { + if (cfg->base_commit) { BUG("requested automatic base selection but a commit was provided"); } else { auto_select = 1; - die_on_failure = auto_base == AUTO_BASE_ALWAYS; + die_on_failure = cfg->auto_base == AUTO_BASE_ALWAYS; } break; default: @@ -1664,9 +1763,9 @@ static struct commit *get_base_commit(const char *base_commit, } if (!auto_select) { - base = lookup_commit_reference_by_name(base_commit); + base = lookup_commit_reference_by_name(cfg->base_commit); if (!base) - die(_("unknown commit %s"), base_commit); + die(_("unknown commit %s"), cfg->base_commit); } else { struct branch *curr_branch = branch_get(NULL); const char *upstream = branch_get_upstream(curr_branch, NULL); @@ -1839,7 +1938,7 @@ static void print_bases(struct base_tree_info *bases, FILE *file) free(bases->patch_id); bases->nr_patch_id = 0; bases->alloc_patch_id = 0; - oidclr(&bases->base_commit); + oidclr(&bases->base_commit, the_repository->hash_algo); } static const char *diff_title(struct strbuf *sb, @@ -1884,6 +1983,7 @@ static void infer_range_diff_ranges(struct strbuf *r1, int cmd_format_patch(int argc, const char **argv, const char *prefix) { + struct format_config cfg; struct commit *commit; struct commit **list = NULL; struct rev_info rev; @@ -1908,7 +2008,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) char *cover_from_description_arg = NULL; char *description_file = NULL; char *branch_name = NULL; - char *base_commit = NULL; struct base_tree_info bases; struct commit *base; int show_progress = 0; @@ -1919,18 +2018,24 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf rdiff1 = STRBUF_INIT; struct strbuf rdiff2 = STRBUF_INIT; struct strbuf rdiff_title = STRBUF_INIT; - struct strbuf sprefix = STRBUF_INIT; const char *rfc = NULL; int creation_factor = -1; + const char *signature = git_version_string; + char *signature_file_arg = NULL; + struct keep_callback_data keep_callback_data = { + .cfg = &cfg, + .revs = &rev, + }; + const char *fmt_patch_suffix = NULL; const struct option builtin_format_patch_options[] = { - OPT_CALLBACK_F('n', "numbered", &numbered, NULL, + OPT_CALLBACK_F('n', "numbered", &cfg, NULL, N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback), - OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL, + OPT_CALLBACK_F('N', "no-numbered", &cfg, NULL, N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback), - OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")), + OPT_BOOL('s', "signoff", &cfg.do_signoff, N_("add a Signed-off-by trailer")), OPT_BOOL(0, "stdout", &use_stdout, N_("print patches to standard out")), OPT_BOOL(0, "cover-letter", &cover_letter, @@ -1943,7 +2048,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("start numbering patches at <n> instead of 1")), OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"), N_("mark the series as Nth re-roll")), - OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max, + OPT_INTEGER(0, "filename-max-length", &cfg.log.fmt_patch_name_max, N_("max length of output filename")), OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"), N_("add <rfc> (default 'RFC') before 'PATCH'"), @@ -1953,13 +2058,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("generate parts of a cover letter based on a branch's description")), OPT_FILENAME(0, "description-file", &description_file, N_("use branch description from file")), - OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"), + OPT_CALLBACK_F(0, "subject-prefix", &cfg, N_("prefix"), N_("use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), OPT_CALLBACK_F('o', "output-directory", &output_directory, N_("dir"), N_("store resulting files in <dir>"), PARSE_OPT_NONEG, output_directory_callback), - OPT_CALLBACK_F('k', "keep-subject", &rev, NULL, + OPT_CALLBACK_F('k', "keep-subject", &keep_callback_data, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback), OPT_BOOL(0, "no-binary", &no_binary_diff, @@ -1972,11 +2077,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("show patch format instead of default (patch + stat)"), 1, PARSE_OPT_NONEG), OPT_GROUP(N_("Messaging")), - OPT_CALLBACK(0, "add-header", NULL, N_("header"), + OPT_CALLBACK(0, "add-header", &cfg, N_("header"), N_("add email header"), header_callback), - OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")), - OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")), - OPT_CALLBACK_F(0, "from", &from, N_("ident"), + OPT_STRING_LIST(0, "to", &cfg.extra_to, N_("email"), N_("add To: header")), + OPT_STRING_LIST(0, "cc", &cfg.extra_cc, N_("email"), N_("add Cc: header")), + OPT_CALLBACK_F(0, "from", &cfg.from, N_("ident"), N_("set From address to <ident> (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback), OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), @@ -1988,15 +2093,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback), - OPT_CALLBACK_F(0, "thread", &thread, N_("style"), + OPT_CALLBACK_F(0, "thread", &cfg, N_("style"), N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback), OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), - OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"), + OPT_CALLBACK_F(0, "base", &cfg, N_("base-commit"), N_("add prerequisite tree info to the patch series"), 0, base_callback), - OPT_FILENAME(0, "signature-file", &signature_file, + OPT_FILENAME(0, "signature-file", &signature_file_arg, N_("add a signature from a file")), OPT__QUIET(&quiet, N_("don't print the patch filenames")), OPT_BOOL(0, "progress", &show_progress, @@ -2013,20 +2118,17 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_END() }; - extra_hdr.strdup_strings = 1; - extra_to.strdup_strings = 1; - extra_cc.strdup_strings = 1; - - init_log_defaults(); - init_display_notes(¬es_opt); - git_config(git_format_config, NULL); + format_config_init(&cfg); + init_diff_ui_defaults(); + init_display_notes(&cfg.notes_opt); + git_config(git_format_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); git_config(grep_config, &rev.grep_filter); - rev.show_notes = show_notes; - memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt)); + rev.show_notes = cfg.show_notes; + memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt)); rev.commit_format = CMIT_FMT_EMAIL; - rev.encode_email_headers = default_encode_email_headers; + rev.encode_email_headers = cfg.log.default_encode_email_headers; rev.expand_tabs_in_log_default = 0; rev.verbose_header = 1; rev.diff = 1; @@ -2037,12 +2139,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; - strbuf_addstr(&sprefix, fmt_patch_subject_prefix); + strbuf_addstr(&cfg.sprefix, cfg.log.fmt_patch_subject_prefix); if (format_no_prefix) diff_set_noprefix(&rev.diffopt); - if (default_attach) { - rev.mime_boundary = default_attach; + if (cfg.default_attach) { + rev.mime_boundary = cfg.default_attach; rev.no_inline = 1; } @@ -2058,60 +2160,63 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.force_in_body_from = force_in_body_from; + if (!fmt_patch_suffix) + fmt_patch_suffix = cfg.fmt_patch_suffix; + /* Make sure "0000-$sub.patch" gives non-negative length for $sub */ - if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) - fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); + if (cfg.log.fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) + cfg.log.fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); if (cover_from_description_arg) - cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); + cfg.cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); if (rfc && rfc[0]) { - subject_prefix = 1; + cfg.subject_prefix = 1; if (rfc[0] == '-') - strbuf_addf(&sprefix, " %s", rfc + 1); + strbuf_addf(&cfg.sprefix, " %s", rfc + 1); else - strbuf_insertf(&sprefix, 0, "%s ", rfc); + strbuf_insertf(&cfg.sprefix, 0, "%s ", rfc); } if (reroll_count) { - strbuf_addf(&sprefix, " v%s", reroll_count); + strbuf_addf(&cfg.sprefix, " v%s", reroll_count); rev.reroll_count = reroll_count; } - rev.subject_prefix = sprefix.buf; + rev.subject_prefix = cfg.sprefix.buf; - for (i = 0; i < extra_hdr.nr; i++) { - strbuf_addstr(&buf, extra_hdr.items[i].string); + for (i = 0; i < cfg.extra_hdr.nr; i++) { + strbuf_addstr(&buf, cfg.extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); } - if (extra_to.nr) + if (cfg.extra_to.nr) strbuf_addstr(&buf, "To: "); - for (i = 0; i < extra_to.nr; i++) { + for (i = 0; i < cfg.extra_to.nr; i++) { if (i) strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_to.items[i].string); - if (i + 1 < extra_to.nr) + strbuf_addstr(&buf, cfg.extra_to.items[i].string); + if (i + 1 < cfg.extra_to.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } - if (extra_cc.nr) + if (cfg.extra_cc.nr) strbuf_addstr(&buf, "Cc: "); - for (i = 0; i < extra_cc.nr; i++) { + for (i = 0; i < cfg.extra_cc.nr; i++) { if (i) strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_cc.items[i].string); - if (i + 1 < extra_cc.nr) + strbuf_addstr(&buf, cfg.extra_cc.items[i].string); + if (i + 1 < cfg.extra_cc.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = to_free = strbuf_detach(&buf, NULL); - if (from) { - if (split_ident_line(&rev.from_ident, from, strlen(from))) - die(_("invalid ident line: %s"), from); + if (cfg.from) { + if (split_ident_line(&rev.from_ident, cfg.from, strlen(cfg.from))) + die(_("invalid ident line: %s"), cfg.from); } if (start_number < 0) @@ -2122,14 +2227,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ - if (numbered && keep_subject && !numbered_cmdline_opt) - numbered = 0; + if (cfg.numbered && cfg.keep_subject && !cfg.numbered_cmdline_opt) + cfg.numbered = 0; - if (numbered && keep_subject) + if (cfg.numbered && cfg.keep_subject) die(_("options '%s' and '%s' cannot be used together"), "-n", "-k"); - if (keep_subject && subject_prefix) + if (cfg.keep_subject && cfg.subject_prefix) die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k"); - rev.preserve_subject = keep_subject; + rev.preserve_subject = cfg.keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) @@ -2156,7 +2261,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.always_show_header = 1; rev.zero_commit = zero_commit; - rev.patch_name_max = fmt_patch_name_max; + rev.patch_name_max = cfg.log.fmt_patch_name_max; if (!rev.diffopt.flags.text && !no_binary_diff) rev.diffopt.flags.binary = 1; @@ -2177,7 +2282,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int saved; if (!output_directory) - output_directory = config_output_directory; + output_directory = cfg.config_output_directory; output_directory = set_outdir(prefix, output_directory); if (rev.diffopt.use_color != GIT_COLOR_ALWAYS) @@ -2275,14 +2380,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) goto done; total = nr; if (cover_letter == -1) { - if (config_cover_letter == COVER_AUTO) + if (cfg.config_cover_letter == COVER_AUTO) cover_letter = (total > 1); + else if ((idiff_prev.nr || rdiff_prev) && (total > 1)) + cover_letter = (cfg.config_cover_letter != COVER_OFF); else - cover_letter = (config_cover_letter == COVER_ON); + cover_letter = (cfg.config_cover_letter == COVER_ON); } - if (!keep_subject && auto_number && (total > 1 || cover_letter)) - numbered = 1; - if (numbered) + if (!cfg.keep_subject && cfg.auto_number && (total > 1 || cover_letter)) + cfg.numbered = 1; + if (cfg.numbered) rev.total = total + start_number - 1; if (idiff_prev.nr) { @@ -2296,7 +2403,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } if (creation_factor < 0) - creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT; + creation_factor = CREATION_FACTOR_FOR_THE_SAME_SERIES; else if (!rdiff_prev) die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff"); @@ -2314,27 +2421,40 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) _("Range-diff against v%d:")); } + /* + * The order of precedence is: + * + * 1. The `--signature` and `--no-signature` options. + * 2. The `--signature-file` option. + * 3. The `format.signature` config. + * 4. The `format.signatureFile` config. + * 5. Default `git_version_string`. + */ if (!signature) { ; /* --no-signature inhibits all signatures */ } else if (signature && signature != git_version_string) { ; /* non-default signature already set */ - } else if (signature_file) { + } else if (signature_file_arg || (cfg.signature_file && !cfg.signature)) { struct strbuf buf = STRBUF_INIT; + const char *signature_file = signature_file_arg ? + signature_file_arg : cfg.signature_file; if (strbuf_read_file(&buf, signature_file, 128) < 0) die_errno(_("unable to read signature file '%s'"), signature_file); signature = strbuf_detach(&buf, NULL); + } else if (cfg.signature) { + signature = cfg.signature; } memset(&bases, 0, sizeof(bases)); - base = get_base_commit(base_commit, list, nr); + base = get_base_commit(&cfg, list, nr); if (base) { reset_revision_walk(); clear_object_flags(UNINTERESTING); prepare_bases(&bases, base, list, nr); } - if (in_reply_to || thread || cover_letter) { + if (in_reply_to || cfg.thread || cover_letter) { rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids)); string_list_init_dup(rev.ref_message_ids); } @@ -2345,19 +2465,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { - if (thread) + if (cfg.thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, description_file, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet, &cfg); print_bases(&bases, rev.diffopt.file); - print_signature(rev.diffopt.file); + print_signature(signature, rev.diffopt.file); total++; start_number--; /* interdiff/range-diff in cover-letter; omit from patches */ rev.idiff_oid1 = NULL; rev.rdiff1 = NULL; } - rev.add_signoff = do_signoff; + rev.add_signoff = cfg.do_signoff; if (show_progress) progress = start_delayed_progress(_("Generating patches"), total); @@ -2367,7 +2487,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ - if (thread) { + if (cfg.thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* @@ -2391,7 +2511,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * letter is a reply to the * --in-reply-to, if specified. */ - if (thread == THREAD_SHALLOW + if (cfg.thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); @@ -2424,7 +2544,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) mime_boundary_leader, rev.mime_boundary); else - print_signature(rev.diffopt.file); + print_signature(signature, rev.diffopt.file); } if (output_directory) fclose(rev.diffopt.file); @@ -2432,9 +2552,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) stop_progress(&progress); free(list); free(branch_name); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); - string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); @@ -2444,13 +2561,16 @@ done: strbuf_release(&rdiff1); strbuf_release(&rdiff2); strbuf_release(&rdiff_title); - strbuf_release(&sprefix); + free(description_file); + free(signature_file_arg); free(to_free); free(rev.message_id); if (rev.ref_message_ids) string_list_clear(rev.ref_message_ids, 0); free(rev.ref_message_ids); - return cmd_log_deinit(0, &rev); + release_revisions(&rev); + format_config_release(&cfg); + return 0; } static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) @@ -2557,16 +2677,16 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) commit_list_insert(commit, &list); } - while (list) { + for (struct commit_list *l = list; l; l = l->next) { char sign = '+'; - commit = list->item; + commit = l->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; print_commit(sign, commit, verbose, abbrev, revs.diffopt.file); - list = list->next; } + free_commit_list(list); free_patch_ids(&ids); return 0; } diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index e8d65ebbdc..debf2d4f88 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -9,7 +9,7 @@ #include "wildmatch.h" static const char * const ls_remote_usage[] = { - N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" + N_("git ls-remote [--branches] [--tags] [--refs] [--upload-pack=<exec>]\n" " [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n" " [--symref] [<repository> [<patterns>...]]"), NULL @@ -68,7 +68,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) N_("path of git-upload-pack on the remote host"), PARSE_OPT_HIDDEN }, OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS), - OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS), + OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES), + OPT_BIT_F('h', "heads", &flags, + N_("deprecated synonym for --branches"), REF_BRANCHES, + PARSE_OPT_HIDDEN), OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL), OPT_BOOL(0, "get-url", &get_url, N_("take url.<base>.insteadOf into account")), @@ -100,7 +103,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (flags & REF_TAGS) strvec_push(&transport_options.ref_prefixes, "refs/tags/"); - if (flags & REF_HEADS) + if (flags & REF_BRANCHES) strvec_push(&transport_options.ref_prefixes, "refs/heads/"); remote = remote_get(dest); @@ -109,11 +112,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) die("bad repository '%s'", dest); die("No remote configured to list refs from."); } - if (!remote->url_nr) - die("remote %s has no configured URL", dest); if (get_url) { - printf("%s\n", *remote->url); + printf("%s\n", remote->url.v[0]); return 0; } @@ -130,7 +131,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) } if (!dest && !quiet) - fprintf(stderr, "From %s\n", *remote->url); + fprintf(stderr, "From %s\n", remote->url.v[0]); for ( ; ref; ref = ref->next) { struct ref_array_item *item; if (!check_ref_type(ref, flags)) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 7bf84b235c..bf372c67d7 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -367,7 +367,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; - struct object_context obj_context; + struct object_context obj_context = {0}; int ret; git_config(git_default_config, NULL); @@ -441,5 +441,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options); clear_pathspec(&options.pathspec); + object_context_release(&obj_context); return ret; } diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c index 3af9ddb8ae..fe6dbc5d05 100644 --- a/builtin/mailsplit.c +++ b/builtin/mailsplit.c @@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path) DIR *dir; struct dirent *dent; char *name = NULL; - char *subs[] = { "cur", "new", NULL }; - char **sub; + const char *subs[] = { "cur", "new", NULL }; + const char **sub; int ret = -1; for (sub = subs; *sub; ++sub) { diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index c2ce044a20..82bebea15b 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -23,7 +23,7 @@ static char *better_branch_name(const char *branch) int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED) { - const struct object_id *bases[21]; + struct object_id bases[21]; unsigned bases_count = 0; int i, failed; struct object_id h1, h2; @@ -49,10 +49,8 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED) continue; } if (bases_count < ARRAY_SIZE(bases)-1) { - struct object_id *oid = xmalloc(sizeof(struct object_id)); - if (repo_get_oid(the_repository, argv[i], oid)) + if (repo_get_oid(the_repository, argv[i], &bases[bases_count++])) die(_("could not parse object '%s'"), argv[i]); - bases[bases_count++] = oid; } else warning(Q_("cannot handle more than %d base. " diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 1082d919fd..dab2fdc2a6 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -482,6 +482,7 @@ static int real_merge(struct merge_tree_options *o, die(_("refusing to merge unrelated histories")); merge_bases = reverse_commit_list(merge_bases); merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); + free_commit_list(merge_bases); } if (result.clean < 0) diff --git a/builtin/merge.c b/builtin/merge.c index e4bd65eeba..9fba27d85d 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -100,7 +100,7 @@ static struct strategy all_strategy[] = { { "subtree", NO_FAST_FORWARD | NO_TRIVIAL }, }; -static const char *pull_twohead, *pull_octopus; +static char *pull_twohead, *pull_octopus; enum ff_type { FF_NO, @@ -110,7 +110,7 @@ enum ff_type { static enum ff_type fast_forward = FF_ALLOW; -static const char *cleanup_arg; +static char *cleanup_arg; static enum commit_msg_cleanup_mode cleanup_mode; static int option_parse_message(const struct option *opt, @@ -164,7 +164,7 @@ static struct strategy *get_strategy(const char *name) { int i; struct strategy *ret; - static struct cmdnames main_cmds, other_cmds; + static struct cmdnames main_cmds = {0}, other_cmds = {0}; static int loaded; char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM"); @@ -182,10 +182,9 @@ static struct strategy *get_strategy(const char *name) return &all_strategy[i]; if (!loaded) { - struct cmdnames not_strategies; + struct cmdnames not_strategies = {0}; loaded = 1; - memset(¬_strategies, 0, sizeof(struct cmdnames)); load_command_list("git-merge-", &main_cmds, &other_cmds); for (i = 0; i < main_cmds.cnt; i++) { int j, found = 0; @@ -197,6 +196,8 @@ static struct strategy *get_strategy(const char *name) add_cmdname(¬_strategies, ent->name, ent->len); } exclude_cmds(&main_cmds, ¬_strategies); + + cmdnames_release(¬_strategies); } if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name); @@ -216,6 +217,9 @@ static struct strategy *get_strategy(const char *name) CALLOC_ARRAY(ret, 1); ret->name = xstrdup(name); ret->attr = NO_TRIVIAL; + + cmdnames_release(&main_cmds); + cmdnames_release(&other_cmds); return ret; } @@ -330,7 +334,8 @@ static void read_empty(const struct object_id *oid) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(), + strvec_pushl(&cmd.args, "read-tree", "-m", "-u", + empty_tree_oid_hex(the_repository->hash_algo), oid_to_hex(oid), NULL); cmd.git_cmd = 1; @@ -494,7 +499,7 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_branchname(&bname, remote, 0); remote = bname.buf; - oidclr(&branch_head); + oidclr(&branch_head, the_repository->hash_algo); remote_head = get_merge_parent(remote); if (!remote_head) die(_("'%s' does not point to a commit"), remote); @@ -611,17 +616,19 @@ static int git_merge_config(const char *k, const char *v, return 0; } - if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) + if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) { show_diffstat = git_config_bool(k, v); - else if (!strcmp(k, "merge.verifysignatures")) + } else if (!strcmp(k, "merge.verifysignatures")) { verify_signatures = git_config_bool(k, v); - else if (!strcmp(k, "pull.twohead")) + } else if (!strcmp(k, "pull.twohead")) { + FREE_AND_NULL(pull_twohead); return git_config_string(&pull_twohead, k, v); - else if (!strcmp(k, "pull.octopus")) + } else if (!strcmp(k, "pull.octopus")) { + FREE_AND_NULL(pull_octopus); return git_config_string(&pull_octopus, k, v); - else if (!strcmp(k, "commit.cleanup")) + } else if (!strcmp(k, "commit.cleanup")) { return git_config_string(&cleanup_arg, k, v); - else if (!strcmp(k, "merge.ff")) { + } else if (!strcmp(k, "merge.ff")) { int boolval = git_parse_maybe_bool(v); if (0 <= boolval) { fast_forward = boolval ? FF_ALLOW : FF_NO; @@ -701,7 +708,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, SKIP_IF_UNCHANGED, 0, NULL, NULL, NULL) < 0) - return error(_("Unable to write index.")); + die(_("Unable to write index.")); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") || !strcmp(strategy, "ort")) { @@ -742,6 +749,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, else clean = merge_recursive(&o, head, remoteheads->item, reversed, &result); + free_commit_list(reversed); + if (clean < 0) { rollback_lock_file(&lock); return 2; @@ -895,7 +904,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) static int merge_trivial(struct commit *head, struct commit_list *remoteheads) { struct object_id result_tree, result_commit; - struct commit_list *parents, **pptr = &parents; + struct commit_list *parents = NULL, **pptr = &parents; if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, SKIP_IF_UNCHANGED, 0, NULL, NULL, @@ -911,7 +920,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) &result_commit, NULL, sign_commit)) die(_("failed to write commit object")); finish(head, remoteheads, &result_commit, "In-index merge"); + remove_merge_branch_state(the_repository); + free_commit_list(parents); return 0; } @@ -937,8 +948,10 @@ static int finish_automerge(struct commit *head, die(_("failed to write commit object")); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); finish(head, remoteheads, &result_commit, buf.buf); + strbuf_release(&buf); remove_merge_branch_state(the_repository); + free_commit_list(parents); return 0; } @@ -1294,7 +1307,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!pull_twohead) { char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM"); if (default_strategy && !strcmp(default_strategy, "ort")) - pull_twohead = "ort"; + pull_twohead = xstrdup("ort"); } init_diff_ui_defaults(); @@ -1690,7 +1703,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * index and working tree polluted. */ if (save_state(&stash)) - oidclr(&stash); + oidclr(&stash, the_repository->hash_algo); for (i = 0; i < use_strategies_nr; i++) { int ret, cnt; @@ -1793,6 +1806,8 @@ done: } strbuf_release(&buf); free(branch_to_free); + free(pull_twohead); + free(pull_octopus); discard_index(the_repository->index); return ret; } diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 8360932d2e..9cf1a32d65 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -50,7 +50,7 @@ static char const * const builtin_multi_pack_index_usage[] = { static struct opts_multi_pack_index { char *object_dir; const char *preferred_pack; - const char *refs_snapshot; + char *refs_snapshot; unsigned long batch_size; unsigned flags; int stdin_packs; @@ -135,6 +135,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, N_("refs snapshot for selecting bitmap commits")), OPT_END(), }; + int ret; opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; @@ -157,7 +158,6 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, if (opts.stdin_packs) { struct string_list packs = STRING_LIST_INIT_DUP; - int ret; read_packs_from_stdin(&packs); @@ -166,12 +166,17 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, opts.refs_snapshot, opts.flags); string_list_clear(&packs, 0); + free(opts.refs_snapshot); return ret; } - return write_midx_file(opts.object_dir, opts.preferred_pack, - opts.refs_snapshot, opts.flags); + + ret = write_midx_file(opts.object_dir, opts.preferred_pack, + opts.refs_snapshot, opts.flags); + + free(opts.refs_snapshot); + return ret; } static int cmd_multi_pack_index_verify(int argc, const char **argv, diff --git a/builtin/mv.c b/builtin/mv.c index 74aa9746aa..6c69033c5f 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -20,6 +20,7 @@ #include "read-cache-ll.h" #include "repository.h" #include "setup.h" +#include "strvec.h" #include "submodule.h" #include "entry.h" @@ -38,45 +39,35 @@ enum update_mode { #define DUP_BASENAME 1 #define KEEP_TRAILING_SLASH 2 -static const char **internal_prefix_pathspec(const char *prefix, - const char **pathspec, - int count, unsigned flags) +static void internal_prefix_pathspec(struct strvec *out, + const char *prefix, + const char **pathspec, + int count, unsigned flags) { - int i; - const char **result; int prefixlen = prefix ? strlen(prefix) : 0; - ALLOC_ARRAY(result, count + 1); /* Create an intermediate copy of the pathspec based on the flags */ - for (i = 0; i < count; i++) { - int length = strlen(pathspec[i]); - int to_copy = length; - char *it; + for (int i = 0; i < count; i++) { + size_t length = strlen(pathspec[i]); + size_t to_copy = length; + const char *maybe_basename; + char *trimmed, *prefixed_path; + while (!(flags & KEEP_TRAILING_SLASH) && to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1])) to_copy--; - it = xmemdupz(pathspec[i], to_copy); - if (flags & DUP_BASENAME) { - result[i] = xstrdup(basename(it)); - free(it); - } else { - result[i] = it; - } - } - result[count] = NULL; + trimmed = xmemdupz(pathspec[i], to_copy); + maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed; + prefixed_path = prefix_path(prefix, prefixlen, maybe_basename); + strvec_push(out, prefixed_path); - /* Prefix the pathspec and free the old intermediate strings */ - for (i = 0; i < count; i++) { - const char *match = prefix_path(prefix, prefixlen, result[i]); - free((char *) result[i]); - result[i] = match; + free(prefixed_path); + free(trimmed); } - - return result; } -static const char *add_slash(const char *path) +static char *add_slash(const char *path) { size_t len = strlen(path); if (len && path[len - 1] != '/') { @@ -86,32 +77,34 @@ static const char *add_slash(const char *path) with_slash[len] = 0; return with_slash; } - return path; + return xstrdup(path); } #define SUBMODULE_WITH_GITDIR ((const char *)1) -static void prepare_move_submodule(const char *src, int first, - const char **submodule_gitfile) +static const char *submodule_gitfile_path(const char *src, int first) { struct strbuf submodule_dotgit = STRBUF_INIT; + const char *path; + if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode)) die(_("Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok(the_repository->index)) die(_("Please stage your changes to .gitmodules or stash them to proceed")); + strbuf_addf(&submodule_dotgit, "%s/.git", src); - *submodule_gitfile = read_gitfile(submodule_dotgit.buf); - if (*submodule_gitfile) - *submodule_gitfile = xstrdup(*submodule_gitfile); - else - *submodule_gitfile = SUBMODULE_WITH_GITDIR; + + path = read_gitfile(submodule_dotgit.buf); strbuf_release(&submodule_dotgit); + if (path) + return path; + return SUBMODULE_WITH_GITDIR; } static int index_range_of_same_dir(const char *src, int length, int *first_p, int *last_p) { - const char *src_w_slash = add_slash(src); + char *src_w_slash = add_slash(src); int first, last, len_w_slash = length + 1; first = index_name_pos(the_repository->index, src_w_slash, len_w_slash); @@ -124,8 +117,8 @@ static int index_range_of_same_dir(const char *src, int length, if (strncmp(path, src_w_slash, len_w_slash)) break; } - if (src_w_slash != src) - free((char *)src_w_slash); + + free(src_w_slash); *first_p = first; *last_p = last; return last - first; @@ -141,7 +134,7 @@ static int index_range_of_same_dir(const char *src, int length, static int empty_dir_has_sparse_contents(const char *name) { int ret = 0; - const char *with_slash = add_slash(name); + char *with_slash = add_slash(name); int length = strlen(with_slash); int pos = index_name_pos(the_repository->index, with_slash, length); @@ -159,11 +152,32 @@ static int empty_dir_has_sparse_contents(const char *name) } free_return: - if (with_slash != name) - free((char *)with_slash); + free(with_slash); return ret; } +static void remove_empty_src_dirs(const char **src_dir, size_t src_dir_nr) +{ + size_t i; + struct strbuf a_src_dir = STRBUF_INIT; + + 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); +} + int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; @@ -177,18 +191,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")), OPT_END(), }; - const char **source, **destination, **dest_path, **submodule_gitfile; - 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; + struct strvec sources = STRVEC_INIT; + struct strvec dest_paths = STRVEC_INIT; + struct strvec destinations = STRVEC_INIT; + struct strvec submodule_gitfiles_to_free = STRVEC_INIT; + const char **submodule_gitfiles; + char *dst_w_slash = NULL; + struct strvec src_dir = STRVEC_INIT; enum update_mode *modes, dst_mode = 0; struct stat st, dest_st; - struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + struct string_list src_for_dst = STRING_LIST_INIT_DUP; 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; + struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP; + struct string_list dirty_paths = STRING_LIST_INIT_DUP; + int ret; git_config(git_default_config, NULL); @@ -201,7 +218,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); - source = internal_prefix_pathspec(prefix, argv, argc, 0); + internal_prefix_pathspec(&sources, prefix, argv, argc, 0); CALLOC_ARRAY(modes, argc); /* @@ -212,45 +229,39 @@ int cmd_mv(int argc, const char **argv, const char *prefix) flags = KEEP_TRAILING_SLASH; 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 *)); + internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags); + dst_w_slash = add_slash(dest_paths.v[0]); + submodule_gitfiles = xcalloc(argc, sizeof(char *)); - if (dest_path[0][0] == '\0') + if (dest_paths.v[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); - else if (!lstat(dest_path[0], &st) && - S_ISDIR(st.st_mode)) { - destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); + internal_prefix_pathspec(&destinations, dest_paths.v[0], argv, argc, DUP_BASENAME); + else if (!lstat(dest_paths.v[0], &st) && S_ISDIR(st.st_mode)) { + internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME); + } else if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) && + empty_dir_has_sparse_contents(dst_w_slash)) { + internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME); + dst_mode = SKIP_WORKTREE_DIR; + } else if (argc != 1) { + die(_("destination '%s' is not a directory"), dest_paths.v[0]); } else { - if (!path_in_sparse_checkout(dst_w_slash, the_repository->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]); - } 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_repository->index)) - dst_mode = SPARSE; - } - } - if (dst_w_slash != dest_path[0]) { - free((char *)dst_w_slash); - dst_w_slash = NULL; + strvec_pushv(&destinations, dest_paths.v); + + /* + * <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(destinations.v[0], the_repository->index)) + dst_mode = SPARSE; } /* Checking */ for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; + const char *src = sources.v[i], *dst = destinations.v[i]; int length; const char *bad = NULL; int skip_sparse = 0; @@ -265,12 +276,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix) pos = index_name_pos(the_repository->index, src, length); if (pos < 0) { - const char *src_w_slash = add_slash(src); + char *src_w_slash = add_slash(src); if (!path_in_sparse_checkout(src_w_slash, the_repository->index) && empty_dir_has_sparse_contents(src)) { + free(src_w_slash); modes[i] |= SKIP_WORKTREE_DIR; goto dir_check; } + free(src_w_slash); /* only error if existence is expected. */ if (!(modes[i] & SPARSE)) bad = _("bad source"); @@ -310,12 +323,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix) dir_check: if (S_ISDIR(st.st_mode)) { - int j, dst_len, n; + char *dst_with_slash; + size_t dst_with_slash_len; + int j, n; int first = index_name_pos(the_repository->index, src, length), last; if (first >= 0) { - prepare_move_submodule(src, first, - submodule_gitfile + i); + const char *path = submodule_gitfile_path(src, first); + if (path != SUBMODULE_WITH_GITDIR) + path = strvec_push(&submodule_gitfiles_to_free, path); + submodule_gitfiles[i] = path; goto act_on_entry; } else if (index_range_of_same_dir(src, length, &first, &last) < 1) { @@ -326,28 +343,31 @@ 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; + strvec_push(&src_dir, src); n = argc + last - first; - REALLOC_ARRAY(source, n); - REALLOC_ARRAY(destination, n); REALLOC_ARRAY(modes, n); - REALLOC_ARRAY(submodule_gitfile, n); + REALLOC_ARRAY(submodule_gitfiles, n); - dst = add_slash(dst); - dst_len = strlen(dst); + dst_with_slash = add_slash(dst); + dst_with_slash_len = strlen(dst_with_slash); for (j = 0; j < last - first; j++) { const struct cache_entry *ce = the_repository->index->cache[first + j]; const char *path = ce->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, path + length + 1); + char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1); + + strvec_push(&sources, path); + strvec_push(&destinations, prefixed_path); + memset(modes + argc + j, 0, sizeof(enum update_mode)); modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX; - submodule_gitfile[argc + j] = NULL; + submodule_gitfiles[argc + j] = NULL; + + free(prefixed_path); } + + free(dst_with_slash); argc += last - first; goto act_on_entry; } @@ -428,23 +448,25 @@ act_on_entry: remove_entry: if (--argc > 0) { int n = argc - i; - MOVE_ARRAY(source + i, source + i + 1, n); - MOVE_ARRAY(destination + i, destination + i + 1, n); + strvec_remove(&sources, i); + strvec_remove(&destinations, i); MOVE_ARRAY(modes + i, modes + i + 1, n); - MOVE_ARRAY(submodule_gitfile + i, - submodule_gitfile + i + 1, n); + MOVE_ARRAY(submodule_gitfiles + i, + submodule_gitfiles + i + 1, n); i--; } } if (only_match_skip_worktree.nr) { advise_on_updating_sparse_paths(&only_match_skip_worktree); - if (!ignore_errors) - return 1; + if (!ignore_errors) { + ret = 1; + goto out; + } } for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; + const char *src = sources.v[i], *dst = destinations.v[i]; enum update_mode mode = modes[i]; int pos; int sparse_and_dirty = 0; @@ -464,12 +486,12 @@ remove_entry: continue; die_errno(_("renaming '%s' failed"), src); } - if (submodule_gitfile[i]) { + if (submodule_gitfiles[i]) { if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; - if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) + if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, - submodule_gitfile[i], + submodule_gitfiles[i], 1); } @@ -535,25 +557,7 @@ remove_entry: } } - /* - * 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); + remove_empty_src_dirs(src_dir.v, src_dir.nr); if (dirty_paths.nr) advise_on_moving_dirty_path(&dirty_paths); @@ -565,11 +569,19 @@ remove_entry: COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); + ret = 0; + +out: + strvec_clear(&src_dir); + free(dst_w_slash); string_list_clear(&src_for_dst, 0); string_list_clear(&dirty_paths, 0); - UNLEAK(source); - UNLEAK(dest_path); - free(submodule_gitfile); + string_list_clear(&only_match_skip_worktree, 0); + strvec_clear(&sources); + strvec_clear(&dest_paths); + strvec_clear(&destinations); + strvec_clear(&submodule_gitfiles_to_free); + free(submodule_gitfiles); free(modes); - return 0; + return ret; } diff --git a/builtin/notes.c b/builtin/notes.c index 7f80b3449b..d9c356e354 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -828,7 +828,7 @@ static int merge_commit(struct notes_merge_options *o) if (partial->parents) oidcpy(&parent_oid, &partial->parents->item->object.oid); else - oidclr(&parent_oid); + oidclr(&parent_oid, the_repository->hash_algo); CALLOC_ARRAY(t, 1); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 62ddf41f84..f395488971 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1315,6 +1315,7 @@ static void write_pack_file(void) if (!pack_to_stdout) { struct stat st; struct strbuf tmpname = STRBUF_INIT; + struct bitmap_writer bitmap_writer; char *idx_tmp_name = NULL; /* @@ -1340,8 +1341,10 @@ static void write_pack_file(void) hash_to_hex(hash)); if (write_bitmap_index) { - bitmap_writer_set_checksum(hash); - bitmap_writer_build_type_index( + bitmap_writer_init(&bitmap_writer, + the_repository); + bitmap_writer_set_checksum(&bitmap_writer, hash); + bitmap_writer_build_type_index(&bitmap_writer, &to_pack, written_list, nr_written); } @@ -1359,12 +1362,17 @@ static void write_pack_file(void) strbuf_addstr(&tmpname, "bitmap"); stop_progress(&progress_state); - bitmap_writer_show_progress(progress); - bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1); - if (bitmap_writer_build(&to_pack) < 0) + bitmap_writer_show_progress(&bitmap_writer, + progress); + bitmap_writer_select_commits(&bitmap_writer, + indexed_commits, + indexed_commits_nr); + if (bitmap_writer_build(&bitmap_writer, &to_pack) < 0) die(_("failed to write bitmap index")); - bitmap_writer_finish(written_list, nr_written, + bitmap_writer_finish(&bitmap_writer, + written_list, nr_written, tmpname.buf, write_bitmap_options); + bitmap_writer_free(&bitmap_writer); write_bitmap_index = 0; strbuf_setlen(&tmpname, tmpname_len); } @@ -2071,7 +2079,8 @@ static void check_object(struct object_entry *entry, uint32_t object_index) oidread(&base_ref, use_pack(p, &w_curs, entry->in_pack_offset + used, - NULL)); + NULL), + the_repository->hash_algo); have_base = 1; } entry->in_pack_header_size = used + the_hash_algo->rawsz; diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 4c735ba069..dd9bf35f5b 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -100,7 +100,7 @@ static inline struct llist_item *llist_insert(struct llist *list, const unsigned char *oid) { struct llist_item *new_item = llist_item_get(); - oidread(&new_item->oid, oid); + oidread(&new_item->oid, oid, the_repository->hash_algo); new_item->next = NULL; if (after) { @@ -155,7 +155,7 @@ redo_from_start: l = (hint == NULL) ? list->front : hint; prev = NULL; while (l) { - const int cmp = hashcmp(l->oid.hash, oid); + const int cmp = hashcmp(l->oid.hash, oid, the_repository->hash_algo); if (cmp > 0) /* not in list, since sorted */ return prev; if (!cmp) { /* found */ @@ -258,7 +258,8 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) while (p1_off < p1->pack->num_objects * p1_step && p2_off < p2->pack->num_objects * p2_step) { - const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); + const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off, + the_repository->hash_algo); /* cmp ~ p1 - p2 */ if (cmp == 0) { p1_hint = llist_sorted_remove(p1->unique_objects, @@ -296,7 +297,8 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) while (p1_off < p1->num_objects * p1_step && p2_off < p2->num_objects * p2_step) { - int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); + int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off, + the_repository->hash_algo); /* cmp ~ p1 - p2 */ if (cmp == 0) { ret++; diff --git a/builtin/patch-id.c b/builtin/patch-id.c index 3894d2b970..d790ae6354 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -5,6 +5,7 @@ #include "hash.h" #include "hex.h" #include "parse-options.h" +#include "setup.h" static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) { @@ -69,7 +70,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, git_hash_ctx ctx; the_hash_algo->init_fn(&ctx); - oidclr(result); + oidclr(result, the_repository->hash_algo); while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { char *line = line_buf->buf; @@ -165,7 +166,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, } if (!found_next) - oidclr(next_oid); + oidclr(next_oid, the_repository->hash_algo); flush_one_hunk(result, &ctx); @@ -178,7 +179,7 @@ static void generate_id_list(int stable, int verbatim) int patchlen; struct strbuf line_buf = STRBUF_INIT; - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); while (!feof(stdin)) { patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim); flush_current_id(patchlen, &oid, &result); @@ -237,6 +238,18 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_patch_id_options, patch_id_usage, 0); + /* + * We rely on `the_hash_algo` to compute patch IDs. This is dubious as + * it means that the hash algorithm now depends on the object hash of + * the repository, even though git-patch-id(1) clearly defines that + * patch IDs always use SHA1. + * + * NEEDSWORK: This hack should be removed in favor of converting + * the code that computes patch IDs to always use SHA1. + */ + if (!the_hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + generate_id_list(opts ? opts > 1 : config.stable, opts ? opts == 3 : config.verbatim); return 0; diff --git a/builtin/pull.c b/builtin/pull.c index d622202bce..4c54d8196f 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -71,48 +71,48 @@ static const char * const pull_usage[] = { /* Shared options */ static int opt_verbosity; -static char *opt_progress; +static const char *opt_progress; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; /* Options passed to git-merge or git-rebase */ static enum rebase_type opt_rebase = -1; -static char *opt_diffstat; -static char *opt_log; -static char *opt_signoff; -static char *opt_squash; -static char *opt_commit; -static char *opt_edit; -static char *cleanup_arg; -static char *opt_ff; -static char *opt_verify_signatures; -static char *opt_verify; +static const char *opt_diffstat; +static const char *opt_log; +static const char *opt_signoff; +static const char *opt_squash; +static const char *opt_commit; +static const char *opt_edit; +static const char *cleanup_arg; +static const char *opt_ff; +static const char *opt_verify_signatures; +static const char *opt_verify; static int opt_autostash = -1; static int config_autostash; static int check_trust_level = 1; static struct strvec opt_strategies = STRVEC_INIT; static struct strvec opt_strategy_opts = STRVEC_INIT; -static char *opt_gpg_sign; +static const char *opt_gpg_sign; static int opt_allow_unrelated_histories; /* Options passed to git-fetch */ -static char *opt_all; -static char *opt_append; -static char *opt_upload_pack; +static const char *opt_all; +static const char *opt_append; +static const char *opt_upload_pack; static int opt_force; -static char *opt_tags; -static char *opt_prune; -static char *max_children; +static const char *opt_tags; +static const char *opt_prune; +static const char *max_children; static int opt_dry_run; -static char *opt_keep; -static char *opt_depth; -static char *opt_unshallow; -static char *opt_update_shallow; -static char *opt_refmap; -static char *opt_ipv4; -static char *opt_ipv6; +static const char *opt_keep; +static const char *opt_depth; +static const char *opt_unshallow; +static const char *opt_update_shallow; +static const char *opt_refmap; +static const char *opt_ipv4; +static const char *opt_ipv6; static int opt_show_forced_updates = -1; -static char *set_upstream; +static const char *set_upstream; static struct strvec opt_fetch = STRVEC_INIT; static struct option pull_options[] = { @@ -1038,7 +1038,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) die_conclude_merge(); if (repo_get_oid(the_repository, "HEAD", &orig_head)) - oidclr(&orig_head); + oidclr(&orig_head, the_repository->hash_algo); if (opt_rebase) { if (opt_autostash == -1) @@ -1053,7 +1053,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) _("Please commit or stash them."), 1, 0); if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs)) - oidclr(&rebase_fork_point); + oidclr(&rebase_fork_point, the_repository->hash_algo); } if (run_fetch(repo, refspecs)) @@ -1063,7 +1063,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) return 0; if (repo_get_oid(the_repository, "HEAD", &curr_head)) - oidclr(&curr_head); + oidclr(&curr_head, the_repository->hash_algo); if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) && !oideq(&orig_head, &curr_head)) { diff --git a/builtin/push.c b/builtin/push.c index 2fbb31c3ad..7a67398124 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -96,9 +96,8 @@ static void refspec_append_mapped(struct refspec *refspec, const char *ref, refspec_append(refspec, ref); } -static void set_refspecs(const char **refs, int nr, const char *repo) +static void set_refspecs(const char **refs, int nr, struct remote *remote) { - struct remote *remote = NULL; struct ref *local_refs = NULL; int i; @@ -124,33 +123,16 @@ static void set_refspecs(const char **refs, int nr, const char *repo) local_refs = get_local_heads(); /* Does "ref" uniquely name our ref? */ - if (count_refspec_match(ref, local_refs, &matched) != 1) { + if (count_refspec_match(ref, local_refs, &matched) != 1) refspec_append(&rs, ref); - } else { - /* lazily grab remote */ - if (!remote) - remote = remote_get(repo); - if (!remote) - BUG("must get a remote for repo '%s'", repo); - + else refspec_append_mapped(&rs, ref, remote, matched); - } } else refspec_append(&rs, ref); } free_refs(local_refs); } -static int push_url_of_remote(struct remote *remote, const char ***url_p) -{ - if (remote->pushurl_nr) { - *url_p = remote->pushurl; - return remote->pushurl_nr; - } - *url_p = remote->url; - return remote->url_nr; -} - static NORETURN void die_push_simple(struct branch *branch, struct remote *remote) { @@ -434,8 +416,7 @@ static int do_push(int flags, struct remote *remote) { int i, errs; - const char **url; - int url_nr; + struct strvec *url; struct refspec *push_refspec = &rs; if (push_options->nr) @@ -448,19 +429,10 @@ static int do_push(int flags, setup_default_push_refspecs(&flags, remote); } errs = 0; - url_nr = push_url_of_remote(remote, &url); - if (url_nr) { - for (i = 0; i < url_nr; i++) { - struct transport *transport = - transport_get(remote, url[i]); - if (flags & TRANSPORT_PUSH_OPTIONS) - transport->push_options = push_options; - if (push_with_options(transport, push_refspec, flags)) - errs++; - } - } else { + url = push_url_of_remote(remote); + for (i = 0; i < url->nr; i++) { struct transport *transport = - transport_get(remote, NULL); + transport_get(remote, url->v[i]); if (flags & TRANSPORT_PUSH_OPTIONS) transport->push_options = push_options; if (push_with_options(transport, push_refspec, flags)) @@ -650,10 +622,8 @@ int cmd_push(int argc, const char **argv, const char *prefix) if (tags) refspec_append(&rs, "refs/tags/*"); - if (argc > 0) { + if (argc > 0) repo = argv[0]; - set_refspecs(argv + 1, argc - 1, repo); - } remote = pushremote_get(repo); if (!remote) { @@ -669,6 +639,9 @@ int cmd_push(int argc, const char **argv, const char *prefix) " git push <name>\n")); } + if (argc > 0) + set_refspecs(argv + 1, argc - 1, remote); + if (remote->mirror) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); diff --git a/builtin/rebase.c b/builtin/rebase.c index 0466d9414a..e3a8e74cfc 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -83,7 +83,7 @@ static const char *action_names[] = { struct rebase_options { enum rebase_type type; enum empty_type empty; - const char *default_backend; + char *default_backend; const char *state_dir; struct commit *upstream; const char *upstream_name; @@ -135,7 +135,7 @@ struct rebase_options { .type = REBASE_UNSPECIFIED, \ .empty = EMPTY_UNSPECIFIED, \ .keep_empty = 1, \ - .default_backend = "merge", \ + .default_backend = xstrdup("merge"), \ .flags = REBASE_NO_QUIET, \ .git_am_opts = STRVEC_INIT, \ .exec = STRING_LIST_INIT_NODUP, \ @@ -151,6 +151,19 @@ struct rebase_options { .strategy_opts = STRING_LIST_INIT_NODUP,\ } +static void rebase_options_release(struct rebase_options *opts) +{ + free(opts->default_backend); + free(opts->reflog_action); + free(opts->head_name); + strvec_clear(&opts->git_am_opts); + free(opts->gpg_sign_opt); + string_list_clear(&opts->exec, 0); + free(opts->strategy); + string_list_clear(&opts->strategy_opts, 0); + strbuf_release(&opts->git_format_patch_opt); +} + static struct replay_opts get_replay_opts(const struct rebase_options *opts) { struct replay_opts replay = REPLAY_OPTS_INIT; @@ -193,7 +206,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) return replay; } -static int edit_todo_file(unsigned flags) +static int edit_todo_file(unsigned flags, struct replay_opts *opts) { const char *todo_file = rebase_path_todo(); struct todo_list todo_list = TODO_LIST_INIT, @@ -204,7 +217,8 @@ static int edit_todo_file(unsigned flags) return error_errno(_("could not read '%s'."), todo_file); strbuf_stripspace(&todo_list.buf, comment_line_str); - res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); + res = edit_todo_list(the_repository, opts, &todo_list, &new_todo, + NULL, NULL, flags); if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) res = error_errno(_("could not write '%s'"), todo_file); @@ -295,8 +309,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) error(_("could not generate todo list")); else { discard_index(the_repository->index); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) + if (todo_list_parse_insn_buffer(the_repository, &replay, + todo_list.buf.buf, &todo_list)) BUG("unusable todo list"); ret = complete_action(the_repository, &replay, flags, @@ -351,9 +365,13 @@ static int run_sequencer_rebase(struct rebase_options *opts) replay_opts_release(&replay_opts); break; } - case ACTION_EDIT_TODO: - ret = edit_todo_file(flags); + case ACTION_EDIT_TODO: { + struct replay_opts replay_opts = get_replay_opts(opts); + + ret = edit_todo_file(flags, &replay_opts); + replay_opts_release(&replay_opts); break; + } case ACTION_SHOW_CURRENT_PATCH: { struct child_process cmd = CHILD_PROCESS_INIT; @@ -796,6 +814,7 @@ static int rebase_config(const char *var, const char *value, } if (!strcmp(var, "rebase.backend")) { + FREE_AND_NULL(opts->default_backend); return git_config_string(&opts->default_backend, var, value); } @@ -1047,6 +1066,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = REBASE_OPTIONS_INIT; const char *branch_name; + const char *strategy_opt = NULL; int ret, flags, total_argc, in_progress = 0; int keep_base = 0; int ok_to_skip_pre_rebase = 0; @@ -1161,7 +1181,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, parse_opt_rebase_merges), OPT_BOOL(0, "fork-point", &options.fork_point, N_("use 'merge-base --fork-point' to refine upstream")), - OPT_STRING('s', "strategy", &options.strategy, + OPT_STRING('s', "strategy", &strategy_opt, N_("strategy"), N_("use the given merge strategy")), OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts, N_("option"), @@ -1470,13 +1490,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } } - if (options.strategy_opts.nr && !options.strategy) - options.strategy = "ort"; - - if (options.strategy) { - options.strategy = xstrdup(options.strategy); + if (strategy_opt) + options.strategy = xstrdup(strategy_opt); + else if (options.strategy_opts.nr && !options.strategy) + options.strategy = xstrdup("ort"); + if (options.strategy) imply_merge(&options, "--strategy"); - } if (options.root && !options.onto_name) imply_merge(&options, "--root without --onto"); @@ -1833,14 +1852,7 @@ run_rebase: cleanup: strbuf_release(&buf); strbuf_release(&revisions); - free(options.reflog_action); - free(options.head_name); - strvec_clear(&options.git_am_opts); - free(options.gpg_sign_opt); - string_list_clear(&options.exec, 0); - free(options.strategy); - string_list_clear(&options.strategy_opts, 0); - strbuf_release(&options.git_format_patch_opt); + rebase_options_release(&options); free(squash_onto_name); free(keep_base_onto_name); return !!ret; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index be8969a84a..339524ae2a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -88,7 +88,7 @@ static struct strbuf push_cert = STRBUF_INIT; static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; -static const char *cert_nonce_seed; +static char *cert_nonce_seed; static struct strvec hidden_refs = STRVEC_INIT; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; @@ -168,13 +168,13 @@ static int receive_pack_config(const char *var, const char *value, } if (strcmp(var, "receive.fsck.skiplist") == 0) { - const char *path; + char *path; if (git_config_pathname(&path, var, value)) return 1; strbuf_addf(&fsck_msg_types, "%cskiplist=%s", fsck_msg_types.len ? ',' : '=', path); - free((char *)path); + free(path); return 0; } @@ -741,7 +741,7 @@ static void prepare_push_cert_sha1(struct child_process *proc) already_done = 1; if (write_object_file(push_cert.buf, push_cert.len, OBJ_BLOB, &push_cert_oid)) - oidclr(&push_cert_oid); + oidclr(&push_cert_oid, the_repository->hash_algo); memset(&sigcheck, '\0', sizeof(sigcheck)); @@ -1249,7 +1249,7 @@ cleanup: return code; } -static char *refuse_unconfigured_deny_msg = +static const char *refuse_unconfigured_deny_msg = N_("By default, updating the current branch in a non-bare repository\n" "is denied, because it will make the index and work tree inconsistent\n" "with what you pushed, and will require 'git reset --hard' to match\n" @@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void) rp_error("%s", _(refuse_unconfigured_deny_msg)); } -static char *refuse_unconfigured_deny_delete_current_msg = +static const char *refuse_unconfigured_deny_delete_current_msg = N_("By default, deleting the current branch is denied, because the next\n" "'git clone' won't result in any file checked out, causing confusion.\n" "\n" @@ -1371,7 +1371,7 @@ static const char *push_to_deploy(unsigned char *sha1, strvec_pushl(&child.args, "diff-index", "--quiet", "--cached", "--ignore-submodules", /* diff-index with either HEAD or an empty tree */ - head_has_history() ? "HEAD" : empty_tree_oid_hex(), + head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo), "--", NULL); strvec_pushv(&child.env, env->v); child.no_stdin = 1; @@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (ref_transaction_delete(transaction, namespaced_name, old_oid, - 0, "push", &err)) { + NULL, 0, + "push", &err)) { rp_error("%s", err.buf); ret = "failed to delete"; } else { diff --git a/builtin/remote.c b/builtin/remote.c index 447ef1d3c9..08292498bd 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat { struct ref *ref, *matches; struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; - struct refspec_item refspec; + struct refspec_item refspec = { + .force = 0, + .pattern = 1, + .src = (char *) "refs/heads/*", + .dst = (char *) "refs/heads/*", + }; - memset(&refspec, 0, sizeof(refspec)); - refspec.force = 0; - refspec.pattern = 1; - refspec.src = refspec.dst = "refs/heads/*"; get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), fetch_map, 1); @@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat free_refs(fetch_map); free_refs(matches); - return 0; } @@ -619,8 +619,8 @@ static int migrate_file(struct remote *remote) int i; strbuf_addf(&buf, "remote.%s.url", remote->name); - for (i = 0; i < remote->url_nr; i++) - git_config_set_multivar(buf.buf, remote->url[i], "^$", 0); + for (i = 0; i < remote->url.nr; i++) + git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); for (i = 0; i < remote->push.raw_nr; i++) @@ -1002,8 +1002,7 @@ static int get_remote_ref_states(const char *name, struct transport *transport; const struct ref *remote_refs; - transport = transport_get(states->remote, states->remote->url_nr > 0 ? - states->remote->url[0] : NULL); + transport = transport_get(states->remote, states->remote->url.v[0]); remote_refs = transport_get_remote_refs(transport, NULL); states->queried = 1; @@ -1213,15 +1212,15 @@ static int get_one_entry(struct remote *remote, void *priv) { struct string_list *list = priv; struct strbuf remote_info_buf = STRBUF_INIT; - const char **url; - int i, url_nr; + struct strvec *url; + int i; - if (remote->url_nr > 0) { + if (remote->url.nr > 0) { struct strbuf promisor_config = STRBUF_INIT; const char *partial_clone_filter = NULL; strbuf_addf(&promisor_config, "remote.%s.partialclonefilter", remote->name); - strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url[0]); + strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url.v[0]); if (!git_config_get_string_tmp(promisor_config.buf, &partial_clone_filter)) strbuf_addf(&remote_info_buf, " [%s]", partial_clone_filter); @@ -1230,16 +1229,10 @@ static int get_one_entry(struct remote *remote, void *priv) strbuf_detach(&remote_info_buf, NULL); } else string_list_append(list, remote->name)->util = NULL; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } - for (i = 0; i < url_nr; i++) + url = push_url_of_remote(remote); + for (i = 0; i < url->nr; i++) { - strbuf_addf(&remote_info_buf, "%s (push)", url[i]); + strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]); string_list_append(list, remote->name)->util = strbuf_detach(&remote_info_buf, NULL); } @@ -1295,28 +1288,20 @@ static int show(int argc, const char **argv, const char *prefix) for (; argc; argc--, argv++) { int i; - const char **url; - int url_nr; + struct strvec *url; get_remote_ref_states(*argv, &info.states, query_flag); printf_ln(_("* remote %s"), *argv); - printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ? - info.states.remote->url[0] : _("(no URL)")); - if (info.states.remote->pushurl_nr) { - url = info.states.remote->pushurl; - url_nr = info.states.remote->pushurl_nr; - } else { - url = info.states.remote->url; - url_nr = info.states.remote->url_nr; - } - for (i = 0; i < url_nr; i++) + printf_ln(_(" Fetch URL: %s"), info.states.remote->url.v[0]); + url = push_url_of_remote(info.states.remote); + for (i = 0; i < url->nr; i++) /* * TRANSLATORS: the colon ':' should align * with the one in " Fetch URL: %s" * translation. */ - printf_ln(_(" Push URL: %s"), url[i]); + printf_ln(_(" Push URL: %s"), url->v[i]); if (!i) printf_ln(_(" Push URL: %s"), _("(no URL)")); if (no_query) @@ -1453,10 +1438,7 @@ static int prune_remote(const char *remote, int dry_run) } printf_ln(_("Pruning %s"), remote); - printf_ln(_("URL: %s"), - states.remote->url_nr - ? states.remote->url[0] - : _("(no URL)")); + printf_ln(_("URL: %s"), states.remote->url.v[0]); for_each_string_list_item(item, &states.stale) string_list_append(&refs_to_prune, item->util); @@ -1622,8 +1604,7 @@ static int get_url(int argc, const char **argv, const char *prefix) int i, push_mode = 0, all_mode = 0; const char *remotename = NULL; struct remote *remote; - const char **url; - int url_nr; + struct strvec *url; struct option options[] = { OPT_BOOL('\0', "push", &push_mode, N_("query push URLs rather than fetch URLs")), @@ -1645,27 +1626,13 @@ static int get_url(int argc, const char **argv, const char *prefix) exit(2); } - url_nr = 0; - if (push_mode) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } - /* else fetch mode */ - - /* Use the fetch URL when no push URLs were found or requested. */ - if (!url_nr) { - url = remote->url; - url_nr = remote->url_nr; - } - - if (!url_nr) - die(_("no URLs configured for remote '%s'"), remotename); + url = push_mode ? push_url_of_remote(remote) : &remote->url; if (all_mode) { - for (i = 0; i < url_nr; i++) - printf_ln("%s", url[i]); + for (i = 0; i < url->nr; i++) + printf_ln("%s", url->v[i]); } else { - printf_ln("%s", *url); + printf_ln("%s", url->v[0]); } return 0; @@ -1680,8 +1647,7 @@ static int set_url(int argc, const char **argv, const char *prefix) const char *oldurl = NULL; struct remote *remote; regex_t old_regex; - const char **urlset; - int urlset_nr; + struct strvec *urlset; struct strbuf name_buf = STRBUF_INIT; struct option options[] = { OPT_BOOL('\0', "push", &push_mode, @@ -1718,12 +1684,10 @@ static int set_url(int argc, const char **argv, const char *prefix) if (push_mode) { strbuf_addf(&name_buf, "remote.%s.pushurl", remotename); - urlset = remote->pushurl; - urlset_nr = remote->pushurl_nr; + urlset = &remote->pushurl; } else { strbuf_addf(&name_buf, "remote.%s.url", remotename); - urlset = remote->url; - urlset_nr = remote->url_nr; + urlset = &remote->url; } /* Special cases that add new entry. */ @@ -1740,8 +1704,8 @@ static int set_url(int argc, const char **argv, const char *prefix) if (regcomp(&old_regex, oldurl, REG_EXTENDED)) die(_("Invalid old URL pattern: %s"), oldurl); - for (i = 0; i < urlset_nr; i++) - if (!regexec(&old_regex, urlset[i], 0, NULL, 0)) + for (i = 0; i < urlset->nr; i++) + if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0)) matches++; else negative_matches++; diff --git a/builtin/repack.c b/builtin/repack.c index 58ad82dd97..f0317fa94a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -48,10 +48,10 @@ static const char incremental_bitmap_conflict_error[] = N_( ); struct pack_objects_args { - const char *window; - const char *window_memory; - const char *depth; - const char *threads; + char *window; + char *window_memory; + char *depth; + char *threads; unsigned long max_pack_size; int no_reuse_delta; int no_reuse_object; diff --git a/builtin/replace.c b/builtin/replace.c index ce9f6974d2..1ef833c07f 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -167,7 +167,7 @@ static int check_ref_valid(struct object_id *object, return error(_("'%s' is not a valid ref name"), ref->buf); if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev)) - oidclr(prev); + oidclr(prev, the_repository->hash_algo); else if (!force) return error(_("replace ref '%s' already exists"), ref->buf); return 0; diff --git a/builtin/replay.c b/builtin/replay.c index 6bf0691f15..0448326636 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -52,11 +52,11 @@ static struct commit *create_commit(struct tree *tree, struct commit *parent) { struct object_id ret; - struct object *obj; + struct object *obj = NULL; struct commit_list *parents = NULL; char *author; char *sign_commit = NULL; /* FIXME: cli users might want to sign again */ - struct commit_extra_header *extra; + struct commit_extra_header *extra = NULL; struct strbuf msg = STRBUF_INIT; const char *out_enc = get_commit_output_encoding(); const char *message = repo_logmsg_reencode(the_repository, based_on, @@ -73,12 +73,16 @@ static struct commit *create_commit(struct tree *tree, if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, &ret, author, NULL, sign_commit, extra)) { error(_("failed to write commit object")); - return NULL; + goto out; } - free(author); - strbuf_release(&msg); obj = parse_object(the_repository, &ret); + +out: + free_commit_extra_headers(extra); + free_commit_list(parents); + strbuf_release(&msg); + free(author); return (struct commit *)obj; } diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 77803727e0..97d077a994 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -508,6 +508,8 @@ static int try_bitmap_disk_usage(struct rev_info *revs, size_from_bitmap = get_disk_usage_from_bitmap(bitmap_git, revs); print_disk_usage(size_from_bitmap); + + free_bitmap_index(bitmap_git); return 0; } diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 2db047fff4..2e64f5bda7 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -423,12 +423,12 @@ static char *findspace(const char *s) static int cmd_parseopt(int argc, const char **argv, const char *prefix) { - static int keep_dashdash = 0, stop_at_non_option = 0; - static char const * const parseopt_usage[] = { + int keep_dashdash = 0, stop_at_non_option = 0; + char const * const parseopt_usage[] = { N_("git rev-parse --parseopt [<options>] -- [<args>...]"), NULL }; - static struct option parseopt_opts[] = { + struct option parseopt_opts[] = { OPT_BOOL(0, "keep-dashdash", &keep_dashdash, N_("keep the `--` passed as an arg")), OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option, @@ -438,12 +438,11 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) N_("output in stuck long form")), OPT_END(), }; - static const char * const flag_chars = "*=?!"; - struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT; - const char **usage = NULL; + struct strvec longnames = STRVEC_INIT; + struct strvec usage = STRVEC_INIT; struct option *opts = NULL; - int onb = 0, osz = 0, unb = 0, usz = 0; + size_t opts_nr = 0, opts_alloc = 0; strbuf_addstr(&parsed, "set --"); argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage, @@ -453,16 +452,16 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) /* get the usage up to the first line with a -- on it */ for (;;) { + strbuf_reset(&sb); if (strbuf_getline(&sb, stdin) == EOF) die(_("premature end of input")); - ALLOC_GROW(usage, unb + 1, usz); if (!strcmp("--", sb.buf)) { - if (unb < 1) + if (!usage.nr) die(_("no usage string given before the `--' separator")); - usage[unb] = NULL; break; } - usage[unb++] = strbuf_detach(&sb, NULL); + + strvec_push(&usage, sb.buf); } /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */ @@ -474,10 +473,10 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) if (!sb.len) continue; - ALLOC_GROW(opts, onb + 1, osz); - memset(opts + onb, 0, sizeof(opts[onb])); + ALLOC_GROW(opts, opts_nr + 1, opts_alloc); + memset(opts + opts_nr, 0, sizeof(*opts)); - o = &opts[onb++]; + o = &opts[opts_nr++]; help = findspace(sb.buf); if (!help || sb.buf == help) { o->type = OPTION_GROUP; @@ -494,20 +493,22 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) o->callback = &parseopt_dump; /* name(s) */ - s = strpbrk(sb.buf, flag_chars); + s = strpbrk(sb.buf, "*=?!"); if (!s) s = help; if (s == sb.buf) die(_("missing opt-spec before option flags")); - if (s - sb.buf == 1) /* short option only */ + if (s - sb.buf == 1) { /* short option only */ o->short_name = *sb.buf; - else if (sb.buf[1] != ',') /* long option only */ - o->long_name = xmemdupz(sb.buf, s - sb.buf); - else { + } else if (sb.buf[1] != ',') { /* long option only */ + o->long_name = strvec_pushf(&longnames, "%.*s", + (int)(s - sb.buf), sb.buf); + } else { o->short_name = *sb.buf; - o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2); + o->long_name = strvec_pushf(&longnames, "%.*s", + (int)(s - sb.buf - 2), sb.buf + 2); } /* flags */ @@ -537,9 +538,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) strbuf_release(&sb); /* put an OPT_END() */ - ALLOC_GROW(opts, onb + 1, osz); - memset(opts + onb, 0, sizeof(opts[onb])); - argc = parse_options(argc, argv, prefix, opts, usage, + ALLOC_GROW(opts, opts_nr + 1, opts_alloc); + memset(opts + opts_nr, 0, sizeof(*opts)); + argc = parse_options(argc, argv, prefix, opts, usage.v, (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) | (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) | PARSE_OPT_SHELL_EVAL); @@ -547,7 +548,13 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) strbuf_addstr(&parsed, " --"); sq_quote_argv(&parsed, argv); puts(parsed.buf); + strbuf_release(&parsed); + strbuf_release(&sb); + strvec_clear(&longnames); + strvec_clear(&usage); + free((char *) opts->help); + free(opts); return 0; } @@ -691,7 +698,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) const char *name = NULL; struct object_context unused; struct strbuf buf = STRBUF_INIT; - const int hexsz = the_hash_algo->hexsz; int seen_end_of_options = 0; enum format_type format = FORMAT_DEFAULT; @@ -867,8 +873,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) abbrev = strtoul(arg, NULL, 10); if (abbrev < MINIMUM_ABBREV) abbrev = MINIMUM_ABBREV; - else if (hexsz <= abbrev) - abbrev = hexsz; + else if ((int)the_hash_algo->hexsz <= abbrev) + abbrev = the_hash_algo->hexsz; continue; } if (!strcmp(arg, "--sq")) { @@ -1122,6 +1128,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!get_oid_with_context(the_repository, name, flags, &oid, &unused)) { + object_context_release(&unused); if (output_algo) repo_oid_to_algop(the_repository, &oid, output_algo, &oid); @@ -1131,6 +1138,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) show_rev(type, &oid, name); continue; } + object_context_release(&unused); if (verify) die_no_single_rev(quiet); if (has_dashdash) diff --git a/builtin/revert.c b/builtin/revert.c index 53935d2c68..7bf2b4e11d 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, /* Check for incompatible command line arguments */ if (cmd) { - char *this_operation; + const char *this_operation; if (cmd == 'q') this_operation = "--quit"; else if (cmd == 'c') diff --git a/builtin/rm.c b/builtin/rm.c index d195c16e74..0e79cbab62 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -377,7 +377,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!force) { struct object_id oid; if (repo_get_oid(the_repository, "HEAD", &oid)) - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); if (check_local_mod(&oid, index_only)) exit(1); } diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 3df9eaad09..17cae6bbbd 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) /* stable plumbing output; do not modify or localize */ fprintf(stderr, "Everything up-to-date\n"); + free_refs(remote_refs); + free_refs(local_refs); return ret; } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 3c7cd2d6ef..5bde7c68c2 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -435,7 +435,7 @@ parse_done: usage_with_options(shortlog_usage, options); } - if (setup_revisions(argc, argv, &rev, NULL) != 1) { + if (!nongit && setup_revisions(argc, argv, &rev, NULL) != 1) { error(_("unrecognized argument: %s"), argv[1]); usage_with_options(shortlog_usage, options); } @@ -460,11 +460,8 @@ parse_done: else get_from_rev(&rev, &log); - release_revisions(&rev); - shortlog_output(&log); - if (log.file != stdout) - fclose(log.file); + release_revisions(&rev); return 0; } diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 3114bdc391..839a5c29f3 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -11,8 +11,8 @@ static const char * const show_ref_usage[] = { N_("git show-ref [--head] [-d | --dereference]\n" - " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n" - " [--heads] [--] [<pattern>...]"), + " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n" + " [--] [<pattern>...]"), N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n" " [-s | --hash[=<n>]] [--abbrev[=<n>]]\n" " [--] [<ref>...]"), @@ -189,7 +189,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, struct patterns_options { int show_head; - int heads_only; + int branches_only; int tags_only; }; @@ -208,8 +208,8 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts, if (opts->show_head) refs_head_ref(get_main_ref_store(the_repository), show_ref, &show_ref_data); - if (opts->heads_only || opts->tags_only) { - if (opts->heads_only) + if (opts->branches_only || opts->tags_only) { + if (opts->branches_only) refs_for_each_fullref_in(get_main_ref_store(the_repository), "refs/heads/", NULL, show_ref, &show_ref_data); @@ -293,8 +293,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) struct show_one_options show_one_opts = {0}; int verify = 0, exists = 0; const struct option show_ref_options[] = { - OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")), - OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")), + OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with branches)")), + OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with tags)")), + OPT_HIDDEN_BOOL(0, "heads", &patterns_opts.branches_only, + N_("deprecated synonym for --branches")), OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")), OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " "requires exact ref path")), diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 0f52e25249..2604ab04df 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix) printf("\n"); } - return 0; + string_list_clear(&sl, 0); + } else { + write_patterns_to_file(stdout, &pl); } - write_patterns_to_file(stdout, &pl); clear_pattern_list(&pl); return 0; @@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl) struct unpack_trees_options o; struct lock_file lock_file = LOCK_INIT; struct repository *r = the_repository; + struct pattern_list *old_pl; /* If no branch has been checked out, there are no updates to make. */ if (is_index_unborn(r->index)) return UPDATE_SPARSITY_SUCCESS; + old_pl = r->index->sparse_checkout_patterns; r->index->sparse_checkout_patterns = pl; memset(&o, 0, sizeof(o)); @@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl) clean_tracked_sparse_directories(r); - r->index->sparse_checkout_patterns = NULL; + if (r->index->sparse_checkout_patterns != pl) { + clear_pattern_list(r->index->sparse_checkout_patterns); + FREE_AND_NULL(r->index->sparse_checkout_patterns); + } + r->index->sparse_checkout_patterns = old_pl; + return result; } @@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl) fprintf(fp, "%s/\n", pattern); free(pattern); } + + string_list_clear(&sl, 0); } static int write_patterns_and_update(struct pattern_list *pl) @@ -440,7 +450,6 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix) char *sparse_filename; int res; struct object_id oid; - struct strbuf pattern = STRBUF_INIT; static struct option builtin_sparse_checkout_init_options[] = { OPT_BOOL(0, "cone", &init_opts.cone_mode, @@ -471,6 +480,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix) /* If we already have a sparse-checkout file, use it. */ if (res >= 0) { free(sparse_filename); + clear_pattern_list(&pl); return update_working_directory(NULL); } @@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix) return 0; } - strbuf_addstr(&pattern, "/*"); - add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0); - strbuf_addstr(&pattern, "!/*/"); - add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0); + free(sparse_filename); + + add_pattern("/*", empty_base, 0, &pl, 0); + add_pattern("!/*/", empty_base, 0, &pl, 0); pl.use_cone_patterns = init_opts.cone_mode; return write_patterns_and_update(&pl); @@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat char *slash = strrchr(e->pattern, '/'); char *oldpattern = e->pattern; size_t newlen; + struct pattern_entry *dup; if (!slash || slash == e->pattern) break; @@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat e->pattern = xstrndup(oldpattern, newlen); hashmap_entry_init(&e->ent, fspathhash(e->pattern)); - if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL)) + dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL); + if (!dup) { hashmap_add(&pl->parent_hashmap, &e->ent); + } else { + free(e->pattern); + free(e); + e = dup; + } } } @@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl, strbuf_to_cone_pattern(&line, pl); } } + strbuf_release(&line); } else { if (file) { struct strbuf line = STRBUF_INIT; - while (!strbuf_getline(&line, file)) { - size_t len; - char *buf = strbuf_detach(&line, &len); - add_pattern(buf, empty_base, 0, pl, 0); - } + while (!strbuf_getline(&line, file)) + add_pattern(line.buf, empty_base, 0, pl, 0); + + strbuf_release(&line); } else { for (i = 0; i < argc; i++) add_pattern(argv[i], empty_base, 0, pl, 0); @@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv, OPT_END(), }; struct pattern_list pl; - struct strbuf match_all = STRBUF_INIT; /* * We do not exit early if !core_apply_sparse_checkout; due to the @@ -917,8 +933,7 @@ static int sparse_checkout_disable(int argc, const char **argv, pl.use_cone_patterns = 0; core_apply_sparse_checkout = 1; - strbuf_addstr(&match_all, "/*"); - add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0); + add_pattern("/*", empty_base, 0, &pl, 0); prepare_repo_settings(the_repository); the_repository->settings.sparse_index = 0; @@ -1011,6 +1026,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char * ret = check_rules(&pl, check_rules_opts.null_termination); clear_pattern_list(&pl); + free(check_rules_opts.rules_file); return ret; } diff --git a/builtin/stash.c b/builtin/stash.c index 7859bc0866..46b981c4dd 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -975,7 +975,9 @@ static int show_stash(int argc, const char **argv, const char *prefix) log_tree_diff_flush(&rev); ret = diff_result_code(&rev.diffopt); + cleanup: + strvec_clear(&revision_args); strvec_clear(&stash_args); free_stash_info(&info); release_revisions(&rev); @@ -1018,13 +1020,14 @@ static int store_stash(int argc, const char **argv, const char *prefix) int quiet = 0; const char *stash_msg = NULL; struct object_id obj; - struct object_context dummy; + struct object_context dummy = {0}; struct option options[] = { OPT__QUIET(&quiet, N_("be quiet")), OPT_STRING('m', "message", &stash_msg, "message", N_("stash message")), OPT_END() }; + int ret; argc = parse_options(argc, argv, prefix, options, git_stash_store_usage, @@ -1043,10 +1046,15 @@ static int store_stash(int argc, const char **argv, const char *prefix) if (!quiet) fprintf_ln(stderr, _("Cannot update %s with %s"), ref_stash, argv[0]); - return -1; + ret = -1; + goto out; } - return do_store_stash(&obj, stash_msg, quiet); + ret = do_store_stash(&obj, stash_msg, quiet); + +out: + object_context_release(&dummy); + return ret; } static void add_pathspecs(struct strvec *args, @@ -1408,6 +1416,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b goto done; } + free_commit_list(parents); + parents = NULL; + if (include_untracked) { if (save_untracked_files(info, &msg, untracked_files)) { if (!quiet) @@ -1453,11 +1464,6 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b else strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name); - /* - * `parents` will be empty after calling `commit_tree()`, so there is - * no need to call `free_commit_list()` - */ - parents = NULL; if (untracked_commit_option) commit_list_insert(lookup_commit(the_repository, &info->u_commit), @@ -1479,6 +1485,7 @@ done: strbuf_release(&commit_tree_label); strbuf_release(&msg); strbuf_release(&untracked_files); + free_commit_list(parents); return ret; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 897f19868e..f1218a1995 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -256,11 +256,9 @@ static void module_list_active(struct module_list *list) static char *get_up_path(const char *path) { - int i; struct strbuf sb = STRBUF_INIT; - for (i = count_slashes(path); i; i--) - strbuf_addstr(&sb, "../"); + strbuf_addstrings(&sb, "../", count_slashes(path)); /* * Check if 'path' ends with slash or not @@ -378,8 +376,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive", NULL); - strvec_pushl(&cpr.args, "--super-prefix", NULL); - strvec_pushf(&cpr.args, "%s/", displaypath); + strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath); if (info->quiet) strvec_push(&cpr.args, "--quiet"); @@ -704,8 +701,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, strvec_pushl(&cpr.args, "submodule--helper", "status", "--recursive", NULL); - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); + strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath); if (flags & OPT_CACHED) strvec_push(&cpr.args, "--cached"); @@ -1306,9 +1302,7 @@ static void sync_submodule(const char *path, const char *prefix, strvec_pushl(&cpr.args, "submodule--helper", "sync", "--recursive", NULL); - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); - + strvec_pushf(&cpr.args, "--super-prefix=%s/", displaypath); if (flags & OPT_QUIET) strvec_push(&cpr.args, "--quiet"); @@ -2536,10 +2530,9 @@ static void update_data_to_args(const struct update_data *update_data, enum submodule_update_type update_type = update_data->update_default; strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL); - if (update_data->displaypath) { - strvec_push(args, "--super-prefix"); - strvec_pushf(args, "%s/", update_data->displaypath); - } + if (update_data->displaypath) + strvec_pushf(args, "--super-prefix=%s/", + update_data->displaypath); strvec_pushf(args, "--jobs=%d", update_data->max_jobs); if (update_data->quiet) strvec_push(args, "--quiet"); diff --git a/builtin/tag.c b/builtin/tag.c index 6e2c0cf342..a1fb218512 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -650,7 +650,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die(_("'%s' is not a valid tag name."), tag); if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev)) - oidclr(&prev); + oidclr(&prev, the_repository->hash_algo); else if (!force) die(_("tag '%s' already exists"), tag); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index f1c85a00ae..08fa2a7a74 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -439,7 +439,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, struct object_id base_oid; if (type == OBJ_REF_DELTA) { - oidread(&base_oid, fill(the_hash_algo->rawsz)); + oidread(&base_oid, fill(the_hash_algo->rawsz), the_repository->hash_algo); use(the_hash_algo->rawsz); delta_data = get_data(delta_size); if (!delta_data) @@ -451,7 +451,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, return; /* we are done */ else { /* cannot resolve yet --- queue it */ - oidclr(&obj_list[nr].oid); + oidclr(&obj_list[nr].oid, the_repository->hash_algo); add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size); return; } @@ -500,7 +500,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, * The delta base object is itself a delta that * has not been resolved yet. */ - oidclr(&obj_list[nr].oid); + oidclr(&obj_list[nr].oid, the_repository->hash_algo); add_delta_to_list(nr, null_oid(), base_offset, delta_data, delta_size); return; @@ -674,7 +674,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) if (fsck_finish(&fsck_options)) die(_("fsck error in pack objects")); } - if (!hasheq(fill(the_hash_algo->rawsz), oid.hash)) + if (!hasheq(fill(the_hash_algo->rawsz), oid.hash, + the_repository->hash_algo)) die("final sha1 did not match"); use(the_hash_algo->rawsz); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 6cda1c08aa..6a6a2ff55d 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -77,6 +77,65 @@ static char *parse_refname(const char **next) } /* + * Wrapper around parse_refname which skips the next delimiter. + */ +static char *parse_next_refname(const char **next) +{ + if (line_termination) { + /* Without -z, consume SP and use next argument */ + if (!**next || **next == line_termination) + return NULL; + if (**next != ' ') + die("expected SP but got: %s", *next); + } else { + /* With -z, read the next NUL-terminated line */ + if (**next) + return NULL; + } + /* Skip the delimiter */ + (*next)++; + + return parse_refname(next); +} + +/* + * Wrapper around parse_arg which skips the next delimiter. + */ +static char *parse_next_arg(const char **next) +{ + struct strbuf arg = STRBUF_INIT; + + if (line_termination) { + /* Without -z, consume SP and use next argument */ + if (!**next || **next == line_termination) + return NULL; + if (**next != ' ') + die("expected SP but got: %s", *next); + } else { + /* With -z, read the next NUL-terminated line */ + if (**next) + return NULL; + } + /* Skip the delimiter */ + (*next)++; + + if (line_termination) { + /* Without -z, use the next argument */ + *next = parse_arg(*next, &arg); + } else { + /* With -z, use everything up to the next NUL */ + strbuf_addstr(&arg, *next); + *next += arg.len; + } + + if (arg.len) + return strbuf_detach(&arg, NULL); + + strbuf_release(&arg); + return NULL; +} + +/* * The value being parsed is <old-oid> (as opposed to <new-oid>; the * difference affects which error messages are generated): */ @@ -122,7 +181,7 @@ static int parse_next_oid(const char **next, const char *end, goto invalid; } else { /* Without -z, an empty value means all zeros: */ - oidclr(oid); + oidclr(oid, the_repository->hash_algo); } } else { /* With -z, read the next NUL-terminated line */ @@ -142,7 +201,7 @@ static int parse_next_oid(const char **next, const char *end, /* With -z, treat an empty value as all zeros: */ warning("%s %s: missing <new-oid>, treating as zero", command, refname); - oidclr(oid); + oidclr(oid, the_repository->hash_algo); } else { /* * With -z, an empty non-required value means @@ -214,6 +273,61 @@ static void parse_cmd_update(struct ref_transaction *transaction, strbuf_release(&err); } +static void parse_cmd_symref_update(struct ref_transaction *transaction, + const char *next, const char *end) +{ + char *refname, *new_target, *old_arg; + char *old_target = NULL; + struct strbuf err = STRBUF_INIT; + struct object_id old_oid; + int have_old_oid = 0; + + refname = parse_refname(&next); + if (!refname) + die("symref-update: missing <ref>"); + + new_target = parse_next_refname(&next); + if (!new_target) + die("symref-update %s: missing <new-target>", refname); + + old_arg = parse_next_arg(&next); + if (old_arg) { + old_target = parse_next_arg(&next); + if (!old_target) + die("symref-update %s: expected old value", refname); + + if (!strcmp(old_arg, "oid")) { + if (repo_get_oid(the_repository, old_target, &old_oid)) + die("symref-update %s: invalid oid: %s", refname, old_target); + + have_old_oid = 1; + } else if (!strcmp(old_arg, "ref")) { + if (check_refname_format(old_target, REFNAME_ALLOW_ONELEVEL)) + die("symref-update %s: invalid ref: %s", refname, old_target); + } else { + die("symref-update %s: invalid arg '%s' for old value", refname, old_arg); + } + } + + if (*next != line_termination) + die("symref-update %s: extra input: %s", refname, next); + + if (ref_transaction_update(transaction, refname, NULL, + have_old_oid ? &old_oid : NULL, + new_target, + have_old_oid ? NULL : old_target, + update_flags | create_reflog_flag, + msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + free(old_arg); + free(old_target); + free(new_target); + strbuf_release(&err); +} + static void parse_cmd_create(struct ref_transaction *transaction, const char *next, const char *end) { @@ -234,13 +348,42 @@ static void parse_cmd_create(struct ref_transaction *transaction, if (*next != line_termination) die("create %s: extra input: %s", refname, next); - if (ref_transaction_create(transaction, refname, &new_oid, + if (ref_transaction_create(transaction, refname, &new_oid, NULL, + update_flags | create_reflog_flag, + msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + strbuf_release(&err); +} + + +static void parse_cmd_symref_create(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + char *refname, *new_target; + + refname = parse_refname(&next); + if (!refname) + die("symref-create: missing <ref>"); + + new_target = parse_next_refname(&next); + if (!new_target) + die("symref-create %s: missing <new-target>", refname); + + if (*next != line_termination) + die("symref-create %s: extra input: %s", refname, next); + + if (ref_transaction_create(transaction, refname, NULL, new_target, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); update_flags = default_flags; free(refname); + free(new_target); strbuf_release(&err); } @@ -270,7 +413,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction, if (ref_transaction_delete(transaction, refname, have_old ? &old_oid : NULL, - update_flags, msg, &err)) + NULL, update_flags, msg, &err)) die("%s", err.buf); update_flags = default_flags; @@ -278,6 +421,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction, strbuf_release(&err); } + +static void parse_cmd_symref_delete(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + char *refname, *old_target; + + if (!(update_flags & REF_NO_DEREF)) + die("symref-delete: cannot operate with deref mode"); + + refname = parse_refname(&next); + if (!refname) + die("symref-delete: missing <ref>"); + + old_target = parse_next_refname(&next); + + if (*next != line_termination) + die("symref-delete %s: extra input: %s", refname, next); + + if (ref_transaction_delete(transaction, refname, NULL, + old_target, update_flags, msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + free(old_target); + strbuf_release(&err); +} + + static void parse_cmd_verify(struct ref_transaction *transaction, const char *next, const char *end) { @@ -291,17 +464,53 @@ static void parse_cmd_verify(struct ref_transaction *transaction, if (parse_next_oid(&next, end, &old_oid, "verify", refname, PARSE_SHA1_OLD)) - oidclr(&old_oid); + oidclr(&old_oid, the_repository->hash_algo); if (*next != line_termination) die("verify %s: extra input: %s", refname, next); if (ref_transaction_verify(transaction, refname, &old_oid, - update_flags, &err)) + NULL, update_flags, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + strbuf_release(&err); +} + +static void parse_cmd_symref_verify(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + struct object_id old_oid; + char *refname, *old_target; + + if (!(update_flags & REF_NO_DEREF)) + die("symref-verify: cannot operate with deref mode"); + + refname = parse_refname(&next); + if (!refname) + die("symref-verify: missing <ref>"); + + /* + * old_ref is optional, if not provided, we need to ensure that the + * ref doesn't exist. + */ + old_target = parse_next_refname(&next); + if (!old_target) + oidcpy(&old_oid, null_oid()); + + if (*next != line_termination) + die("symref-verify %s: extra input: %s", refname, next); + + if (ref_transaction_verify(transaction, refname, + old_target ? NULL : &old_oid, + old_target, update_flags, &err)) die("%s", err.buf); update_flags = default_flags; free(refname); + free(old_target); strbuf_release(&err); } @@ -380,15 +589,19 @@ static const struct parse_cmd { unsigned args; enum update_refs_state state; } command[] = { - { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, - { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, - { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, - { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, - { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, - { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, - { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, - { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, - { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, + { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, + { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, + { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, + { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN }, + { "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN }, + { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN }, + { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN }, + { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, + { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, + { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, + { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, + { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, }; static void update_refs_stdin(void) @@ -564,7 +777,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) * The empty string implies that the reference * must not already exist: */ - oidclr(&oldoid); + oidclr(&oldoid, the_repository->hash_algo); else if (repo_get_oid(the_repository, oldval, &oldoid)) die("%s: not a valid old SHA1", oldval); } diff --git a/builtin/var.c b/builtin/var.c index 5dc384810c..e30ff45be1 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -12,6 +12,7 @@ #include "refs.h" #include "path.h" #include "strbuf.h" +#include "run-command.h" static const char var_usage[] = "git var (-l | <variable>)"; @@ -51,7 +52,7 @@ static char *default_branch(int ident_flag UNUSED) static char *shell_path(int ident_flag UNUSED) { - return xstrdup(SHELL_PATH); + return git_shell_path(); } static char *git_attr_val_system(int ident_flag UNUSED) diff --git a/builtin/worktree.c b/builtin/worktree.c index 893e973871..1d51e54fcd 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -736,16 +736,14 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote) return 1; } -static const char *dwim_branch(const char *path, const char **new_branch) +static char *dwim_branch(const char *path, char **new_branch) { int n; int branch_exists; const char *s = worktree_basename(path, &n); - const char *branchname = xstrndup(s, n); + char *branchname = xstrndup(s, n); struct strbuf ref = STRBUF_INIT; - UNLEAK(branchname); - branch_exists = !strbuf_check_branch_ref(&ref, branchname) && refs_ref_exists(get_main_ref_store(the_repository), ref.buf); @@ -756,8 +754,7 @@ static const char *dwim_branch(const char *path, const char **new_branch) *new_branch = branchname; if (guess_remote) { struct object_id oid; - const char *remote = - unique_tracking_name(*new_branch, &oid, NULL); + char *remote = unique_tracking_name(*new_branch, &oid, NULL); return remote; } return NULL; @@ -769,6 +766,8 @@ static int add(int ac, const char **av, const char *prefix) const char *new_branch_force = NULL; char *path; const char *branch; + char *branch_to_free = NULL; + char *new_branch_to_free = NULL; const char *new_branch = NULL; const char *opt_track = NULL; const char *lock_reason = NULL; @@ -859,16 +858,17 @@ static int add(int ac, const char **av, const char *prefix) opts.orphan = dwim_orphan(&opts, !!opt_track, 0); } else if (ac < 2) { /* DWIM: Guess branch name from path. */ - const char *s = dwim_branch(path, &new_branch); + char *s = dwim_branch(path, &new_branch_to_free); if (s) - branch = s; + branch = branch_to_free = s; + new_branch = new_branch_to_free; /* DWIM: Infer --orphan when repo has no refs. */ opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1); } else if (ac == 2) { struct object_id oid; struct commit *commit; - const char *remote; + char *remote; commit = lookup_commit_reference_by_name(branch); if (!commit) { @@ -923,6 +923,8 @@ static int add(int ac, const char **av, const char *prefix) ret = add_worktree(path, branch, &opts); free(path); + free(branch_to_free); + free(new_branch_to_free); return ret; } |
