diff options
Diffstat (limited to 'builtin')
90 files changed, 3139 insertions, 2051 deletions
diff --git a/builtin/add.c b/builtin/add.c index b7d3ff1e28..40b61ef90d 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006 Linus Torvalds */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "advice.h" #include "config.h" @@ -40,20 +40,20 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only) { int i, ret = 0; - for (i = 0; i < the_index.cache_nr; i++) { - struct cache_entry *ce = the_index.cache[i]; + for (i = 0; i < the_repository->index->cache_nr; i++) { + struct cache_entry *ce = the_repository->index->cache[i]; int err; if (!include_sparse && (ce_skip_worktree(ce) || - !path_in_sparse_checkout(ce->name, &the_index))) + !path_in_sparse_checkout(ce->name, the_repository->index))) continue; - if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) + if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL)) continue; if (!show_only) - err = chmod_index_entry(&the_index, ce, flip); + err = chmod_index_entry(the_repository->index, ce, flip); else err = S_ISREG(ce->ce_mode) ? 0 : -1; @@ -68,20 +68,20 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) { int i, retval = 0; - for (i = 0; i < the_index.cache_nr; i++) { - struct cache_entry *ce = the_index.cache[i]; + for (i = 0; i < the_repository->index->cache_nr; i++) { + struct cache_entry *ce = the_repository->index->cache[i]; if (!include_sparse && (ce_skip_worktree(ce) || - !path_in_sparse_checkout(ce->name, &the_index))) + !path_in_sparse_checkout(ce->name, the_repository->index))) continue; if (ce_stage(ce)) continue; /* do not touch unmerged paths */ if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode)) continue; /* do not touch non blobs */ - if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) + if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL)) continue; - retval |= add_file_to_index(&the_index, ce->name, + retval |= add_file_to_index(the_repository->index, ce->name, flags | ADD_CACHE_RENORMALIZE); } @@ -100,11 +100,11 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (dir_path_match(&the_index, entry, pathspec, prefix, seen)) + if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen)) *dst++ = entry; } dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, &the_index, seen, + add_pathspec_matches_against_index(pathspec, the_repository->index, seen, PS_IGNORE_SKIP_WORKTREE); return seen; } @@ -119,14 +119,14 @@ static int refresh(int verbose, const struct pathspec *pathspec) (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET); seen = xcalloc(pathspec->nr, 1); - refresh_index(&the_index, flags, pathspec, seen, + refresh_index(the_repository->index, flags, pathspec, seen, _("Unstaged changes after refreshing the index:")); for (i = 0; i < pathspec->nr; i++) { if (!seen[i]) { const char *path = pathspec->items[i].original; if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) || - !path_in_sparse_checkout(path, &the_index)) { + !path_in_sparse_checkout(path, the_repository->index)) { string_list_append(&only_match_skip_worktree, pathspec->items[i].original); } else { @@ -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 | @@ -338,12 +334,12 @@ static int add_files(struct dir_struct *dir, int flags) for (i = 0; i < dir->nr; i++) { if (!include_sparse && - !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) { + !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) { string_list_append(&matched_sparse_paths, dir->entries[i]->name); continue; } - if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) { + if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) { if (!ignore_add_errors) die(_("adding files failed")); exit_status = 1; @@ -461,8 +457,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (repo_read_index_preload(the_repository, &pathspec, 0) < 0) die(_("index file corrupt")); - die_in_unpopulated_submodule(&the_index, prefix); - die_path_inside_submodule(&the_index, &pathspec); + die_in_unpopulated_submodule(the_repository->index, prefix); + die_path_inside_submodule(the_repository->index, &pathspec); if (add_new_files) { int baselen; @@ -474,7 +470,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, &the_index, &pathspec); + baselen = fill_directory(&dir, the_repository->index, &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen); } @@ -491,7 +487,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (!seen) seen = find_pathspecs_matching_against_index(&pathspec, - &the_index, PS_IGNORE_SKIP_WORKTREE); + the_repository->index, PS_IGNORE_SKIP_WORKTREE); /* * file_exists() assumes exact match @@ -527,8 +523,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) !file_exists(path)) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, &the_index, path, &dtype)) - dir_add_ignored(&dir, &the_index, + if (is_excluded(&dir, the_repository->index, path, &dtype)) + dir_add_ignored(&dir, the_repository->index, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), @@ -569,7 +565,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) end_odb_transaction(); finish: - if (write_locked_index(&the_index, &lock_file, + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write new index file")); diff --git a/builtin/am.c b/builtin/am.c index 022cae2e8d..370f5593f2 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -3,7 +3,7 @@ * * Based on git-am.sh by Junio C Hamano. */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "abspath.h" #include "advice.h" @@ -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")); @@ -1001,7 +1001,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, if (mkdir(state->dir, 0777) < 0 && errno != EEXIST) die_errno(_("failed to create directory '%s'"), state->dir); - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + "REBASE_HEAD", NULL, REF_NO_DEREF); if (split_mail(state, patch_format, paths, keep_cr) < 0) { am_destroy(state); @@ -1081,12 +1082,15 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, if (!repo_get_oid(the_repository, "HEAD", &curr_head)) { write_state_text(state, "abort-safety", oid_to_hex(&curr_head)); if (!state->rebasing) - update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + "am", "ORIG_HEAD", &curr_head, NULL, + 0, + UPDATE_REFS_DIE_ON_ERR); } else { write_state_text(state, "abort-safety", ""); if (!state->rebasing) - delete_ref(NULL, "ORIG_HEAD", NULL, 0); + refs_delete_ref(get_main_ref_store(the_repository), + NULL, "ORIG_HEAD", NULL, 0); } /* @@ -1117,9 +1121,10 @@ 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")); - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + "REBASE_HEAD", NULL, REF_NO_DEREF); if (!repo_get_oid(the_repository, "HEAD", &head)) write_state_text(state, "abort-safety", oid_to_hex(&head)); @@ -1466,8 +1471,9 @@ static int parse_mail_rebase(struct am_state *state, const char *mail) oidcpy(&state->orig_commit, &commit_oid); write_state_text(state, "original-commit", oid_to_hex(&commit_oid)); - update_ref("am", "REBASE_HEAD", &commit_oid, - NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), "am", + "REBASE_HEAD", &commit_oid, + NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); return 0; } @@ -1536,8 +1542,8 @@ static int run_apply(const struct am_state *state, const char *index_file) if (index_file) { /* Reload index as apply_all_patches() will have modified it. */ - discard_index(&the_index); - read_index_from(&the_index, index_file, get_git_dir()); + discard_index(the_repository->index); + read_index_from(the_repository->index, index_file, get_git_dir()); } return 0; @@ -1567,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; @@ -1579,10 +1585,10 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa if (build_fake_ancestor(state, index_path)) return error("could not build fake ancestor"); - discard_index(&the_index); - read_index_from(&the_index, index_path, get_git_dir()); + discard_index(the_repository->index); + read_index_from(the_repository->index, index_path, get_git_dir()); - if (write_index_as_tree(&orig_tree, &the_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...")); @@ -1608,12 +1614,12 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa return error(_("Did you hand edit your patch?\n" "It does not apply to blobs recorded in its index.")); - if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL)) + if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL)) return error("could not write tree"); say(state, stdout, _("Falling back to patching base and 3-way merge...")); - discard_index(&the_index); + discard_index(the_repository->index); repo_read_index(the_repository); /* @@ -1660,7 +1666,7 @@ static void do_commit(const struct am_state *state) if (!state->no_verify && run_hooks("pre-applypatch")) exit(1); - if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL)) + if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL)) die(_("git write-tree failed to write a tree")); if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) { @@ -1697,8 +1703,9 @@ static void do_commit(const struct am_state *state) strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg), state->msg); - update_ref(sb.buf, "HEAD", &commit, old_oid, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), sb.buf, "HEAD", + &commit, old_oid, 0, + UPDATE_REFS_DIE_ON_ERR); if (state->rebasing) { FILE *fp = xfopen(am_path(state, "rewritten"), "a"); @@ -1711,6 +1718,7 @@ static void do_commit(const struct am_state *state) run_hooks("post-applypatch"); + free_commit_list(parents); strbuf_release(&sb); } @@ -1948,7 +1956,7 @@ static void am_resolve(struct am_state *state, int allow_empty) } } - if (unmerged_index(&the_index)) { + if (unmerged_index(the_repository->index)) { printf_ln(_("You still have unmerged paths in your index.\n" "You should 'git add' each file with resolved conflicts to mark them as such.\n" "You might run `git rm` on a file to accept \"deleted by them\" for it.")); @@ -1987,12 +1995,12 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.update = 1; opts.merge = 1; opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0; @@ -2006,7 +2014,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) return -1; } - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); return 0; @@ -2029,8 +2037,8 @@ static int merge_tree(struct tree *tree) memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.merge = 1; opts.fn = oneway_merge; init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size); @@ -2040,7 +2048,7 @@ static int merge_tree(struct tree *tree) return -1; } - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); return 0; @@ -2068,7 +2076,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem if (fast_forward_to(head_tree, head_tree, 1)) return -1; - if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL)) + if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL)) return -1; index_tree = parse_tree_indirect(&index); @@ -2144,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; @@ -2175,7 +2183,8 @@ static void am_abort(struct am_state *state) am_rerere_clear(); - curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL); + curr_branch = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", 0, &curr_head, NULL); has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) oidcpy(&curr_head, the_hash_algo->empty_tree); @@ -2188,11 +2197,13 @@ static void am_abort(struct am_state *state) die(_("failed to clean index")); if (has_orig_head) - update_ref("am --abort", "HEAD", &orig_head, - has_curr_head ? &curr_head : NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + "am --abort", "HEAD", &orig_head, + has_curr_head ? &curr_head : NULL, 0, + UPDATE_REFS_DIE_ON_ERR); else if (curr_branch) - delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + curr_branch, NULL, REF_NO_DEREF); free(curr_branch); am_destroy(state); @@ -2383,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 f69c3f7e43..dabce9b542 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c @@ -243,7 +243,7 @@ static int bisect_reset(const char *commit) strbuf_addstr(&branch, commit); } - if (branch.len && !ref_exists("BISECT_HEAD")) { + if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) { struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; @@ -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}; @@ -302,8 +303,8 @@ static int bisect_write(const char *state, const char *rev, goto finish; } - if (update_ref(NULL, tag.buf, &oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR)) { + if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0, + UPDATE_REFS_MSG_ON_ERR)) { res = -1; goto finish; } @@ -416,11 +417,12 @@ static void bisect_status(struct bisect_state *state, char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad); char *good_glob = xstrfmt("%s-*", terms->term_good); - if (ref_exists(bad_ref)) + if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref)) state->nr_bad = 1; - for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/", - (void *) &state->nr_good); + refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr, + good_glob, "refs/bisect/", + (void *) &state->nr_good); free(good_glob); free(bad_ref); @@ -574,9 +576,11 @@ static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs) reset_revision_walk(); repo_init_revisions(the_repository, revs, NULL); setup_revisions(0, NULL, revs, NULL); - for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb); + refs_for_each_glob_ref_in(get_main_ref_store(the_repository), + add_bisect_ref, bad, "refs/bisect/", &cb); cb.object_flags = UNINTERESTING; - for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb); + refs_for_each_glob_ref_in(get_main_ref_store(the_repository), + add_bisect_ref, good, "refs/bisect/", &cb); if (prepare_revision_walk(revs)) res = error(_("revision walk setup failed\n")); @@ -636,7 +640,7 @@ static int bisect_successful(struct bisect_terms *terms) char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad); int res; - read_ref(bad_ref, &oid); + refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid); commit = lookup_commit_reference_by_name(bad_ref); repo_format_commit_message(the_repository, commit, "%s", &commit_name, &pp); @@ -779,7 +783,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, /* * Verify HEAD */ - head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags); + head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + "HEAD", 0, &head_oid, &flags); if (!head) if (repo_get_oid(the_repository, "HEAD", &head_oid)) return error(_("bad HEAD - I need a HEAD")); @@ -838,8 +843,8 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, res = error(_("invalid ref: '%s'"), start_head.buf); goto finish; } - if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR)) { + if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0, + UPDATE_REFS_MSG_ON_ERR)) { res = BISECT_FAILED; goto finish; } @@ -972,7 +977,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, oid_array_append(&revs, &commit->object.oid); } - if (read_ref("BISECT_EXPECTED_REV", &expected)) + if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected)) verify_expected = 0; /* Ignore invalid file contents */ for (i = 0; i < revs.nr; i++) { @@ -982,7 +987,9 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, } if (verify_expected && !oideq(&revs.oid[i], &expected)) { unlink_or_warn(git_path_bisect_ancestors_ok()); - delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), + NULL, "BISECT_EXPECTED_REV", NULL, + REF_NO_DEREF); verify_expected = 0; } } @@ -1179,13 +1186,15 @@ static int verify_good(const struct bisect_terms *terms, const char *command) struct object_id good_rev; struct object_id current_rev; char *good_glob = xstrfmt("%s-*", terms->term_good); - int no_checkout = ref_exists("BISECT_HEAD"); + int no_checkout = refs_ref_exists(get_main_ref_store(the_repository), + "BISECT_HEAD"); - for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/", - &good_rev); + refs_for_each_glob_ref_in(get_main_ref_store(the_repository), + get_first_good, good_glob, "refs/bisect/", + &good_rev); free(good_glob); - if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev)) + if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", ¤t_rev)) return -1; res = bisect_checkout(&good_rev, no_checkout); diff --git a/builtin/blame.c b/builtin/blame.c index 9aa74680a3..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); @@ -1093,8 +1094,8 @@ parse_done: struct commit *head_commit; struct object_id head_oid; - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - &head_oid, NULL) || + if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, + &head_oid, NULL) || !(head_commit = lookup_commit_reference_gently(revs.repo, &head_oid, 1))) die("no such ref: HEAD"); @@ -1227,6 +1228,7 @@ parse_done: } cleanup: + free(path); cleanup_scoreboard(&sb); release_revisions(&revs); return 0; diff --git a/builtin/branch.c b/builtin/branch.c index dd3e3a7dc0..48cac74f97 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -148,8 +148,8 @@ static int branch_merged(int kind, const char *name, if (upstream && (reference_name = reference_name_to_free = - resolve_refdup(upstream, RESOLVE_REF_READING, - &oid, NULL)) != NULL) + refs_resolve_refdup(get_main_ref_store(the_repository), upstream, RESOLVE_REF_READING, + &oid, NULL)) != NULL) reference_rev = lookup_commit_reference(the_repository, &oid); } @@ -272,21 +272,24 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } } - target = resolve_refdup(name, - RESOLVE_REF_READING - | RESOLVE_REF_NO_RECURSE - | RESOLVE_REF_ALLOW_BAD_NAME, - &oid, &flags); + target = refs_resolve_refdup(get_main_ref_store(the_repository), + name, + RESOLVE_REF_READING + | RESOLVE_REF_NO_RECURSE + | RESOLVE_REF_ALLOW_BAD_NAME, + &oid, &flags); if (!target) { if (remote_branch) { error(_("remote-tracking branch '%s' not found"), bname.buf); } else { char *virtual_name = mkpathdup(fmt_remotes, bname.buf); - char *virtual_target = resolve_refdup(virtual_name, - RESOLVE_REF_READING - | RESOLVE_REF_NO_RECURSE - | RESOLVE_REF_ALLOW_BAD_NAME, - &oid, &flags); + char *virtual_target = refs_resolve_refdup(get_main_ref_store(the_repository), + virtual_name, + RESOLVE_REF_READING + | RESOLVE_REF_NO_RECURSE + | RESOLVE_REF_ALLOW_BAD_NAME, + &oid, + &flags); FREE_AND_NULL(virtual_name); if (virtual_target) @@ -317,13 +320,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, free(target); } - if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF)) + if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF)) ret = 1; for_each_string_list_item(item, &refs_to_delete) { char *describe_ref = item->util; char *name = item->string; - if (!ref_exists(name)) { + if (!refs_ref_exists(get_main_ref_store(the_repository), name)) { char *refname = name + branch_name_pos; if (!quiet) printf(remote_branch @@ -499,7 +502,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin static void print_current_branch_name(void) { int flags; - const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags); + const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + "HEAD", 0, NULL, &flags); const char *shortname; if (!refname) die(_("could not resolve HEAD")); @@ -555,7 +559,7 @@ static int replace_each_worktree_head_symref(struct worktree **worktrees, continue; refs = get_worktree_ref_store(worktrees[i]); - if (refs_create_symref(refs, "HEAD", newref, logmsg)) + if (refs_update_symref(refs, "HEAD", newref, logmsg)) ret = error(_("HEAD of working tree %s is not updated"), worktrees[i]->path); } @@ -580,7 +584,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int * Bad name --- this could be an attempt to rename a * ref that we used to allow to be created by accident. */ - if (ref_exists(oldref.buf)) + if (refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) recovery = 1; else { int code = die_message(_("invalid branch name: '%s'"), oldname); @@ -601,7 +605,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int } } - if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) { + if ((copy || !(oldref_usage & IS_HEAD)) && !refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) { if (oldref_usage & IS_HEAD) die(_("no commit on branch '%s' yet"), oldname); else @@ -632,9 +636,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int oldref.buf, newref.buf); if (!copy && !(oldref_usage & IS_ORPHAN) && - rename_ref(oldref.buf, newref.buf, logmsg.buf)) + refs_rename_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf)) die(_("branch rename failed")); - if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf)) + if (copy && refs_copy_existing_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf)) die(_("branch copy failed")); if (recovery) { @@ -786,7 +790,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) track = git_branch_track; - head = resolve_refdup("HEAD", 0, &head_oid, NULL); + head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", + 0, &head_oid, NULL); if (!head) die(_("failed to resolve HEAD as a valid ref")); if (!strcmp(head, "HEAD")) @@ -891,7 +896,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); - if (!ref_exists(branch_ref.buf)) + if (!refs_ref_exists(get_main_ref_store(the_repository), branch_ref.buf)) error((!argc || branch_checked_out(branch_ref.buf)) ? _("no commit on branch '%s' yet") : _("no branch named '%s'"), @@ -936,7 +941,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("no such branch '%s'"), argv[0]); } - if (!ref_exists(branch->refname)) { + if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) { if (!argc || branch_checked_out(branch->refname)) die(_("no commit on branch '%s' yet"), branch->name); die(_("branch '%s' does not exist"), branch->name); 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..86d0ed7049 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; @@ -202,12 +207,13 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) builtin_bundle_unbundle_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ + if (!startup_info->have_repository) + die(_("Need a repository to unbundle.")); + if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { ret = 1; goto cleanup; } - if (!startup_info->have_repository) - die(_("Need a repository to unbundle.")); if (progress) strvec_pushl(&extra_index_pack_args, "-v", "--progress-title", _("Unbundling objects"), NULL); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 0c948f40fb..18fe58d6b8 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -3,7 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "config.h" #include "convert.h" @@ -77,7 +77,7 @@ static int filter_object(const char *path, unsigned mode, struct checkout_metadata meta; init_checkout_metadata(&meta, NULL, NULL, oid); - if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) { + if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) { free(*buf); *size = strbuf.len; *buf = strbuf_detach(&strbuf, NULL); @@ -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-attr.c b/builtin/check-attr.c index c1da1d184e..9376810710 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "attr.h" @@ -71,9 +70,9 @@ static void check_attr(const char *prefix, struct attr_check *check, prefix_path(prefix, prefix ? strlen(prefix) : 0, file); if (collect_all) { - git_all_attrs(&the_index, full_path, check); + git_all_attrs(the_repository->index, full_path, check); } else { - git_check_attr(&the_index, full_path, check); + git_check_attr(the_repository->index, full_path, check); } output_attr(check, file); diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 906cd96753..2bda6a1d46 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "dir.h" @@ -36,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'); @@ -95,21 +94,21 @@ static int check_ignore(struct dir_struct *dir, PATHSPEC_KEEP_ORDER, prefix, argv); - die_path_inside_submodule(&the_index, &pathspec); + die_path_inside_submodule(the_repository->index, &pathspec); /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ - seen = find_pathspecs_matching_against_index(&pathspec, &the_index, + seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index, PS_HEED_SKIP_WORKTREE); for (i = 0; i < pathspec.nr; i++) { full_path = pathspec.items[i].match; pattern = NULL; if (!seen[i]) { int dtype = DT_UNKNOWN; - pattern = last_matching_pattern(dir, &the_index, + pattern = last_matching_pattern(dir, the_repository->index, full_path, &dtype); if (!verbose && pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 2e086a204d..29e744d11b 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -4,7 +4,7 @@ * Copyright (C) 2005 Linus Torvalds * */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "config.h" #include "gettext.h" @@ -69,7 +69,7 @@ static void write_tempfile_record(const char *name, const char *prefix) static int checkout_file(const char *name, const char *prefix) { int namelen = strlen(name); - int pos = index_name_pos(&the_index, name, namelen); + int pos = index_name_pos(the_repository->index, name, namelen); int has_same_name = 0; int is_file = 0; int is_skipped = 1; @@ -79,8 +79,8 @@ static int checkout_file(const char *name, const char *prefix) if (pos < 0) pos = -pos - 1; - while (pos < the_index.cache_nr) { - struct cache_entry *ce = the_index.cache[pos]; + while (pos <the_repository->index->cache_nr) { + struct cache_entry *ce =the_repository->index->cache[pos]; if (ce_namelen(ce) != namelen || memcmp(ce->name, name, namelen)) break; @@ -140,8 +140,8 @@ static int checkout_all(const char *prefix, int prefix_length) int i, errs = 0; struct cache_entry *last_ce = NULL; - for (i = 0; i < the_index.cache_nr ; i++) { - struct cache_entry *ce = the_index.cache[i]; + for (i = 0; i < the_repository->index->cache_nr ; i++) { + struct cache_entry *ce = the_repository->index->cache[i]; if (S_ISSPARSEDIR(ce->ce_mode)) { if (!ce_skip_worktree(ce)) @@ -154,8 +154,8 @@ static int checkout_all(const char *prefix, int prefix_length) * first entry inside the expanded sparse directory). */ if (ignore_skip_worktree) { - ensure_full_index(&the_index); - ce = the_index.cache[i]; + ensure_full_index(the_repository->index); + ce = the_repository->index->cache[i]; } } @@ -260,7 +260,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_checkout_index_options, builtin_checkout_index_usage, 0); - state.istate = &the_index; + state.istate = the_repository->index; state.force = force; state.quiet = quiet; state.not_new = not_new; @@ -280,7 +280,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) */ if (index_opt && !state.base_dir_len && !to_tempfile) { state.refresh_cache = 1; - state.istate = &the_index; + state.istate = the_repository->index; repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); } @@ -339,7 +339,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) return 1; if (is_lock_file_locked(&lock_file) && - write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die("Unable to write new index file"); return 0; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 71e6036aab..1748d68c96 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "advice.h" #include "branch.h" @@ -146,7 +145,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base, return READ_TREE_RECURSIVE; len = base->len + strlen(pathname); - ce = make_empty_cache_entry(&the_index, len); + ce = make_empty_cache_entry(the_repository->index, len); oidcpy(&ce->oid, oid); memcpy(ce->name, base->buf, base->len); memcpy(ce->name + base->len, pathname, len - base->len); @@ -159,9 +158,9 @@ static int update_some(const struct object_id *oid, struct strbuf *base, * entry in place. Whether it is UPTODATE or not, checkout_entry will * do the right thing. */ - pos = index_name_pos(&the_index, ce->name, ce->ce_namelen); + pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen); if (pos >= 0) { - struct cache_entry *old = the_index.cache[pos]; + struct cache_entry *old = the_repository->index->cache[pos]; if (ce->ce_mode == old->ce_mode && !ce_intent_to_add(old) && oideq(&ce->oid, &old->oid)) { @@ -171,7 +170,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base, } } - add_index_entry(&the_index, ce, + add_index_entry(the_repository->index, ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; } @@ -190,8 +189,8 @@ static int read_tree_some(struct tree *tree, const struct pathspec *pathspec) static int skip_same_name(const struct cache_entry *ce, int pos) { - while (++pos < the_index.cache_nr && - !strcmp(the_index.cache[pos]->name, ce->name)) + while (++pos < the_repository->index->cache_nr && + !strcmp(the_repository->index->cache[pos]->name, ce->name)) ; /* skip */ return pos; } @@ -199,9 +198,9 @@ static int skip_same_name(const struct cache_entry *ce, int pos) static int check_stage(int stage, const struct cache_entry *ce, int pos, int overlay_mode) { - while (pos < the_index.cache_nr && - !strcmp(the_index.cache[pos]->name, ce->name)) { - if (ce_stage(the_index.cache[pos]) == stage) + while (pos < the_repository->index->cache_nr && + !strcmp(the_repository->index->cache[pos]->name, ce->name)) { + if (ce_stage(the_repository->index->cache[pos]) == stage) return 0; pos++; } @@ -218,8 +217,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos) unsigned seen = 0; const char *name = ce->name; - while (pos < the_index.cache_nr) { - ce = the_index.cache[pos]; + while (pos < the_repository->index->cache_nr) { + ce = the_repository->index->cache[pos]; if (strcmp(name, ce->name)) break; seen |= (1 << ce_stage(ce)); @@ -235,10 +234,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos, const struct checkout *state, int *nr_checkouts, int overlay_mode) { - while (pos < the_index.cache_nr && - !strcmp(the_index.cache[pos]->name, ce->name)) { - if (ce_stage(the_index.cache[pos]) == stage) - return checkout_entry(the_index.cache[pos], state, + while (pos < the_repository->index->cache_nr && + !strcmp(the_repository->index->cache[pos]->name, ce->name)) { + if (ce_stage(the_repository->index->cache[pos]) == stage) + return checkout_entry(the_repository->index->cache[pos], state, NULL, nr_checkouts); pos++; } @@ -256,7 +255,7 @@ static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts, struct mem_pool *ce_mem_pool, int conflict_style) { - struct cache_entry *ce = the_index.cache[pos]; + struct cache_entry *ce = the_repository->index->cache[pos]; const char *path = ce->name; mmfile_t ancestor, ours, theirs; enum ll_merge_result merge_status; @@ -269,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state, int renormalize = 0; memset(threeway, 0, sizeof(threeway)); - while (pos < the_index.cache_nr) { + while (pos < the_repository->index->cache_nr) { int stage; stage = ce_stage(ce); if (!stage || strcmp(path, ce->name)) @@ -278,7 +277,7 @@ static int checkout_merged(int pos, const struct checkout *state, if (stage == 2) mode = create_ce_mode(ce->ce_mode); pos++; - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; } if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2])) return error(_("path '%s' does not have necessary versions"), path); @@ -356,7 +355,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce, * match_pathspec() for _all_ entries when * opts->source_tree != NULL. */ - if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) + if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) ce->ce_flags |= CE_MATCHED; } @@ -367,7 +366,7 @@ static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce, ce->ce_flags &= ~CE_MATCHED; if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) return; - if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) { + if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) { ce->ce_flags |= CE_MATCHED; if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) /* @@ -391,7 +390,7 @@ static int checkout_worktree(const struct checkout_opts *opts, state.force = 1; state.refresh_cache = 1; - state.istate = &the_index; + state.istate = the_repository->index; mem_pool_init(&ce_mem_pool, 0); get_parallel_checkout_configs(&pc_workers, &pc_threshold); @@ -404,8 +403,8 @@ static int checkout_worktree(const struct checkout_opts *opts, if (pc_workers > 1) init_parallel_checkout(); - for (pos = 0; pos < the_index.cache_nr; pos++) { - struct cache_entry *ce = the_index.cache[pos]; + for (pos = 0; pos < the_repository->index->cache_nr; pos++) { + struct cache_entry *ce = the_repository->index->cache[pos]; if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, @@ -429,7 +428,7 @@ static int checkout_worktree(const struct checkout_opts *opts, errs |= run_parallel_checkout(&state, pc_workers, pc_threshold, NULL, NULL); mem_pool_discard(&ce_mem_pool, should_validate_cache_entries()); - remove_marked_cache_entries(&the_index, 1); + remove_marked_cache_entries(the_repository->index, 1); remove_scheduled_dirs(); errs |= finish_delayed_checkout(&state, opts->show_progress); @@ -571,7 +570,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->source_tree) read_tree_some(opts->source_tree, &opts->pathspec); if (opts->merge) - unmerge_index(&the_index, &opts->pathspec, CE_MATCHED); + unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED); ps_matched = xcalloc(opts->pathspec.nr, 1); @@ -579,13 +578,13 @@ static int checkout_paths(const struct checkout_opts *opts, * Make sure all pathspecs participated in locating the paths * to be checked out. */ - for (pos = 0; pos < the_index.cache_nr; pos++) + for (pos = 0; pos < the_repository->index->cache_nr; pos++) if (opts->overlay_mode) - mark_ce_for_checkout_overlay(the_index.cache[pos], + mark_ce_for_checkout_overlay(the_repository->index->cache[pos], ps_matched, opts); else - mark_ce_for_checkout_no_overlay(the_index.cache[pos], + mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos], ps_matched, opts); @@ -596,8 +595,8 @@ static int checkout_paths(const struct checkout_opts *opts, free(ps_matched); /* Any unmerged paths? */ - for (pos = 0; pos < the_index.cache_nr; pos++) { - const struct cache_entry *ce = the_index.cache[pos]; + for (pos = 0; pos < the_repository->index->cache_nr; pos++) { + const struct cache_entry *ce = the_repository->index->cache[pos]; if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) continue; @@ -622,7 +621,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->checkout_worktree) errs |= checkout_worktree(opts, new_branch_info); else - remove_marked_cache_entries(&the_index, 1); + remove_marked_cache_entries(the_repository->index, 1); /* * Allow updating the index when checking out from the index. @@ -634,7 +633,7 @@ static int checkout_paths(const struct checkout_opts *opts, checkout_index = opts->checkout_index; if (checkout_index) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); } else { /* @@ -646,7 +645,8 @@ static int checkout_paths(const struct checkout_opts *opts, rollback_lock_file(&lock_file); } - read_ref_full("HEAD", 0, &rev, NULL); + refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, + &rev, NULL); head = lookup_commit_reference_gently(the_repository, &rev, 1); errs |= post_checkout_hook(head, head, 0); @@ -703,8 +703,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o, opts.merge = 1; opts.fn = oneway_merge; opts.verbose_update = o->show_progress; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(), NULL); @@ -756,12 +756,12 @@ static void init_topts(struct unpack_trees_options *topts, int merge, { memset(topts, 0, sizeof(*topts)); topts->head_idx = -1; - topts->src_index = &the_index; - topts->dst_index = &the_index; + topts->src_index = the_repository->index; + topts->dst_index = the_repository->index; setup_unpack_trees_porcelain(topts, "checkout"); - topts->initial_checkout = is_index_unborn(&the_index); + topts->initial_checkout = is_index_unborn(the_repository->index); topts->update = 1; topts->merge = 1; topts->quiet = merge && old_commit; @@ -783,7 +783,7 @@ static int merge_working_tree(const struct checkout_opts *opts, if (repo_read_index_preload(the_repository, NULL, 0) < 0) return error(_("index file corrupt")); - resolve_undo_clear_index(&the_index); + resolve_undo_clear_index(the_repository->index); if (opts->new_orphan_branch && opts->orphan_from_empty_tree) { if (new_branch_info->commit) BUG("'switch --orphan' should never accept a commit as starting point"); @@ -807,9 +807,9 @@ static int merge_working_tree(const struct checkout_opts *opts, struct unpack_trees_options topts; const struct object_id *old_commit_oid; - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); - if (unmerged_index(&the_index)) { + if (unmerged_index(the_repository->index)) { error(_("you need to resolve your current index first")); return 1; } @@ -919,10 +919,10 @@ static int merge_working_tree(const struct checkout_opts *opts, } } - if (!cache_tree_fully_valid(the_index.cache_tree)) - cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); + if (!cache_tree_fully_valid(the_repository->index->cache_tree)) + cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); if (!opts->discard_changes && !opts->quiet && new_branch_info->commit) @@ -958,7 +958,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts, int ret; struct strbuf err = STRBUF_INIT; - ret = safe_create_reflog(refname, &err); + ret = refs_create_reflog(get_main_ref_store(the_repository), + refname, &err); if (ret) { fprintf(stderr, _("Can not do reflog for '%s': %s\n"), opts->new_orphan_branch, err.buf); @@ -999,8 +1000,10 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) { /* Nothing to do. */ } else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */ - update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL, - REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg.buf, + "HEAD", &new_branch_info->commit->object.oid, + NULL, + REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); if (!opts->quiet) { if (old_branch_info->path && advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach) @@ -1008,7 +1011,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, describe_detached_head(_("HEAD is now at"), new_branch_info->commit); } } else if (new_branch_info->path) { /* Switch branches. */ - if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0) + if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0) die(_("unable to update HEAD")); if (!opts->quiet) { if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) { @@ -1029,8 +1032,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts, } } if (old_branch_info->path && old_branch_info->name) { - if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path)) - delete_reflog(old_branch_info->path); + if (!refs_ref_exists(get_main_ref_store(the_repository), old_branch_info->path) && refs_reflog_exists(get_main_ref_store(the_repository), old_branch_info->path)) + refs_delete_reflog(get_main_ref_store(the_repository), + old_branch_info->path); } } remove_branch_state(the_repository, !opts->quiet); @@ -1129,7 +1133,8 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne object->flags &= ~UNINTERESTING; add_pending_object(&revs, object, oid_to_hex(&object->oid)); - for_each_ref(add_pending_uninteresting_ref, &revs); + refs_for_each_ref(get_main_ref_store(the_repository), + add_pending_uninteresting_ref, &revs); if (new_commit) add_pending_oid(&revs, "HEAD", &new_commit->object.oid, @@ -1159,7 +1164,8 @@ static int switch_branches(const struct checkout_opts *opts, trace2_cmd_mode("branch"); memset(&old_branch_info, 0, sizeof(old_branch_info)); - old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag); + old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", 0, &rev, &flag); if (old_branch_info.path) old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1); if (!(flag & REF_ISSYMREF)) @@ -1247,7 +1253,7 @@ static void setup_new_branch_info_and_source_tree( setup_branch_path(new_branch_info); if (!check_refname_format(new_branch_info->path, 0) && - !read_ref(new_branch_info->path, &branch_rev)) + !refs_read_ref(get_main_ref_store(the_repository), new_branch_info->path, &branch_rev)) oidcpy(rev, &branch_rev); else /* not an existing branch */ @@ -1269,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" @@ -1310,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; @@ -1410,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; @@ -1453,6 +1460,7 @@ static int parse_branchname_arg(int argc, const char **argv, argc--; } + free(remote); return argcount; } @@ -1466,7 +1474,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) if (!opts->new_branch) die(_("You are on a branch yet to be born")); strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); - status = create_symref("HEAD", branch_ref.buf, "checkout -b"); + status = refs_update_symref(get_main_ref_store(the_repository), + "HEAD", branch_ref.buf, "checkout -b"); strbuf_release(&branch_ref); if (!opts->quiet) fprintf(stderr, _("Switched to a new branch '%s'\n"), @@ -1553,7 +1562,8 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, if (opts->ignore_other_worktrees) return; - head_ref = resolve_refdup("HEAD", 0, NULL, &flags); + head_ref = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", 0, NULL, &flags); if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref))) die_if_checked_out(full_ref, 1); free(head_ref); @@ -1562,6 +1572,10 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, static int checkout_branch(struct checkout_opts *opts, struct branch_info *new_branch_info) { + int noop_switch = (!new_branch_info->name && + !opts->new_branch && + !opts->force_detach); + if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); @@ -1573,9 +1587,14 @@ static int checkout_branch(struct checkout_opts *opts, die(_("'%s' cannot be used with switching branches"), "--[no]-overlay"); - if (opts->writeout_stage) - die(_("'%s' cannot be used with switching branches"), - "--ours/--theirs"); + if (opts->writeout_stage) { + const char *msg; + if (noop_switch) + msg = _("'%s' needs the paths to check out"); + else + msg = _("'%s' cannot be used with switching branches"); + die(msg, "--ours/--theirs"); + } if (opts->force && opts->merge) die(_("'%s' cannot be used with '%s'"), "-f", "-m"); @@ -1602,10 +1621,8 @@ static int checkout_branch(struct checkout_opts *opts, die(_("Cannot switch branch to a non-commit '%s'"), new_branch_info->name); - if (!opts->switch_branch_doing_nothing_is_ok && - !new_branch_info->name && - !opts->new_branch && - !opts->force_detach) + if (noop_switch && + !opts->switch_branch_doing_nothing_is_ok) die(_("missing branch or commit argument")); if (!opts->implicit_detach && @@ -1634,7 +1651,7 @@ static int checkout_branch(struct checkout_opts *opts, struct object_id rev; int flag; - if (!read_ref_full("HEAD", 0, &rev, &flag) && + if (!refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &rev, &flag) && (flag & REF_ISSYMREF) && is_null_oid(&rev)) return switch_unborn_to_new_branch(opts); } diff --git a/builtin/clean.c b/builtin/clean.c index 29efe84153..ded5a91534 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -6,7 +6,6 @@ * Based on git-clean.sh by Pavel Roskin */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "config.h" @@ -714,7 +713,7 @@ static int filter_by_patterns_cmd(void) for_each_string_list_item(item, &del_list) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, &the_index, item->string, &dtype)) { + if (is_excluded(&dir, the_repository->index, item->string, &dtype)) { *item->string = '\0'; changed++; } @@ -1021,7 +1020,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) PATHSPEC_PREFER_CWD, prefix, argv); - fill_directory(&dir, &the_index, &pathspec); + fill_directory(&dir, the_repository->index, &pathspec); correct_untracked_entries(&dir); for (i = 0; i < dir.nr; i++) { @@ -1029,7 +1028,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct stat st; const char *rel; - if (!index_name_is_other(&the_index, ent->name, ent->len)) + if (!index_name_is_other(the_repository->index, ent->name, ent->len)) continue; if (lstat(ent->name, &st)) diff --git a/builtin/clone.c b/builtin/clone.c index 74ec14542e..af6017d41a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -8,7 +8,6 @@ * Clone a repository into a different directory that does not yet exist. */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "advice.h" @@ -72,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; @@ -178,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; @@ -329,7 +328,20 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, int src_len, dest_len; struct dir_iterator *iter; int iter_status; - struct strbuf realpath = STRBUF_INIT; + + /* + * Refuse copying directories by default which aren't owned by us. The + * code that performs either the copying or hardlinking is not prepared + * to handle various edge cases where an adversary may for example + * racily swap out files for symlinks. This can cause us to + * inadvertently use the wrong source file. + * + * Furthermore, even if we were prepared to handle such races safely, + * creating hardlinks across user boundaries is an inherently unsafe + * operation as the hardlinked files can be rewritten at will by the + * potentially-untrusted user. We thus refuse to do so by default. + */ + die_upon_dubious_ownership(NULL, NULL, src_repo); mkdir_if_missing(dest->buf, 0777); @@ -377,9 +389,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, if (unlink(dest->buf) && errno != ENOENT) die_errno(_("failed to unlink '%s'"), dest->buf); if (!option_no_hardlinks) { - strbuf_realpath(&realpath, src->buf, 1); - if (!link(realpath.buf, dest->buf)) + if (!link(src->buf, dest->buf)) { + struct stat st; + + /* + * Sanity-check whether the created hardlink + * actually links to the expected file now. This + * catches time-of-check-time-of-use bugs in + * case the source file was meanwhile swapped. + */ + if (lstat(dest->buf, &st)) + die(_("hardlink cannot be checked at '%s'"), dest->buf); + if (st.st_mode != iter->st.st_mode || + st.st_ino != iter->st.st_ino || + st.st_dev != iter->st.st_dev || + st.st_size != iter->st.st_size || + st.st_uid != iter->st.st_uid || + st.st_gid != iter->st.st_gid) + die(_("hardlink different from source at '%s'"), dest->buf); + continue; + } if (option_local > 0) die_errno(_("failed to create link '%s'"), dest->buf); option_no_hardlinks = 1; @@ -392,8 +422,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, strbuf_setlen(src, src_len); die(_("failed to iterate over '%s'"), src->buf); } - - strbuf_release(&realpath); } static void clone_local(const char *src_repo, const char *dest_repo) @@ -495,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; @@ -502,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)); } @@ -517,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 { @@ -527,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; } @@ -539,7 +572,8 @@ static void write_remote_refs(const struct ref *local_refs) struct ref_transaction *t; struct strbuf err = STRBUF_INIT; - t = ref_transaction_begin(&err); + t = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!t) die("%s", err.buf); @@ -547,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); } @@ -570,8 +604,9 @@ static void write_followtags(const struct ref *refs, const char *msg) OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT)) continue; - update_ref(msg, ref->name, &ref->old_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg, + ref->name, &ref->old_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); } } @@ -623,9 +658,9 @@ static void update_remote_refs(const struct ref *refs, struct strbuf head_ref = STRBUF_INIT; strbuf_addstr(&head_ref, branch_top); strbuf_addstr(&head_ref, "HEAD"); - if (create_symref(head_ref.buf, - remote_head_points_at->peer_ref->name, - msg) < 0) + if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf, + remote_head_points_at->peer_ref->name, + msg) < 0) die(_("unable to update %s"), head_ref.buf); strbuf_release(&head_ref); } @@ -637,33 +672,36 @@ static void update_head(const struct ref *our, const struct ref *remote, const char *head; if (our && skip_prefix(our->name, "refs/heads/", &head)) { /* Local default branch link */ - if (create_symref("HEAD", our->name, NULL) < 0) + if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0) die(_("unable to update HEAD")); if (!option_bare) { - update_ref(msg, "HEAD", &our->old_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + msg, "HEAD", &our->old_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); install_branch_config(0, head, remote_name, our->name); } } else if (our) { struct commit *c = lookup_commit_reference(the_repository, &our->old_oid); /* --branch specifies a non-branch (i.e. tags), detach HEAD */ - update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg, + "HEAD", &c->object.oid, NULL, REF_NO_DEREF, + UPDATE_REFS_DIE_ON_ERR); } else if (remote) { /* * We know remote HEAD points to a non-branch, or * HEAD points to a branch but we don't know which one. * Detach HEAD in all these cases. */ - update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg, + "HEAD", &remote->old_oid, NULL, REF_NO_DEREF, + UPDATE_REFS_DIE_ON_ERR); } else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) { /* * Unborn head from remote; same as "our" case above except * that we have no ref to update. */ - if (create_symref("HEAD", unborn, NULL) < 0) + if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0) die(_("unable to update HEAD")); if (!option_bare) install_branch_config(0, head, remote_name, unborn); @@ -704,7 +742,8 @@ static int checkout(int submodule_progress, int filter_submodules) if (option_no_checkout) return 0; - head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL); + head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", + RESOLVE_REF_READING, &oid, NULL); if (!head) { warning(_("remote HEAD refers to nonexistent ref, " "unable to checkout")); @@ -731,8 +770,8 @@ static int checkout(int submodule_progress, int filter_submodules) opts.preserve_ignored = 0; opts.fn = oneway_merge; opts.verbose_update = (option_verbosity >= 0); - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; init_checkout_metadata(&opts.meta, head, &oid, NULL); tree = parse_tree_indirect(&oid); @@ -746,7 +785,7 @@ static int checkout(int submodule_progress, int filter_submodules) free(head); - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); err |= run_hooks_l("post-checkout", oid_to_hex(null_oid()), @@ -936,7 +975,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int submodule_progress; int filter_submodules = 0; int hash_algo; - unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; + enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; const int do_not_override_repo_unix_permissions = -1; struct transport_ls_refs_options transport_ls_refs_options = @@ -1256,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) @@ -1278,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; @@ -1425,6 +1464,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } else if (remote_head) { our_head_points_at = NULL; } else { + char *to_free = NULL; const char *branch; if (!mapped_refs) { @@ -1437,7 +1477,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) "refs/heads/", &branch)) { unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target); } else { - branch = git_default_branch_name(0); + branch = to_free = repo_default_branch_name(the_repository, 0); unborn_head = xstrfmt("refs/heads/%s", branch); } @@ -1453,6 +1493,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) * a match. */ our_head_points_at = find_remote_branch(mapped_refs, branch); + + free(to_free); } write_refspec_config(src_ref_prefix, our_head_points_at, @@ -1507,6 +1549,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) free(dir); free(path); free(repo_to_free); + 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 6e1484446b..66427ba82d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -5,7 +5,6 @@ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "advice.h" #include "config.h" @@ -38,10 +37,11 @@ #include "commit-reach.h" #include "commit-graph.h" #include "pretty.h" +#include "trailer.h" static const char * const builtin_commit_usage[] = { N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n" - " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>)]\n" + " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|reword):]<commit>]\n" " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n" " [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n" " [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n" @@ -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; @@ -142,14 +143,6 @@ static struct strbuf message = STRBUF_INIT; static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; -static int opt_pass_trailer(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - - strvec_pushl(opt->value, "--trailer", arg, NULL); - return 0; -} - static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset) { enum wt_status_format *value = (enum wt_status_format *)opt->value; @@ -266,19 +259,19 @@ static int list_paths(struct string_list *list, const char *with_tree, if (with_tree) { char *max_prefix = common_prefix(pattern); - overlay_tree_on_index(&the_index, with_tree, max_prefix); + overlay_tree_on_index(the_repository->index, with_tree, max_prefix); free(max_prefix); } /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < the_index.cache_nr; i++) { - const struct cache_entry *ce = the_index.cache[i]; + ensure_full_index(the_repository->index); + for (i = 0; i < the_repository->index->cache_nr; i++) { + const struct cache_entry *ce = the_repository->index->cache[i]; struct string_list_item *item; if (ce->ce_flags & CE_UPDATE) continue; - if (!ce_path_match(&the_index, ce, pattern, m)) + if (!ce_path_match(the_repository->index, ce, pattern, m)) continue; item = string_list_insert(list, ce->name); if (ce_skip_worktree(ce)) @@ -302,10 +295,10 @@ static void add_remove_files(struct string_list *list) continue; if (!lstat(p->string, &st)) { - if (add_to_index(&the_index, p->string, &st, 0)) + if (add_to_index(the_repository->index, p->string, &st, 0)) die(_("updating files failed")); } else - remove_file_from_index(&the_index, p->string); + remove_file_from_index(the_repository->index, p->string); } } @@ -316,7 +309,7 @@ static void create_base_index(const struct commit *current_head) struct tree_desc t; if (!current_head) { - discard_index(&the_index); + discard_index(the_repository->index); return; } @@ -324,8 +317,8 @@ static void create_base_index(const struct commit *current_head) opts.head_idx = 1; opts.index_only = 1; opts.merge = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.fn = oneway_merge; tree = parse_tree_indirect(¤t_head->object.oid); @@ -344,7 +337,7 @@ static void refresh_cache_or_die(int refresh_flags) * refresh_flags contains REFRESH_QUIET, so the only errors * are for unmerged entries. */ - if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL)) + if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL)) die_resolve_conflict("commit"); } @@ -393,7 +386,7 @@ static const char *prepare_index(const char **argv, const char *prefix, refresh_cache_or_die(refresh_flags); - if (write_locked_index(&the_index, &index_lock, 0)) + if (write_locked_index(the_repository->index, &index_lock, 0)) die(_("unable to create temporary index")); old_repo_index_file = the_repository->index_file; @@ -412,13 +405,13 @@ static const char *prepare_index(const char **argv, const char *prefix, unsetenv(INDEX_ENVIRONMENT); FREE_AND_NULL(old_index_env); - discard_index(&the_index); - read_index_from(&the_index, get_lock_file_path(&index_lock), + discard_index(the_repository->index); + read_index_from(the_repository->index, get_lock_file_path(&index_lock), get_git_dir()); - if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) { + if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) { if (reopen_lock_file(&index_lock) < 0) die(_("unable to write index file")); - if (write_locked_index(&the_index, &index_lock, 0)) + if (write_locked_index(the_repository->index, &index_lock, 0)) die(_("unable to update temporary index")); } else warning(_("Failed to update main cache tree")); @@ -450,8 +443,8 @@ static const char *prepare_index(const char **argv, const char *prefix, exit(128); refresh_cache_or_die(refresh_flags); - cache_tree_update(&the_index, WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, 0)) + cache_tree_update(the_repository->index, WRITE_TREE_SILENT); + if (write_locked_index(the_repository->index, &index_lock, 0)) die(_("unable to write new index file")); commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); @@ -472,10 +465,10 @@ static const char *prepare_index(const char **argv, const char *prefix, repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); - if (the_index.cache_changed - || !cache_tree_fully_valid(the_index.cache_tree)) - cache_tree_update(&the_index, WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, + if (the_repository->index->cache_changed + || !cache_tree_fully_valid(the_repository->index->cache_tree)) + cache_tree_update(the_repository->index, WRITE_TREE_SILENT); + if (write_locked_index(the_repository->index, &index_lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write new index file")); commit_style = COMMIT_AS_IS; @@ -516,15 +509,15 @@ static const char *prepare_index(const char **argv, const char *prefix, if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec)) exit(1); - discard_index(&the_index); + discard_index(the_repository->index); if (repo_read_index(the_repository) < 0) die(_("cannot read the index")); repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR); add_remove_files(&partial); - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); - cache_tree_update(&the_index, WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, 0)) + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); + cache_tree_update(the_repository->index, WRITE_TREE_SILENT); + if (write_locked_index(the_repository->index, &index_lock, 0)) die(_("unable to write new index file")); hold_lock_file_for_update(&false_lock, @@ -534,14 +527,14 @@ static const char *prepare_index(const char **argv, const char *prefix, create_base_index(current_head); add_remove_files(&partial); - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); - if (write_locked_index(&the_index, &false_lock, 0)) + if (write_locked_index(the_repository->index, &false_lock, 0)) die(_("unable to write temporary index file")); - discard_index(&the_index); + discard_index(the_repository->index); ret = get_lock_file_path(&false_lock); - read_index_from(&the_index, ret, get_git_dir()); + read_index_from(the_repository->index, ret, get_git_dir()); out: string_list_clear(&partial, 0); clear_pathspec(&pathspec); @@ -999,7 +992,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct object_id oid; const char *parent = "HEAD"; - if (!the_index.initialized && repo_read_index(the_repository) < 0) + if (!the_repository->index->initialized && repo_read_index(the_repository) < 0) die(_("Cannot read index")); if (amend) @@ -1009,11 +1002,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, int i, ita_nr = 0; /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < the_index.cache_nr; i++) - if (ce_intent_to_add(the_index.cache[i])) + ensure_full_index(the_repository->index); + for (i = 0; i < the_repository->index->cache_nr; i++) + if (ce_intent_to_add(the_repository->index->cache[i])) ita_nr++; - committable = the_index.cache_nr - ita_nr > 0; + committable = the_repository->index->cache_nr - ita_nr > 0; } else { /* * Unless the user did explicitly request a submodule @@ -1038,14 +1031,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, fclose(s->fp); if (trailer_args.nr) { - struct child_process run_trailer = CHILD_PROCESS_INIT; - - strvec_pushl(&run_trailer.args, "interpret-trailers", - "--in-place", "--no-divider", - git_path_commit_editmsg(), NULL); - strvec_pushv(&run_trailer.args, trailer_args.v); - run_trailer.git_cmd = 1; - if (run_command(&run_trailer)) + if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args)) die(_("unable to pass trailers to --trailers")); strvec_clear(&trailer_args); } @@ -1081,11 +1067,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, * and could have updated it. We must do this before we invoke * the editor and after we invoke run_status above. */ - discard_index(&the_index); + discard_index(the_repository->index); } - read_index_from(&the_index, index_file, get_git_dir()); + read_index_from(the_repository->index, index_file, get_git_dir()); - if (cache_tree_update(&the_index, 0)) { + if (cache_tree_update(the_repository->index, 0)) { error(_("Error building trees")); return 0; } @@ -1324,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) @@ -1586,7 +1572,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) status_format != STATUS_FORMAT_PORCELAIN_V2) progress_flag = REFRESH_PROGRESS; repo_read_index(the_repository); - refresh_index(&the_index, + refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED|progress_flag, &s.pathspec, NULL, NULL); @@ -1673,7 +1659,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer), + OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG), OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), @@ -1856,13 +1842,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) append_merge_tag_headers(parents, &tail); } - if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid, + if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid, parents, &oid, author_ident.buf, NULL, sign_commit, extra)) { 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)) { @@ -1904,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-cache--daemon.c b/builtin/credential-cache--daemon.c index 17f929dede..4952b22547 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -115,7 +115,9 @@ static int read_request(FILE *fh, struct credential *c, return error("client sent bogus timeout line: %s", item.buf); *timeout = atoi(p); - if (credential_read(c, fh) < 0) + credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL); + + if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0) return -1; return 0; } @@ -131,8 +133,18 @@ static void serve_one_client(FILE *in, FILE *out) else if (!strcmp(action.buf, "get")) { struct credential_cache_entry *e = lookup_credential(&c); if (e) { - fprintf(out, "username=%s\n", e->item.username); - fprintf(out, "password=%s\n", e->item.password); + e->item.capa_authtype.request_initial = 1; + e->item.capa_authtype.request_helper = 1; + + fprintf(out, "capability[]=authtype\n"); + if (e->item.username) + fprintf(out, "username=%s\n", e->item.username); + if (e->item.password) + fprintf(out, "password=%s\n", e->item.password); + if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype) + fprintf(out, "authtype=%s\n", e->item.authtype); + if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential) + fprintf(out, "credential=%s\n", e->item.credential); if (e->item.password_expiry_utc != TIME_MAX) fprintf(out, "password_expiry_utc=%"PRItime"\n", e->item.password_expiry_utc); @@ -157,8 +169,10 @@ static void serve_one_client(FILE *in, FILE *out) else if (!strcmp(action.buf, "store")) { if (timeout < 0) warning("cache client didn't specify a timeout"); - else if (!c.username || !c.password) + else if ((!c.username || !c.password) && (!c.authtype && !c.credential)) warning("cache client gave us a partial credential"); + else if (c.ephemeral) + warning("not storing ephemeral credential"); else { remove_credential(&c, 0); cache_credential(&c, timeout); diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index bef120b537..3db8df70a9 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "credential.h" #include "gettext.h" #include "parse-options.h" #include "path.h" @@ -127,6 +128,13 @@ static char *get_socket_path(void) return socket; } +static void announce_capabilities(void) +{ + struct credential c = CREDENTIAL_INIT; + c.capa_authtype.request_initial = 1; + credential_announce_capabilities(&c, stdout); +} + int cmd_credential_cache(int argc, const char **argv, const char *prefix) { char *socket_path = NULL; @@ -163,6 +171,8 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) do_cache(socket_path, op, timeout, FLAG_RELAY); else if (!strcmp(op, "store")) do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN); + else if (!strcmp(op, "capability")) + announce_capabilities(); else ; /* ignore unknown operation */ diff --git a/builtin/credential-store.c b/builtin/credential-store.c index 4a492411bb..494c809332 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -205,7 +205,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix) if (!fns.nr) die("unable to set up default path; use --file"); - if (credential_read(&c, stdin) < 0) + if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0) die("unable to read credential"); if (!strcmp(op, "get")) diff --git a/builtin/credential.c b/builtin/credential.c index 7010752987..b72e76dd9a 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -17,18 +17,29 @@ int cmd_credential(int argc, const char **argv, const char *prefix UNUSED) usage(usage_msg); op = argv[1]; - if (credential_read(&c, stdin) < 0) + if (!strcmp(op, "capability")) { + credential_set_all_capabilities(&c, CREDENTIAL_OP_INITIAL); + credential_announce_capabilities(&c, stdout); + return 0; + } + + if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0) die("unable to read credential from stdin"); if (!strcmp(op, "fill")) { - credential_fill(&c); - credential_write(&c, stdout); + credential_fill(&c, 0); + credential_next_state(&c); + credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE); } else if (!strcmp(op, "approve")) { + credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER); credential_approve(&c); } else if (!strcmp(op, "reject")) { + credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER); credential_reject(&c); } else { usage(usage_msg); } + + credential_clear(&c); return 0; } diff --git a/builtin/describe.c b/builtin/describe.c index d6c77a714f..cf8edc4222 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "environment.h" @@ -54,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; @@ -201,7 +204,7 @@ static int get_name(const char *path, const struct object_id *oid, } /* Is it annotated? */ - if (!peel_iterated_oid(oid, &peeled)) { + if (!peel_iterated_oid(the_repository, oid, &peeled)) { is_annotated = !oideq(oid, &peeled); } else { oidcpy(&peeled, oid); @@ -638,13 +641,22 @@ int cmd_describe(int argc, const char **argv, const char *prefix) } hashmap_init(&names, commit_name_neq, NULL, 0); - for_each_rawref(get_name, NULL); + refs_for_each_rawref(get_main_ref_store(the_repository), get_name, + NULL); if (!hashmap_get_size(&names) && !always) die(_("No names found, cannot describe anything.")); 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; @@ -674,7 +686,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; repo_read_index(the_repository); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, + refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); fd = repo_hold_locked_index(the_repository, &index_lock, 0); 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-tree.c b/builtin/diff-tree.c index a8e68ce8ef..b8df1d4b79 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "diff.h" @@ -9,6 +8,7 @@ #include "read-cache-ll.h" #include "repository.h" #include "revision.h" +#include "tmp-objdir.h" #include "tree.h" static struct rev_info log_tree_opt; @@ -167,6 +167,13 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) opt->diffopt.rotate_to_strict = 1; + if (opt->remerge_diff) { + opt->remerge_objdir = tmp_objdir_create("remerge-diff"); + if (!opt->remerge_objdir) + die(_("unable to create temporary object directory")); + tmp_objdir_replace_primary_odb(opt->remerge_objdir, 1); + } + /* * NOTE! We expect "a..b" to expand to "^a b" but it is * perfectly valid for revision range parser to yield "b ^a", @@ -206,7 +213,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) opt->diffopt.rotate_to_strict = 0; opt->diffopt.no_free = 1; if (opt->diffopt.detect_rename) { - if (!the_index.cache) + if (the_repository->index->cache) repo_read_index(the_repository); opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE; } @@ -231,5 +238,10 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) diff_free(&opt->diffopt); } + if (opt->remerge_diff) { + tmp_objdir_destroy(opt->remerge_objdir); + opt->remerge_objdir = NULL; + } + return diff_result_code(&opt->diffopt); } diff --git a/builtin/diff.c b/builtin/diff.c index 6e196e0c7d..9b6cdabe15 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -3,7 +3,7 @@ * * Copyright (c) 2006 Junio C Hamano */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "config.h" #include "ewah/ewok.h" @@ -239,9 +239,9 @@ static void refresh_index_quietly(void) fd = repo_hold_locked_index(the_repository, &lock_file, 0); if (fd < 0) return; - discard_index(&the_index); + discard_index(the_repository->index); repo_read_index(the_repository); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, + refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); repo_update_index_if_able(the_repository, &lock_file); } @@ -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 a3c72b8258..dcc68e190c 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -11,7 +11,7 @@ * * Copyright (C) 2016 Johannes Schindelin */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "abspath.h" #include "config.h" @@ -117,7 +117,7 @@ static int use_wt_file(const char *workdir, const char *name, int fd = open(buf.buf, O_RDONLY); if (fd >= 0 && - !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) { + !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) { if (is_null_oid(oid)) { oidcpy(oid, &wt_oid); use = 1; @@ -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 dc5a9d32dd..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; } @@ -1604,11 +1607,12 @@ static int update_branch(struct branch *b) if (is_null_oid(&b->oid)) { if (b->delete) - delete_ref(NULL, b->name, NULL, 0); + refs_delete_ref(get_main_ref_store(the_repository), + NULL, b->name, NULL, 0); return 0; } - if (read_ref(b->name, &old_oid)) - oidclr(&old_oid); + if (refs_read_ref(get_main_ref_store(the_repository), b->name, &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; @@ -1631,10 +1635,11 @@ static int update_branch(struct branch *b) return -1; } } - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction || ref_transaction_update(transaction, b->name, &b->oid, &old_oid, - 0, msg, &err) || + NULL, NULL, 0, msg, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); error("%s", err.buf); @@ -1665,7 +1670,8 @@ static void dump_tags(void) struct strbuf err = STRBUF_INIT; struct ref_transaction *transaction; - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) { failure |= error("%s", err.buf); goto cleanup; @@ -1675,7 +1681,8 @@ static void dump_tags(void) strbuf_addf(&ref_name, "refs/tags/%s", t->name); if (ref_transaction_update(transaction, ref_name.buf, - &t->oid, NULL, 0, msg, &err)) { + &t->oid, NULL, NULL, NULL, + 0, msg, &err)) { failure |= error("%s", err.buf); goto cleanup; } @@ -2354,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; } @@ -2546,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; } @@ -2566,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; @@ -2890,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 5857d860db..693f02b958 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -340,7 +340,8 @@ static void find_non_local_tags(const struct ref *refs, refname_hash_init(&remote_refs); create_fetch_oidset(head, &fetch_oids); - for_each_ref(add_one_refname, &existing_refs); + refs_for_each_ref(get_main_ref_store(the_repository), add_one_refname, + &existing_refs); /* * If we already have a transaction, then we need to filter out all @@ -581,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; @@ -614,7 +620,9 @@ static struct ref *get_ref_map(struct remote *remote, if (!existing_refs_populated) { refname_hash_init(&existing_refs); - for_each_ref(add_one_refname, &existing_refs); + refs_for_each_ref(get_main_ref_store(the_repository), + add_one_refname, + &existing_refs); existing_refs_populated = 1; } @@ -659,7 +667,8 @@ static int s_update_ref(const char *action, * lifecycle. */ if (!transaction) { - transaction = our_transaction = ref_transaction_begin(&err); + transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) { ret = STORE_REF_ERROR_OTHER; goto out; @@ -668,7 +677,7 @@ static int s_update_ref(const char *action, ret = ref_transaction_update(transaction, ref->name, &ref->new_oid, check_old ? &ref->old_oid : NULL, - 0, msg, &err); + NULL, NULL, 0, msg, &err); if (ret) { ret = STORE_REF_ERROR_OTHER; goto out; @@ -1382,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; } @@ -1393,7 +1402,9 @@ static int prune_refs(struct display_state *display_state, for (ref = stale_refs; ref; ref = ref->next) string_list_append(&refnames, ref->name); - result = delete_refs("fetch: prune", &refnames, 0); + result = refs_delete_refs(get_main_ref_store(the_repository), + "fetch: prune", &refnames, + 0); string_list_clear(&refnames, 0); } } @@ -1406,7 +1417,8 @@ static int prune_refs(struct display_state *display_state, _("(none)"), ref->name, &ref->new_oid, &ref->old_oid, summary_width); - warn_dangling_symref(stderr, dangling_msg, ref->name); + refs_warn_dangling_symref(get_main_ref_store(the_repository), + stderr, dangling_msg, ref->name); } } @@ -1479,7 +1491,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options) continue; } old_nr = oids->nr; - for_each_glob_ref(add_oid, s, oids); + refs_for_each_glob_ref(get_main_ref_store(the_repository), + add_oid, s, oids); if (old_nr == oids->nr) warning("ignoring --negotiation-tip=%s because it does not match any refs", s); @@ -1655,7 +1668,8 @@ static int do_fetch(struct transport *transport, config->display_format); if (atomic_fetch) { - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) { retcode = -1; 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/for-each-ref.c b/builtin/for-each-ref.c index 919282e12a..5517a4a1c0 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -98,7 +98,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) } if (include_root_refs) - flags |= FILTER_REFS_ROOT_REFS; + flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD; filter.match_as_path = 1; filter_and_format_refs(&filter, flags, sorting, &format); diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c index 28186b30f5..c4fa41fda9 100644 --- a/builtin/for-each-repo.c +++ b/builtin/for-each-repo.c @@ -32,6 +32,7 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv) int cmd_for_each_repo(int argc, const char **argv, const char *prefix) { static const char *config_key = NULL; + int keep_going = 0; int i, result = 0; const struct string_list *values; int err; @@ -39,6 +40,8 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix) const struct option options[] = { OPT_STRING(0, "config", &config_key, N_("config"), N_("config key storing a list of repository paths")), + OPT_BOOL(0, "keep-going", &keep_going, + N_("keep going even if command fails in a repository")), OPT_END() }; @@ -55,8 +58,14 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix) else if (err) return 0; - for (i = 0; !result && i < values->nr; i++) - result = run_command_on_repo(values->items[i].string, argc, argv); + for (i = 0; i < values->nr; i++) { + int ret = run_command_on_repo(values->items[i].string, argc, argv); + if (ret) { + if (!keep_going) + return ret; + result = 1; + } + } return result; } diff --git a/builtin/fsck.c b/builtin/fsck.c index f892487c9b..d13a226c2e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -514,7 +514,9 @@ static int fsck_handle_reflog(const char *logname, void *cb_data) struct strbuf refname = STRBUF_INIT; strbuf_worktree_ref(cb_data, &refname, logname); - for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf); + refs_for_each_reflog_ent(get_main_ref_store(the_repository), + refname.buf, fsck_handle_reflog_ent, + refname.buf); strbuf_release(&refname); return 0; } @@ -563,7 +565,8 @@ static void get_default_heads(void) const char *head_points_at; struct object_id head_oid; - for_each_rawref(fsck_handle_ref, NULL); + refs_for_each_rawref(get_main_ref_store(the_repository), + fsck_handle_ref, NULL); worktrees = get_worktrees(); for (p = worktrees; *p; p++) { @@ -712,7 +715,9 @@ static int fsck_head_link(const char *head_ref_name, if (verbose) fprintf_ln(stderr, _("Checking %s link"), head_ref_name); - *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL); + *head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + head_ref_name, 0, head_oid, + NULL); if (!*head_points_at) { errors_found |= ERROR_REFS; return error(_("invalid %s"), head_ref_name); diff --git a/builtin/gc.c b/builtin/gc.c index d187cec1ea..72bac2554f 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -846,7 +846,7 @@ static int dfs_on_ref(const char *refname UNUSED, struct commit_list *stack = NULL; struct commit *commit; - if (!peel_iterated_oid(oid, &peeled)) + if (!peel_iterated_oid(the_repository, oid, &peeled)) oid = &peeled; if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) return 0; @@ -907,7 +907,8 @@ static int should_write_commit_graph(void) if (data.limit < 0) return 1; - result = for_each_ref(dfs_on_ref, &data); + result = refs_for_each_ref(get_main_ref_store(the_repository), + dfs_on_ref, &data); repo_clear_commit_marks(the_repository, SEEN); @@ -1870,6 +1871,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit "<string>%s/git</string>\n" "<string>--exec-path=%s</string>\n" "<string>for-each-repo</string>\n" + "<string>--keep-going</string>\n" "<string>--config=maintenance.repo</string>\n" "<string>maintenance</string>\n" "<string>run</string>\n" @@ -2112,7 +2114,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Actions Context=\"Author\">\n" "<Exec>\n" "<Command>\"%s\\headless-git.exe\"</Command>\n" - "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n" + "<Arguments>--exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n" "</Exec>\n" "</Actions>\n" "</Task>\n"; @@ -2257,7 +2259,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n", exec_path, exec_path); fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); @@ -2458,7 +2460,7 @@ static int systemd_timer_write_service_template(const char *exec_path) "\n" "[Service]\n" "Type=oneshot\n" - "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n" + "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n" "LockPersonality=yes\n" "MemoryDenyWriteExecute=yes\n" "NoNewPrivileges=yes\n" diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c index 66a7389f9f..7195a072ed 100644 --- a/builtin/get-tar-commit-id.c +++ b/builtin/get-tar-commit-id.c @@ -35,6 +35,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER) return 1; + errno = 0; len = strtol(content, &end, 10); if (errno == ERANGE || end == content || len < 0) return 1; 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..763b01372a 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; } @@ -1867,6 +1868,15 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (!index_name && pack_name) index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf); + /* + * Packfiles and indices do not carry enough information to be able to + * identify their object hash. So when we are neither in a repository + * nor has the user told us which object hash to use we have no other + * choice but to guess the object hash. + */ + if (!the_repository->hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY); if (rev_index) { opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV; diff --git a/builtin/init-db.c b/builtin/init-db.c index 0170469b84..582dcf20f8 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -81,7 +81,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *ref_format = NULL; const char *initial_branch = NULL; int hash_algo = GIT_HASH_UNKNOWN; - unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; + enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; int init_shared_repository = -1; const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"), 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 c0a8bb95e9..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; + cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); -static char *default_attach = NULL; + ret = cmd_log_walk(&rev); -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; - -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); - } - - 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,41 +1564,54 @@ 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; } -static int numbered_cmdline_opt = 0; +static int rfc_callback(const struct option *opt, const char *arg, + int unset) +{ + const char **rfc = opt->value; + + *rfc = opt->value; + if (unset) + *rfc = NULL; + else + *rfc = arg ? arg : "RFC"; + return 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; } @@ -1526,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. @@ -1568,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; } @@ -1598,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; } @@ -1619,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) { @@ -1627,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 { @@ -1639,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: @@ -1651,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); @@ -1826,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, @@ -1871,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; @@ -1895,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; @@ -1906,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; - int rfc = 0; + 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, @@ -1930,21 +2048,23 @@ 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_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")), + OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"), + N_("add <rfc> (default 'RFC') before 'PATCH'"), + PARSE_OPT_OPTARG, rfc_callback), OPT_STRING(0, "cover-from-description", &cover_from_description_arg, N_("cover-from-description-mode"), 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, @@ -1957,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"), @@ -1973,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, @@ -1998,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; @@ -2022,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; } @@ -2043,55 +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) - strbuf_insertstr(&sprefix, 0, "RFC "); + if (rfc && rfc[0]) { + cfg.subject_prefix = 1; + if (rfc[0] == '-') + strbuf_addf(&cfg.sprefix, " %s", rfc + 1); + else + 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) @@ -2102,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) @@ -2136,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; @@ -2157,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) @@ -2206,8 +2331,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (check_head) { const char *ref, *v; - ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - NULL, NULL); + ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + "HEAD", + RESOLVE_REF_READING, + NULL, NULL); if (ref && skip_prefix(ref, "refs/heads/", &v)) branch_name = xstrdup(v); else @@ -2253,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) { @@ -2274,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"); @@ -2292,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); } @@ -2323,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); @@ -2345,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) { /* @@ -2369,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); @@ -2402,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); @@ -2410,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); @@ -2422,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) @@ -2535,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..6da63a67f5 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")), @@ -88,6 +91,21 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) PARSE_OPT_STOP_AT_NON_OPTION); dest = argv[0]; + /* + * TODO: This is buggy, but required for transport helpers. When a + * transport helper advertises a "refspec", then we'd add that to a + * list of refspecs via `refspec_append()`, which transitively depends + * on `the_hash_algo`. Thus, when the hash algorithm isn't properly set + * up, this would lead to a segfault. + * + * We really should fix this in the transport helper logic such that we + * lazily parse refspec capabilities _after_ we have learned about the + * remote's object format. Otherwise, we may end up misparsing refspecs + * depending on what object hash the remote uses. + */ + if (!the_repository->hash_algo) + repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + packet_trace_identity("ls-remote"); if (argc > 1) { @@ -100,7 +118,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 +127,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 +146,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-index.c b/builtin/merge-index.c index 270d5f644a..0fabe3f6bb 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "hex.h" #include "read-cache-ll.h" @@ -18,11 +17,11 @@ static int merge_entry(int pos, const char *path) char ownbuf[4][60]; struct child_process cmd = CHILD_PROCESS_INIT; - if (pos >= the_index.cache_nr) + if (pos >= the_repository->index->cache_nr) die("git merge-index: %s not in the cache", path); found = 0; do { - const struct cache_entry *ce = the_index.cache[pos]; + const struct cache_entry *ce = the_repository->index->cache[pos]; int stage = ce_stage(ce); if (strcmp(ce->name, path)) @@ -32,7 +31,7 @@ static int merge_entry(int pos, const char *path) xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode); arguments[stage] = hexbuf[stage]; arguments[stage + 4] = ownbuf[stage]; - } while (++pos < the_index.cache_nr); + } while (++pos < the_repository->index->cache_nr); if (!found) die("git merge-index: %s not in the cache", path); @@ -51,7 +50,7 @@ static int merge_entry(int pos, const char *path) static void merge_one_path(const char *path) { - int pos = index_name_pos(&the_index, path, strlen(path)); + int pos = index_name_pos(the_repository->index, path, strlen(path)); /* * If it already exists in the cache as stage0, it's @@ -65,9 +64,9 @@ static void merge_all(void) { int i; /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < the_index.cache_nr; i++) { - const struct cache_entry *ce = the_index.cache[i]; + ensure_full_index(the_repository->index); + for (i = 0; i < the_repository->index->cache_nr; i++) { + const struct cache_entry *ce = the_repository->index->cache[i]; if (!ce_stage(ce)) continue; i += merge_entry(i, ce->name)-1; @@ -89,7 +88,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED) repo_read_index(the_repository); /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); + ensure_full_index(the_repository->index); i = 1; if (!strcmp(argv[i], "-o")) { 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 8bdb439131..dab2fdc2a6 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "tree-walk.h" #include "xdiff-interface.h" @@ -364,7 +363,7 @@ static void trivial_merge_trees(struct tree_desc t[3], const char *base) setup_traverse_info(&info, base); info.fn = threeway_callback; - traverse_trees(&the_index, 3, t, &info); + traverse_trees(the_repository->index, 3, t, &info); } static void *get_tree_descriptor(struct repository *r, @@ -483,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 6f4fec87fc..9fba27d85d 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -6,7 +6,6 @@ * Based on git-merge.sh by Junio C Hamano. */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "advice.h" @@ -101,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, @@ -111,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, @@ -165,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"); @@ -183,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; @@ -198,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); @@ -217,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; } @@ -300,7 +303,7 @@ static int save_state(struct object_id *stash) int rc = -1; fd = repo_hold_locked_index(the_repository, &lock_file, 0); - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); if (0 <= fd) repo_update_index_if_able(the_repository, &lock_file); rollback_lock_file(&lock_file); @@ -331,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; @@ -372,7 +376,7 @@ static void restore_state(const struct object_id *head, run_command(&cmd); refresh_cache: - discard_index(&the_index); + discard_index(the_repository->index); if (repo_read_index(the_repository) < 0) die(_("could not read index")); } @@ -449,8 +453,10 @@ static void finish(struct commit *head_commit, if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); else { - update_ref(reflog_message.buf, "HEAD", new_head, head, - 0, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + reflog_message.buf, "HEAD", new_head, + head, + 0, UPDATE_REFS_DIE_ON_ERR); /* * We ignore errors in 'gc --auto', since the * user should see them. @@ -493,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); @@ -547,7 +553,7 @@ static void merge_name(const char *remote, struct strbuf *msg) struct strbuf truname = STRBUF_INIT; strbuf_addf(&truname, "refs/heads/%s", remote); strbuf_setlen(&truname, truname.len - len); - if (ref_exists(truname.buf)) { + if (refs_ref_exists(get_main_ref_store(the_repository), truname.buf)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", oid_to_hex(&remote_head->object.oid), @@ -610,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; @@ -657,8 +665,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, memset(&opts, 0, sizeof(opts)); opts.head_idx = 2; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.update = 1; opts.verbose_update = 1; opts.trivial_merges_only = 1; @@ -674,7 +682,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, if (!trees[nr_trees++]) return -1; opts.fn = threeway_merge; - cache_tree_free(&the_index.cache_tree); + cache_tree_free(&the_repository->index->cache_tree); for (i = 0; i < nr_trees; i++) { parse_tree(trees[i]); init_tree_desc(t+i, &trees[i]->object.oid, @@ -687,7 +695,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, static void write_tree_trivial(struct object_id *oid) { - if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL)) + if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL)) die(_("git write-tree failed to write a tree")); } @@ -700,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")) { @@ -741,11 +749,13 @@ 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; } - if (write_locked_index(&the_index, &lock, + if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write %s"), get_index_file()); return clean ? 0 : 1; @@ -768,8 +778,8 @@ static int count_unmerged_entries(void) { int i, ret = 0; - for (i = 0; i < the_index.cache_nr; i++) - if (ce_stage(the_index.cache[i])) + for (i = 0; i < the_repository->index->cache_nr; i++) + if (ce_stage(the_repository->index->cache[i])) ret++; return ret; @@ -843,9 +853,9 @@ static void prepare_to_commit(struct commit_list *remoteheads) * the editor and after we invoke run_status above. */ if (invoked_hook) - discard_index(&the_index); + discard_index(the_repository->index); } - read_index_from(&the_index, index_file, get_git_dir()); + read_index_from(the_repository->index, index_file, get_git_dir()); strbuf_addbuf(&msg, &merge_msg); if (squash) BUG("the control must not reach here under --squash"); @@ -894,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, @@ -910,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; } @@ -936,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; } @@ -957,7 +971,7 @@ static int suggest_conflicts(void) * Thus, we will get the cleanup mode which is returned when we _are_ * using an editor. */ - append_conflicts_hint(&the_index, &msgbuf, + append_conflicts_hint(the_repository->index, &msgbuf, get_cleanup_mode(cleanup_arg, 1)); fputs(msgbuf.buf, fp); strbuf_release(&msgbuf); @@ -1252,7 +1266,7 @@ static int merging_a_throwaway_tag(struct commit *commit) */ tag_ref = xstrfmt("refs/tags/%s", ((struct tag *)merge_remote_util(commit)->obj)->tag); - if (!read_ref(tag_ref, &oid) && + if (!refs_read_ref(get_main_ref_store(the_repository), tag_ref, &oid) && oideq(&oid, &merge_remote_util(commit)->obj->oid)) is_throwaway_tag = 0; else @@ -1284,14 +1298,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ - branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL); + branch = branch_to_free = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", 0, &head_oid, + NULL); if (branch) skip_prefix(branch, "refs/heads/", &branch); 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(); @@ -1325,8 +1341,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!file_exists(git_path_merge_head(the_repository))) die(_("There is no merge to abort (MERGE_HEAD missing).")); - if (!read_ref("MERGE_AUTOSTASH", &stash_oid)) - delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF); + if (!refs_read_ref(get_main_ref_store(the_repository), "MERGE_AUTOSTASH", &stash_oid)) + refs_delete_ref(get_main_ref_store(the_repository), + "", "MERGE_AUTOSTASH", &stash_oid, + REF_NO_DEREF); /* Invoke 'git reset --merge' */ ret = cmd_reset(nargc, nargv, prefix); @@ -1379,14 +1397,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } - if (ref_exists("CHERRY_PICK_HEAD")) { + if (refs_ref_exists(get_main_ref_store(the_repository), "CHERRY_PICK_HEAD")) { if (advice_enabled(ADVICE_RESOLVE_CONFLICT)) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge.")); else die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } - resolve_undo_clear_index(&the_index); + resolve_undo_clear_index(the_repository->index); if (option_edit < 0) option_edit = default_edit_option(); @@ -1450,8 +1468,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) remote_head_oid = &remoteheads->item->object.oid; read_empty(remote_head_oid); - update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + "initial pull", "HEAD", remote_head_oid, NULL, + 0, + UPDATE_REFS_DIE_ON_ERR); goto done; } @@ -1531,8 +1551,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) free(list); } - update_ref("updating ORIG_HEAD", "ORIG_HEAD", - &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), + "updating ORIG_HEAD", "ORIG_HEAD", + &head_commit->object.oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); if (remoteheads && !common) { /* No common ancestors found. */ @@ -1595,7 +1617,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * We are not doing octopus, not fast-forward, and have * only one common. */ - refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); if (allow_trivial && fast_forward != FF_ONLY) { /* * Must first ensure that index matches HEAD before @@ -1681,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; @@ -1784,6 +1806,8 @@ done: } strbuf_release(&buf); free(branch_to_free); - discard_index(&the_index); + 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 a72aebecaa..9cf1a32d65 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -8,6 +8,7 @@ #include "strbuf.h" #include "trace2.h" #include "object-store-ll.h" +#include "replace-object.h" #define BUILTIN_MIDX_WRITE_USAGE \ N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \ @@ -49,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; @@ -134,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; @@ -156,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); @@ -165,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, @@ -273,6 +279,8 @@ int cmd_multi_pack_index(int argc, const char **argv, }; struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts); + disable_replace_refs(); + git_config(git_default_config, NULL); if (the_repository && diff --git a/builtin/mv.c b/builtin/mv.c index 22e64fc290..6c69033c5f 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006 Johannes Schindelin */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "abspath.h" #include "advice.h" @@ -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,46 +77,48 @@ 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; - if (!S_ISGITLINK(the_index.cache[first]->ce_mode)) + 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_index)) + 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_index, src_w_slash, len_w_slash); + first = index_name_pos(the_repository->index, src_w_slash, len_w_slash); if (first >= 0) die(_("%.*s is in index"), len_w_slash, src_w_slash); first = -1 - first; - for (last = first; last < the_index.cache_nr; last++) { - const char *path = the_index.cache[last]->name; + for (last = first; last < the_repository->index->cache_nr; last++) { + const char *path = the_repository->index->cache[last]->name; 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,17 +134,17 @@ 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_index, with_slash, length); + int pos = index_name_pos(the_repository->index, with_slash, length); const struct cache_entry *ce; if (pos < 0) { pos = -pos - 1; - if (pos >= the_index.cache_nr) + if (pos >= the_repository->index->cache_nr) goto free_return; - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; if (strncmp(with_slash, ce->name, length)) goto free_return; if (ce_skip_worktree(ce)) @@ -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_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_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; @@ -263,20 +274,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int pos; const struct cache_entry *ce; - pos = index_name_pos(&the_index, src, length); + pos = index_name_pos(the_repository->index, src, length); if (pos < 0) { - const char *src_w_slash = add_slash(src); - if (!path_in_sparse_checkout(src_w_slash, &the_index) && + 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"); goto act_on_entry; } - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; if (!ce_skip_worktree(ce)) { bad = _("bad source"); goto act_on_entry; @@ -286,7 +299,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) goto act_on_entry; } /* Check if dst exists in index */ - if (index_name_pos(&the_index, dst, strlen(dst)) < 0) { + if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) { modes[i] |= SPARSE; goto act_on_entry; } @@ -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; - int first = index_name_pos(&the_index, src, length), last; + 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,32 +343,35 @@ 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_index.cache[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; } - if (!(ce = index_file_exists(&the_index, src, length, 0))) { + if (!(ce = index_file_exists(the_repository->index, src, length, 0))) { bad = _("not under version control"); goto act_on_entry; } @@ -387,7 +407,7 @@ dir_check: if (ignore_sparse && (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && - index_entry_exists(&the_index, dst, strlen(dst))) { + index_entry_exists(the_repository->index, dst, strlen(dst))) { bad = _("destination exists in the index"); if (force) { if (verbose) @@ -404,12 +424,12 @@ dir_check: * option as a way to have a successful run. */ if (!ignore_sparse && - !path_in_sparse_checkout(src, &the_index)) { + !path_in_sparse_checkout(src, the_repository->index)) { string_list_append(&only_match_skip_worktree, src); skip_sparse = 1; } if (!ignore_sparse && - !path_in_sparse_checkout(dst, &the_index)) { + !path_in_sparse_checkout(dst, the_repository->index)) { string_list_append(&only_match_skip_worktree, dst); skip_sparse = 1; } @@ -428,28 +448,30 @@ 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; struct checkout state = CHECKOUT_INIT; - state.istate = &the_index; + state.istate = the_repository->index; if (force) state.force = 1; @@ -464,26 +486,26 @@ 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); } if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR)) continue; - pos = index_name_pos(&the_index, src, strlen(src)); + pos = index_name_pos(the_repository->index, src, strlen(src)); assert(pos >= 0); if (!(mode & SPARSE) && !lstat(src, &st)) - sparse_and_dirty = ie_modified(&the_index, - the_index.cache[pos], + sparse_and_dirty = ie_modified(the_repository->index, + the_repository->index->cache[pos], &st, 0); - rename_index_entry_at(&the_index, pos, dst); + rename_index_entry_at(the_repository->index, pos, dst); if (ignore_sparse && core_apply_sparse_checkout && @@ -495,11 +517,11 @@ remove_entry: * should be added in a future patch. */ if ((mode & SPARSE) && - path_in_sparse_checkout(dst, &the_index)) { + path_in_sparse_checkout(dst, the_repository->index)) { /* from out-of-cone to in-cone */ - int dst_pos = index_name_pos(&the_index, dst, + int dst_pos = index_name_pos(the_repository->index, dst, strlen(dst)); - struct cache_entry *dst_ce = the_index.cache[dst_pos]; + struct cache_entry *dst_ce = the_repository->index->cache[dst_pos]; dst_ce->ce_flags &= ~CE_SKIP_WORKTREE; @@ -507,11 +529,11 @@ remove_entry: die(_("cannot checkout %s"), dst_ce->name); } else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && !(mode & SPARSE) && - !path_in_sparse_checkout(dst, &the_index)) { + !path_in_sparse_checkout(dst, the_repository->index)) { /* from in-cone to out-of-cone */ - int dst_pos = index_name_pos(&the_index, dst, + int dst_pos = index_name_pos(the_repository->index, dst, strlen(dst)); - struct cache_entry *dst_ce = the_index.cache[dst_pos]; + struct cache_entry *dst_ce = the_repository->index->cache[dst_pos]; /* * if src is clean, it will suffice to remove it @@ -535,41 +557,31 @@ 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); if (gitmodules_modified) - stage_updated_gitmodules(&the_index); + stage_updated_gitmodules(the_repository->index); - if (write_locked_index(&the_index, &lock_file, + if (write_locked_index(the_repository->index, &lock_file, 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/name-rev.c b/builtin/name-rev.c index ad9930c831..70e9ec4e47 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -296,7 +296,8 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname, char *short_refname = NULL; if (shorten_unambiguous) - short_refname = shorten_unambiguous_ref(refname, 0); + short_refname = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), + refname, 0); else if (skip_prefix(refname, "refs/heads/", &refname)) ; /* refname already advanced */ else @@ -647,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) adjust_cutoff_timestamp_for_slop(); - for_each_ref(name_ref, &data); + refs_for_each_ref(get_main_ref_store(the_repository), name_ref, &data); name_tips(&string_pool); if (annotate_stdin) { diff --git a/builtin/notes.c b/builtin/notes.c index cb011303e6..4cc5bfedc3 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -114,7 +114,6 @@ struct note_msg { }; struct note_data { - int given; int use_editor; int stripspace; char *edit_path; @@ -193,7 +192,7 @@ static void write_commented_object(int fd, const struct object_id *object) static void prepare_note_data(const struct object_id *object, struct note_data *d, const struct object_id *old_note) { - if (d->use_editor || !d->given) { + if (d->use_editor || !d->msg_nr) { int fd; struct strbuf buf = STRBUF_INIT; @@ -201,7 +200,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * d->edit_path = git_pathdup("NOTES_EDITMSG"); fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (d->given) + if (d->msg_nr) write_or_die(fd, d->buf.buf, d->buf.len); else if (old_note) copy_obj_to_fd(fd, old_note); @@ -515,7 +514,6 @@ static int add(int argc, const char **argv, const char *prefix) if (d.msg_nr) concat_messages(&d); - d.given = !!d.buf.len; object_ref = argc > 1 ? argv[1] : "HEAD"; @@ -528,7 +526,7 @@ static int add(int argc, const char **argv, const char *prefix) if (note) { if (!force) { free_notes(t); - if (d.given) { + if (d.msg_nr) { free_note_data(&d); return error(_("Cannot add notes. " "Found existing notes for object %s. " @@ -690,14 +688,14 @@ static int append_edit(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); } - if (d.msg_nr) + if (d.msg_nr) { concat_messages(&d); - d.given = !!d.buf.len; - - if (d.given && edit) - fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " - "for the 'edit' subcommand.\n" - "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); + if (edit) + fprintf(stderr, _("The -m/-F/-c/-C options have been " + "deprecated for the 'edit' subcommand.\n" + "Please use 'git notes add -f -m/-F/-c/-C' " + "instead.\n")); + } object_ref = 1 < argc ? argv[1] : "HEAD"; @@ -794,9 +792,9 @@ static int merge_abort(struct notes_merge_options *o) * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE. */ - if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0)) + if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_PARTIAL", NULL, 0)) ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL")); - if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF)) + if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF)) ret += error(_("failed to delete ref NOTES_MERGE_REF")); if (notes_merge_abort(o)) ret += error(_("failed to remove 'git notes merge' worktree")); @@ -828,13 +826,14 @@ 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); o->local_ref = local_ref_to_free = - resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL); + refs_resolve_refdup(get_main_ref_store(the_repository), + "NOTES_MERGE_REF", 0, &oid, NULL); if (!o->local_ref) die(_("failed to resolve NOTES_MERGE_REF")); @@ -847,9 +846,10 @@ static int merge_commit(struct notes_merge_options *o) &pretty_ctx); strbuf_trim(&msg); strbuf_insertstr(&msg, 0, "notes: "); - update_ref(msg.buf, o->local_ref, &oid, - is_null_oid(&parent_oid) ? NULL : &parent_oid, - 0, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg.buf, + o->local_ref, &oid, + is_null_oid(&parent_oid) ? NULL : &parent_oid, + 0, UPDATE_REFS_DIE_ON_ERR); free_notes(t); strbuf_release(&msg); @@ -961,14 +961,16 @@ static int merge(int argc, const char **argv, const char *prefix) if (result >= 0) /* Merge resulted (trivially) in result_oid */ /* Update default notes ref with new commit */ - update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg.buf, + default_notes_ref(), &result_oid, NULL, 0, + UPDATE_REFS_DIE_ON_ERR); else { /* Merge has unresolved conflicts */ struct worktree **worktrees; const struct worktree *wt; /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ - update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL, - 0, UPDATE_REFS_DIE_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg.buf, + "NOTES_MERGE_PARTIAL", &result_oid, NULL, + 0, UPDATE_REFS_DIE_ON_ERR); /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ worktrees = get_worktrees(); wt = find_shared_symref(worktrees, "NOTES_MERGE_REF", @@ -977,7 +979,7 @@ static int merge(int argc, const char **argv, const char *prefix) die(_("a notes merge into %s is already in-progress at %s"), default_notes_ref(), wt->path); free_worktrees(worktrees); - if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL)) + if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL)) die(_("failed to store link to current notes ref (%s)"), default_notes_ref()); fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s " diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index baf0090fc8..f395488971 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -779,7 +779,7 @@ static int mark_tagged(const char *path UNUSED, const struct object_id *oid, if (entry) entry->tagged = 1; - if (!peel_iterated_oid(oid, &peeled)) { + if (!peel_iterated_oid(the_repository, oid, &peeled)) { entry = packlist_find(&to_pack, &peeled); if (entry) entry->tagged = 1; @@ -939,7 +939,8 @@ static struct object_entry **compute_write_order(void) /* * Mark objects that are at the tip of tags. */ - for_each_tag_ref(mark_tagged, NULL); + refs_for_each_tag_ref(get_main_ref_store(the_repository), mark_tagged, + NULL); if (use_delta_islands) { max_layers = compute_pack_layers(&to_pack); @@ -1314,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; /* @@ -1339,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); } @@ -1358,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); } @@ -2070,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; @@ -3124,7 +3134,7 @@ static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid, { struct object_id peeled; - if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled)) + if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled)) add_tag_chain(oid); return 0; } @@ -4073,7 +4083,7 @@ static int mark_bitmap_preferred_tip(const char *refname, struct object_id peeled; struct object *object; - if (!peel_iterated_oid(oid, &peeled)) + if (!peel_iterated_oid(the_repository, oid, &peeled)) oid = &peeled; object = parse_object_or_die(oid, refname); @@ -4093,7 +4103,9 @@ static void mark_bitmap_preferred_tips(void) return; for_each_string_list_item(item, preferred_tips) { - for_each_ref_in(item->string, mark_bitmap_preferred_tip, NULL); + refs_for_each_ref_in(get_main_ref_store(the_repository), + item->string, mark_bitmap_preferred_tip, + NULL); } } @@ -4588,7 +4600,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } cleanup_preferred_base(); if (include_tag && nr_result) - for_each_tag_ref(add_ref_tag, NULL); + refs_for_each_tag_ref(get_main_ref_store(the_repository), + add_ref_tag, NULL); stop_progress(&progress_state); trace2_region_leave("pack-objects", "enumerate-objects", the_repository); 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..35c1179f7e 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -5,11 +5,11 @@ #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) +static void flush_current_id(struct object_id *id, struct object_id *result) { - if (patchlen) - printf("%s %s\n", oid_to_hex(result), oid_to_hex(id)); + printf("%s %s\n", oid_to_hex(result), oid_to_hex(id)); } static int remove_space(char *line) @@ -59,9 +59,27 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after) return 1; } +/* + * flag bits to control get_one_patchid()'s behaviour. + * + * STABLE/VERBATIM are given from the command line option as + * --stable/--verbatim. FIND_HEADER conveys the internal state + * maintained by the caller to allow the function to avoid mistaking + * lines of log message before seeing the "diff" part as the beginning + * of the next patch. + */ +enum { + GOPID_STABLE = (1<<0), /* --stable */ + GOPID_VERBATIM = (1<<1), /* --verbatim */ + GOPID_FIND_HEADER = (1<<2), /* stop at the beginning of patch message */ +}; + static int get_one_patchid(struct object_id *next_oid, struct object_id *result, - struct strbuf *line_buf, int stable, int verbatim) + struct strbuf *line_buf, unsigned flags) { + int stable = flags & GOPID_STABLE; + int verbatim = flags & GOPID_VERBATIM; + int find_header = flags & GOPID_FIND_HEADER; int patchlen = 0, found_next = 0; int before = -1, after = -1; int diff_is_binary = 0; @@ -69,31 +87,47 @@ 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; const char *p = line; int len; - /* Possibly skip over the prefix added by "log" or "format-patch" */ - if (!skip_prefix(line, "commit ", &p) && - !skip_prefix(line, "From ", &p) && - starts_with(line, "\\ ") && 12 < strlen(line)) { - if (verbatim) - the_hash_algo->update_fn(&ctx, line, strlen(line)); - continue; - } - - if (!get_oid_hex(p, next_oid)) { - found_next = 1; - break; + /* + * The caller hasn't seen us find a patch header and + * return to it, or we have started processing patch + * and may encounter the beginning of the next patch. + */ + if (find_header) { + /* + * If we see a line that begins with "<object name>", + * "commit <object name>" or "From <object name>", it is + * the beginning of a patch. Return to the caller, as + * we are done with the one we have been processing. + */ + if (skip_prefix(line, "commit ", &p)) + ; + else if (skip_prefix(line, "From ", &p)) + ; + if (!get_oid_hex(p, next_oid)) { + if (verbatim) + the_hash_algo->update_fn(&ctx, line, strlen(line)); + found_next = 1; + break; + } } /* Ignore commit comments */ if (!patchlen && !starts_with(line, "diff ")) continue; + /* + * We are past the commit log message. Prepare to + * stop at the beginning of the next patch header. + */ + find_header = 1; + /* Parsing diff header? */ if (before == -1) { if (starts_with(line, "GIT binary patch") || @@ -126,6 +160,16 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, break; } + /* + * A hunk about an incomplete line may have this + * marker at the end, which should just be ignored. + */ + if (starts_with(line, "\\ ") && 12 < strlen(line)) { + if (verbatim) + the_hash_algo->update_fn(&ctx, line, strlen(line)); + continue; + } + if (diff_is_binary) { if (starts_with(line, "diff ")) { diff_is_binary = 0; @@ -165,24 +209,27 @@ 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); return patchlen; } -static void generate_id_list(int stable, int verbatim) +static void generate_id_list(unsigned flags) { struct object_id oid, n, result; int patchlen; struct strbuf line_buf = STRBUF_INIT; - oidclr(&oid); + oidclr(&oid, the_repository->hash_algo); + flags |= GOPID_FIND_HEADER; while (!feof(stdin)) { - patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim); - flush_current_id(patchlen, &oid, &result); + patchlen = get_one_patchid(&n, &result, &line_buf, flags); + if (patchlen) + flush_current_id(&oid, &result); oidcpy(&oid, &n); + flags &= ~GOPID_FIND_HEADER; } strbuf_release(&line_buf); } @@ -218,6 +265,7 @@ int cmd_patch_id(int argc, const char **argv, const char *prefix) /* if nothing is set, default to unstable */ struct patch_id_opts config = {0, 0}; int opts = 0; + unsigned flags = 0; struct option builtin_patch_id_options[] = { OPT_CMDMODE(0, "unstable", &opts, N_("use the unstable patch-id algorithm"), 1), @@ -237,7 +285,23 @@ 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); - generate_id_list(opts ? opts > 1 : config.stable, - opts ? opts == 3 : config.verbatim); + /* + * 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); + + if (opts ? opts > 1 : config.stable) + flags |= GOPID_STABLE; + if (opts ? opts == 3 : config.verbatim) + flags |= GOPID_VERBATIM; + generate_id_list(flags); + return 0; } diff --git a/builtin/pull.c b/builtin/pull.c index 72cbb76d52..4c54d8196f 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -5,7 +5,7 @@ * * Fetch one or more remote refs and merge it/them into the current HEAD. */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "advice.h" #include "config.h" @@ -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[] = { @@ -611,7 +611,7 @@ static int pull_into_void(const struct object_id *merge_head, merge_head, 0)) return 1; - if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR)) + if (refs_update_ref(get_main_ref_store(the_repository), "initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR)) return 1; return 0; @@ -1038,13 +1038,13 @@ 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) opt_autostash = config_autostash; - if (is_null_oid(&orig_head) && !is_index_unborn(&the_index)) + if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index)) die(_("Updating an unborn branch with changes added to the index.")); if (!opt_autostash) @@ -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/read-tree.c b/builtin/read-tree.c index 6f89cec0fb..a8cf8504b8 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -4,7 +4,6 @@ * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "gettext.h" @@ -159,8 +158,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; git_config(git_read_tree_config, NULL); @@ -197,7 +196,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) die(_("You need to resolve your current index first")); stage = opts.merge = 1; } - resolve_undo_clear_index(&the_index); + resolve_undo_clear_index(the_repository->index); for (i = 0; i < argc; i++) { const char *arg = argv[i]; @@ -225,7 +224,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) setup_work_tree(); if (opts.skip_sparse_checkout) - ensure_full_index(&the_index); + ensure_full_index(the_repository->index); if (opts.merge) { switch (stage - 1) { @@ -237,7 +236,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) break; case 2: opts.fn = twoway_merge; - opts.initial_checkout = is_index_unborn(&the_index); + opts.initial_checkout = is_index_unborn(the_repository->index); break; case 3: default: @@ -258,7 +257,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) if (nr_trees == 1 && !opts.prefix) opts.skip_cache_tree_update = 1; - cache_tree_free(&the_index.cache_tree); + cache_tree_free(&the_repository->index->cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; if (parse_tree(tree) < 0) @@ -282,7 +281,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) the_repository->index, trees[0]); - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die("unable to write new index file"); return 0; } diff --git a/builtin/rebase.c b/builtin/rebase.c index 891f28468e..e3a8e74cfc 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -4,7 +4,6 @@ * Copyright (c) 2018 Pratik Karki */ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "environment.h" @@ -84,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; @@ -136,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, \ @@ -152,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; @@ -194,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, @@ -205,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); @@ -252,7 +265,7 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir())) return error_errno(_("could not create temporary %s"), merge_dir()); - delete_reflog("REBASE_HEAD"); + refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD"); interactive = fopen(path_interactive(), "w"); if (!interactive) @@ -295,9 +308,9 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) if (ret) error(_("could not generate todo list")); else { - discard_index(&the_index); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) + discard_index(the_repository->index); + 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, @@ -352,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; @@ -514,8 +531,10 @@ static int finish_rebase(struct rebase_options *opts) struct strbuf dir = STRBUF_INIT; int ret = 0; - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + "REBASE_HEAD", NULL, REF_NO_DEREF); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + "AUTO_MERGE", NULL, REF_NO_DEREF); apply_autostash(state_dir_path("autostash", opts)); /* * We ignore errors in 'git maintenance run --auto', since the @@ -795,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); } @@ -1046,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; @@ -1160,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"), @@ -1469,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"); @@ -1623,7 +1643,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* Is it a local branch? */ strbuf_reset(&buf); strbuf_addf(&buf, "refs/heads/%s", branch_name); - if (!read_ref(buf.buf, &branch_oid)) { + if (!refs_read_ref(get_main_ref_store(the_repository), buf.buf, &branch_oid)) { die_if_checked_out(buf.buf, 1); options.head_name = xstrdup(buf.buf); options.orig_head = @@ -1640,8 +1660,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } else if (argc == 0) { /* Do not need to switch branches, we are already on it. */ options.head_name = - xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, - &flags)); + xstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, + &flags)); if (!options.head_name) die(_("No such ref: %s"), "HEAD"); if (flags & REF_ISSYMREF) { @@ -1735,7 +1755,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag)) puts(_("HEAD is up to date.")); else printf(_("Current branch %s is up to date.\n"), @@ -1745,7 +1765,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } else if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag)) puts(_("HEAD is up to date, rebase forced.")); else printf(_("Current branch %s is up to date, rebase " @@ -1832,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 e8d7df14b6..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; @@ -1566,7 +1566,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) struct strbuf err = STRBUF_INIT; if (!parse_object(the_repository, old_oid)) { old_oid = NULL; - if (ref_exists(name)) { + if (refs_ref_exists(get_main_ref_store(the_repository), name)) { rp_warning("allowing deletion of corrupt ref"); } else { rp_warning("deleting a non-existent ref"); @@ -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 { @@ -1595,6 +1596,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (ref_transaction_update(transaction, namespaced_name, new_oid, old_oid, + NULL, NULL, 0, "push", &err)) { rp_error("%s", err.buf); @@ -1693,7 +1695,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) int flag; strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name); - dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag); + dst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + buf.buf, 0, NULL, &flag); check_aliased_update_internal(cmd, list, dst_name, flag); strbuf_release(&buf); } @@ -1829,7 +1832,8 @@ static void execute_commands_non_atomic(struct command *commands, if (!should_process_cmd(cmd) || cmd->run_proc_receive) continue; - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) { rp_error("%s", err.buf); strbuf_reset(&err); @@ -1857,7 +1861,8 @@ static void execute_commands_atomic(struct command *commands, struct strbuf err = STRBUF_INIT; const char *reported_error = "atomic push failure"; - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) { rp_error("%s", err.buf); strbuf_reset(&err); @@ -1983,7 +1988,9 @@ static void execute_commands(struct command *commands, check_aliased_updates(commands); free(head_name_to_free); - head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL); + head_name = head_name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", 0, NULL, + NULL); if (run_proc_receive && run_proc_receive_hook(commands, push_options)) diff --git a/builtin/reflog.c b/builtin/reflog.c index 060eb3377e..0d2ff95c6e 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -364,11 +364,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) }; set_reflog_expiry_param(&cb.cmd, item->string); - status |= reflog_expire(item->string, flags, - reflog_expiry_prepare, - should_prune_fn, - reflog_expiry_cleanup, - &cb); + status |= refs_reflog_expire(get_main_ref_store(the_repository), + item->string, flags, + reflog_expiry_prepare, + should_prune_fn, + reflog_expiry_cleanup, + &cb); } string_list_clear(&collected.reflogs, 0); } @@ -377,16 +378,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) char *ref; struct expire_reflog_policy_cb cb = { .cmd = cmd }; - if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) { + if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) { status |= error(_("%s points nowhere!"), argv[i]); continue; } set_reflog_expiry_param(&cb.cmd, ref); - status |= reflog_expire(ref, flags, - reflog_expiry_prepare, - should_prune_fn, - reflog_expiry_cleanup, - &cb); + status |= refs_reflog_expire(get_main_ref_store(the_repository), + ref, flags, + reflog_expiry_prepare, + should_prune_fn, + reflog_expiry_cleanup, + &cb); free(ref); } return status; @@ -437,7 +439,8 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) refname = argv[0]; if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) die(_("invalid ref format: %s"), refname); - return !reflog_exists(refname); + return !refs_reflog_exists(get_main_ref_store(the_repository), + refname); } /* diff --git a/builtin/refs.c b/builtin/refs.c new file mode 100644 index 0000000000..46dcd150d4 --- /dev/null +++ b/builtin/refs.c @@ -0,0 +1,75 @@ +#include "builtin.h" +#include "parse-options.h" +#include "refs.h" +#include "repository.h" +#include "strbuf.h" + +#define REFS_MIGRATE_USAGE \ + N_("git refs migrate --ref-format=<format> [--dry-run]") + +static int cmd_refs_migrate(int argc, const char **argv, const char *prefix) +{ + const char * const migrate_usage[] = { + REFS_MIGRATE_USAGE, + NULL, + }; + const char *format_str = NULL; + enum ref_storage_format format; + unsigned int flags = 0; + struct option options[] = { + OPT_STRING_F(0, "ref-format", &format_str, N_("format"), + N_("specify the reference format to convert to"), + PARSE_OPT_NONEG), + OPT_BIT(0, "dry-run", &flags, + N_("perform a non-destructive dry-run"), + REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN), + OPT_END(), + }; + struct strbuf errbuf = STRBUF_INIT; + int err; + + argc = parse_options(argc, argv, prefix, options, migrate_usage, 0); + if (argc) + usage(_("too many arguments")); + if (!format_str) + usage(_("missing --ref-format=<format>")); + + format = ref_storage_format_by_name(format_str); + if (format == REF_STORAGE_FORMAT_UNKNOWN) { + err = error(_("unknown ref storage format '%s'"), format_str); + goto out; + } + + if (the_repository->ref_storage_format == format) { + err = error(_("repository already uses '%s' format"), + ref_storage_format_to_name(format)); + goto out; + } + + if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) { + err = error("%s", errbuf.buf); + goto out; + } + + err = 0; + +out: + strbuf_release(&errbuf); + return err; +} + +int cmd_refs(int argc, const char **argv, const char *prefix) +{ + const char * const refs_usage[] = { + REFS_MIGRATE_USAGE, + NULL, + }; + parse_opt_subcommand_fn *fn = NULL; + struct option opts[] = { + OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate), + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, opts, refs_usage, 0); + return fn(argc, argv, prefix); +} diff --git a/builtin/remote.c b/builtin/remote.c index 8412d12fa5..08292498bd 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -240,7 +240,7 @@ static int add(int argc, const char **argv, const char *prefix) strbuf_reset(&buf2); strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master); - if (create_symref(buf.buf, buf2.buf, "remote add")) + if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add")) return error(_("Could not setup master '%s'"), master); } @@ -376,7 +376,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat for (ref = fetch_map; ref; ref = ref->next) { if (omit_name_by_refspec(ref->name, &states->remote->fetch)) string_list_append(&states->skipped, abbrev_branch(ref->name)); - else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name)) + else if (!ref->peer_ref || !refs_ref_exists(get_main_ref_store(the_repository), ref->peer_ref->name)) string_list_append(&states->new_refs, abbrev_branch(ref->name)); else string_list_append(&states->tracked, abbrev_branch(ref->name)); @@ -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; } @@ -598,8 +598,9 @@ static int read_remote_branches(const char *refname, strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); if (starts_with(refname, buf.buf)) { item = string_list_append(rename->remote_branches, refname); - symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING, - NULL, &flag); + symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + refname, RESOLVE_REF_READING, + NULL, &flag); if (symref && (flag & REF_ISSYMREF)) { item->util = xstrdup(symref); rename->symrefs_nr++; @@ -618,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++) @@ -789,7 +790,8 @@ static int mv(int argc, const char **argv, const char *prefix) * First remove symrefs, then rename the rest, finally create * the new symrefs. */ - for_each_ref(read_remote_branches, &rename); + refs_for_each_ref(get_main_ref_store(the_repository), + read_remote_branches, &rename); if (show_progress) { /* * Count symrefs twice, since "renaming" them is done by @@ -805,7 +807,7 @@ static int mv(int argc, const char **argv, const char *prefix) if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string, &referent)) continue; - if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF)) + if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF)) die(_("deleting '%s' failed"), item->string); strbuf_release(&referent); @@ -823,7 +825,7 @@ static int mv(int argc, const char **argv, const char *prefix) strbuf_reset(&buf2); strbuf_addf(&buf2, "remote: renamed %s to %s", item->string, buf.buf); - if (rename_ref(item->string, buf.buf, buf2.buf)) + if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf)) die(_("renaming '%s' failed"), item->string); display_progress(progress, ++refs_renamed_nr); } @@ -843,7 +845,7 @@ static int mv(int argc, const char **argv, const char *prefix) strbuf_reset(&buf3); strbuf_addf(&buf3, "remote: renamed %s to %s", item->string, buf.buf); - if (create_symref(buf.buf, buf2.buf, buf3.buf)) + if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf)) die(_("creating '%s' failed"), buf.buf); display_progress(progress, ++refs_renamed_nr); } @@ -917,11 +919,14 @@ static int rm(int argc, const char **argv, const char *prefix) * refs, which are invalidated when deleting a branch. */ cb_data.remote = remote; - result = for_each_ref(add_branch_for_removal, &cb_data); + result = refs_for_each_ref(get_main_ref_store(the_repository), + add_branch_for_removal, &cb_data); strbuf_release(&buf); if (!result) - result = delete_refs("remote: remove", &branches, REF_NO_DEREF); + result = refs_delete_refs(get_main_ref_store(the_repository), + "remote: remove", &branches, + REF_NO_DEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@ -997,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; @@ -1010,7 +1014,8 @@ static int get_remote_ref_states(const char *name, get_push_ref_states(remote_refs, states); transport_disconnect(transport); } else { - for_each_ref(append_ref_to_tracked_list, states); + refs_for_each_ref(get_main_ref_store(the_repository), + append_ref_to_tracked_list, states); string_list_sort(&states->tracked); get_push_ref_states_noquery(states); } @@ -1207,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); @@ -1224,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); } @@ -1289,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) @@ -1407,7 +1398,7 @@ static int set_head(int argc, const char **argv, const char *prefix) head_name = xstrdup(states.heads.items[0].string); free_remote_ref_states(&states); } else if (opt_d && !opt_a && argc == 1) { - if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF)) + if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF)) result |= error(_("Could not delete %s"), buf.buf); } else usage_with_options(builtin_remote_sethead_usage, options); @@ -1415,9 +1406,9 @@ static int set_head(int argc, const char **argv, const char *prefix) if (head_name) { strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); /* make sure it's valid */ - if (!ref_exists(buf2.buf)) + if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf)) result |= error(_("Not a valid ref: %s"), buf2.buf); - else if (create_symref(buf.buf, buf2.buf, "remote set-head")) + else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head")) result |= error(_("Could not setup %s"), buf.buf); else if (opt_a) printf("%s/HEAD set to %s\n", argv[0], head_name); @@ -1447,17 +1438,15 @@ 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); string_list_sort(&refs_to_prune); if (!dry_run) - result |= delete_refs("remote: prune", &refs_to_prune, 0); + result |= refs_delete_refs(get_main_ref_store(the_repository), + "remote: prune", &refs_to_prune, 0); for_each_string_list_item(item, &states.stale) { const char *refname = item->util; @@ -1470,7 +1459,8 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune); + refs_warn_dangling_symrefs(get_main_ref_store(the_repository), + stdout, dangling_msg, &refs_to_prune); string_list_clear(&refs_to_prune, 0); free_remote_ref_states(&states); @@ -1614,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")), @@ -1637,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; @@ -1672,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, @@ -1710,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. */ @@ -1732,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 15e4cccc45..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; @@ -673,7 +673,7 @@ static int midx_snapshot_ref_one(const char *refname UNUSED, struct midx_snapshot_ref_data *data = _data; struct object_id peeled; - if (!peel_iterated_oid(oid, &peeled)) + if (!peel_iterated_oid(the_repository, oid, &peeled)) oid = &peeled; if (oidset_insert(&data->seen, oid)) @@ -706,11 +706,14 @@ static void midx_snapshot_refs(struct tempfile *f) data.preferred = 1; for_each_string_list_item(item, preferred) - for_each_ref_in(item->string, midx_snapshot_ref_one, &data); + refs_for_each_ref_in(get_main_ref_store(the_repository), + item->string, + midx_snapshot_ref_one, &data); data.preferred = 0; } - for_each_ref(midx_snapshot_ref_one, &data); + refs_for_each_ref(get_main_ref_store(the_repository), + midx_snapshot_ref_one, &data); if (close_tempfile_gently(f)) { int save_errno = errno; diff --git a/builtin/replace.c b/builtin/replace.c index da59600ad2..1ef833c07f 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -43,11 +43,12 @@ enum replace_format { }; struct show_data { + struct repository *repo; const char *pattern; enum replace_format format; }; -static int show_reference(struct repository *r, const char *refname, +static int show_reference(const char *refname, const struct object_id *oid, int flag UNUSED, void *cb_data) { @@ -62,11 +63,11 @@ static int show_reference(struct repository *r, const char *refname, struct object_id object; enum object_type obj_type, repl_type; - if (repo_get_oid(r, refname, &object)) + if (repo_get_oid(data->repo, refname, &object)) return error(_("failed to resolve '%s' as a valid ref"), refname); - obj_type = oid_object_info(r, &object, NULL); - repl_type = oid_object_info(r, oid, NULL); + obj_type = oid_object_info(data->repo, &object, NULL); + repl_type = oid_object_info(data->repo, oid, NULL); printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type), oid_to_hex(oid), type_name(repl_type)); @@ -80,6 +81,7 @@ static int list_replace_refs(const char *pattern, const char *format) { struct show_data data; + data.repo = the_repository; if (!pattern) pattern = "*"; data.pattern = pattern; @@ -99,7 +101,8 @@ static int list_replace_refs(const char *pattern, const char *format) "valid formats are 'short', 'medium' and 'long'"), format); - for_each_replace_ref(the_repository, show_reference, (void *)&data); + refs_for_each_replace_ref(get_main_ref_store(the_repository), + show_reference, (void *)&data); return 0; } @@ -130,7 +133,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) strbuf_addstr(&ref, oid_to_hex(&oid)); full_hex = ref.buf + base_len; - if (read_ref(ref.buf, &oid)) { + if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) { error(_("replace ref '%s' not found"), full_hex); had_error = 1; continue; @@ -145,7 +148,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) static int delete_replace_ref(const char *name, const char *ref, const struct object_id *oid) { - if (delete_ref(NULL, ref, oid, 0)) + if (refs_delete_ref(get_main_ref_store(the_repository), NULL, ref, oid, 0)) return 1; printf_ln(_("Deleted replace ref '%s'"), name); return 0; @@ -163,8 +166,8 @@ static int check_ref_valid(struct object_id *object, if (check_refname_format(ref->buf, 0)) return error(_("'%s' is not a valid ref name"), ref->buf); - if (read_ref(ref->buf, prev)) - oidclr(prev); + if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev)) + oidclr(prev, the_repository->hash_algo); else if (!force) return error(_("replace ref '%s' already exists"), ref->buf); return 0; @@ -198,10 +201,11 @@ static int replace_object_oid(const char *object_ref, return -1; } - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction || ref_transaction_update(transaction, ref.buf, repl, &prev, - 0, NULL, &err) || + NULL, NULL, 0, NULL, &err) || ref_transaction_commit(transaction, &err)) res = error("%s", err.buf); diff --git a/builtin/replay.c b/builtin/replay.c index 6bc4b47f09..0448326636 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -2,7 +2,6 @@ * "git replay" builtin command */ -#define USE_THE_INDEX_VARIABLE #include "git-compat-util.h" #include "builtin.h" @@ -53,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, @@ -74,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/reset.c b/builtin/reset.c index 1d62ff6332..5f941fb3a2 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -7,7 +7,7 @@ * * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "advice.h" #include "config.h" @@ -66,8 +66,8 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.fn = oneway_merge; opts.merge = 1; init_checkout_metadata(&opts.meta, ref, oid, NULL); @@ -159,11 +159,11 @@ static void update_index_from_diff(struct diff_queue_struct *q, struct cache_entry *ce; if (!is_in_reset_tree && !intent_to_add) { - remove_file_from_index(&the_index, one->path); + remove_file_from_index(the_repository->index, one->path); continue; } - ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path, + ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path, 0, 0); /* @@ -174,9 +174,9 @@ static void update_index_from_diff(struct diff_queue_struct *q, * if this entry is outside the sparse cone - this is necessary * to properly construct the reset sparse directory. */ - pos = index_name_pos(&the_index, one->path, strlen(one->path)); - if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) || - (pos < 0 && !path_in_sparse_checkout(one->path, &the_index))) + pos = index_name_pos(the_repository->index, one->path, strlen(one->path)); + if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) || + (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index))) ce->ce_flags |= CE_SKIP_WORKTREE; if (!ce) @@ -186,7 +186,7 @@ static void update_index_from_diff(struct diff_queue_struct *q, ce->ce_flags |= CE_INTENT_TO_ADD; set_object_name_for_intent_to_add_entry(ce); } - add_index_entry(&the_index, ce, + add_index_entry(the_repository->index, ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } } @@ -208,8 +208,8 @@ static int read_from_tree(const struct pathspec *pathspec, opt.change = diff_change; opt.add_remove = diff_addremove; - if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec)) - ensure_full_index(&the_index); + if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec)) + ensure_full_index(the_repository->index); if (do_diff_cache(tree_oid, &opt)) return 1; @@ -235,7 +235,7 @@ static void set_reflog_message(struct strbuf *sb, const char *action, static void die_if_unmerged_cache(int reset_type) { - if (is_merge() || unmerged_index(&the_index)) + if (is_merge() || unmerged_index(the_repository->index)) die(_("Cannot do a %s reset in the middle of a merge."), _(reset_type_names[reset_type])); @@ -307,13 +307,16 @@ static int reset_refs(const char *rev, const struct object_id *oid) if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) { orig = &oid_orig; set_reflog_message(&msg, "updating ORIG_HEAD", NULL); - update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, - UPDATE_REFS_MSG_ON_ERR); + refs_update_ref(get_main_ref_store(the_repository), msg.buf, + "ORIG_HEAD", orig, old_orig, 0, + UPDATE_REFS_MSG_ON_ERR); } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); + refs_delete_ref(get_main_ref_store(the_repository), NULL, + "ORIG_HEAD", old_orig, 0); set_reflog_message(&msg, "updating HEAD", rev); - update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0, - UPDATE_REFS_MSG_ON_ERR); + update_ref_status = refs_update_ref(get_main_ref_store(the_repository), + msg.buf, "HEAD", oid, orig, 0, + UPDATE_REFS_MSG_ON_ERR); strbuf_release(&msg); return update_ref_status; } @@ -470,12 +473,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix) update_ref_status = 1; goto cleanup; } - the_index.updated_skipworktree = 1; + the_repository->index->updated_skipworktree = 1; if (!no_refresh && get_git_work_tree()) { uint64_t t_begin, t_delta_in_ms; t_begin = getnanotime(); - refresh_index(&the_index, flags, NULL, NULL, + refresh_index(the_repository->index, flags, NULL, NULL, _("Unstaged changes after reset:")); t_delta_in_ms = (getnanotime() - t_begin) / 1000000; if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) { @@ -501,7 +504,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) free(ref); } - if (write_locked_index(&the_index, &lock, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK)) die(_("Could not write new index file.")); } @@ -516,7 +519,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (!pathspec.nr) remove_branch_state(the_repository, 0); - discard_index(&the_index); + discard_index(the_repository->index); cleanup: clear_pathspec(&pathspec); 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 624182e507..2e64f5bda7 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -3,7 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "abspath.h" #include "config.h" @@ -160,8 +160,9 @@ static void show_rev(int type, const struct object_id *oid, const char *name) case 1: /* happy */ if (abbrev_ref) { char *old = full; - full = shorten_unambiguous_ref(full, - abbrev_ref_strict); + full = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), + full, + abbrev_ref_strict); free(old); } show_with_type(type, full); @@ -422,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, @@ -437,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, @@ -452,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> */ @@ -473,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; @@ -493,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 */ @@ -536,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); @@ -546,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; } @@ -599,9 +607,12 @@ static int opt_with_value(const char *arg, const char *opt, const char **value) static void handle_ref_opt(const char *pattern, const char *prefix) { if (pattern) - for_each_glob_ref_in(show_reference, pattern, prefix, NULL); + refs_for_each_glob_ref_in(get_main_ref_store(the_repository), + show_reference, pattern, prefix, + NULL); else - for_each_ref_in(prefix, show_reference, NULL); + refs_for_each_ref_in(get_main_ref_store(the_repository), + prefix, show_reference, NULL); clear_ref_exclusions(&ref_excludes); } @@ -687,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; @@ -863,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")) { @@ -898,7 +908,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--all")) { - for_each_ref(show_reference, NULL); + refs_for_each_ref(get_main_ref_store(the_repository), + show_reference, NULL); clear_ref_exclusions(&ref_excludes); continue; } @@ -908,8 +919,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--bisect")) { - for_each_fullref_in("refs/bisect/bad", show_reference, NULL); - for_each_fullref_in("refs/bisect/good", anti_reference, NULL); + refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/bisect/bad", + NULL, show_reference, + NULL); + refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/bisect/good", + NULL, anti_reference, + NULL); continue; } if (opt_with_value(arg, "--branches", &arg)) { @@ -1049,8 +1066,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--shared-index-path")) { if (repo_read_index(the_repository) < 0) die(_("Could not read the index")); - if (the_index.split_index) { - const struct object_id *oid = &the_index.split_index->base_oid; + if (the_repository->index->split_index) { + const struct object_id *oid = &the_repository->index->split_index->base_oid; const char *path = git_path("sharedindex.%s", oid_to_hex(oid)); print_path(path, prefix, format, DEFAULT_RELATIVE); } @@ -1111,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); @@ -1120,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 fd130cea2d..0e79cbab62 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -3,7 +3,7 @@ * * Copyright (C) Linus Torvalds 2006 */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "advice.h" #include "config.h" @@ -41,8 +41,8 @@ static int get_ours_cache_pos(const char *path, int pos) { int i = -pos - 1; - while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) { - if (ce_stage(the_index.cache[i]) == 2) + while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) { + if (ce_stage(the_repository->index->cache[i]) == 2) return i; i++; } @@ -78,13 +78,13 @@ static void submodules_absorb_gitdir_if_needed(void) int pos; const struct cache_entry *ce; - pos = index_name_pos(&the_index, name, strlen(name)); + pos = index_name_pos(the_repository->index, name, strlen(name)); if (pos < 0) { pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; } - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; if (!S_ISGITLINK(ce->ce_mode) || !file_exists(ce->name) || @@ -122,7 +122,7 @@ static int check_local_mod(struct object_id *head, int index_only) int local_changes = 0; int staged_changes = 0; - pos = index_name_pos(&the_index, name, strlen(name)); + pos = index_name_pos(the_repository->index, name, strlen(name)); if (pos < 0) { /* * Skip unmerged entries except for populated submodules @@ -132,11 +132,11 @@ static int check_local_mod(struct object_id *head, int index_only) if (pos < 0) continue; - if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) || + if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) || is_empty_dir(name)) continue; } - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; if (lstat(ce->name, &st) < 0) { if (!is_missing_file_error(errno)) @@ -173,7 +173,7 @@ static int check_local_mod(struct object_id *head, int index_only) * Is the index different from the file in the work tree? * If it's a submodule, is its work tree modified? */ - if (ie_match_stat(&the_index, ce, &st, 0) || + if (ie_match_stat(the_repository->index, ce, &st, 0) || (S_ISGITLINK(ce->ce_mode) && bad_to_remove_submodule(ce->name, SUBMODULE_REMOVAL_DIE_ON_ERROR | @@ -301,27 +301,27 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL); + refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL); seen = xcalloc(pathspec.nr, 1); - if (pathspec_needs_expanded_index(&the_index, &pathspec)) - ensure_full_index(&the_index); + if (pathspec_needs_expanded_index(the_repository->index, &pathspec)) + ensure_full_index(the_repository->index); - for (i = 0; i < the_index.cache_nr; i++) { - const struct cache_entry *ce = the_index.cache[i]; + for (i = 0; i < the_repository->index->cache_nr; i++) { + const struct cache_entry *ce = the_repository->index->cache[i]; if (!include_sparse && (ce_skip_worktree(ce) || - !path_in_sparse_checkout(ce->name, &the_index))) + !path_in_sparse_checkout(ce->name, the_repository->index))) continue; - if (!ce_path_match(&the_index, ce, &pathspec, seen)) + if (!ce_path_match(the_repository->index, ce, &pathspec, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = xstrdup(ce->name); list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); if (list.entry[list.nr++].is_submodule && - !is_staging_gitmodules_ok(&the_index)) + !is_staging_gitmodules_ok(the_repository->index)) die(_("please stage your changes to .gitmodules or stash them to proceed")); } @@ -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); } @@ -391,7 +391,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!quiet) printf("rm '%s'\n", path); - if (remove_file_from_index(&the_index, path)) + if (remove_file_from_index(the_repository->index, path)) die(_("git rm: unable to remove %s"), path); } @@ -432,10 +432,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } strbuf_release(&buf); if (gitmodules_modified) - stage_updated_gitmodules(&the_index); + stage_updated_gitmodules(the_repository->index); } - if (write_locked_index(&the_index, &lock_file, + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); 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-branch.c b/builtin/show-branch.c index b01ec761d2..d72f4cb98d 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -479,13 +479,15 @@ static void snarf_refs(int head, int remotes) if (head) { int orig_cnt = ref_name_cnt; - for_each_ref(append_head_ref, NULL); + refs_for_each_ref(get_main_ref_store(the_repository), + append_head_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } if (remotes) { int orig_cnt = ref_name_cnt; - for_each_ref(append_remote_ref, NULL); + refs_for_each_ref(get_main_ref_store(the_repository), + append_remote_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } } @@ -549,7 +551,8 @@ static void append_one_rev(const char *av) match_ref_pattern = av; match_ref_slash = count_slashes(av); - for_each_ref(append_matching_ref, NULL); + refs_for_each_ref(get_main_ref_store(the_repository), + append_matching_ref, NULL); if (saved_matches == ref_name_cnt && ref_name_cnt < MAX_REVS) error(_("no matching refs with %s"), av); @@ -740,9 +743,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (ac == 0) { static const char *fake_av[2]; - fake_av[0] = resolve_refdup("HEAD", - RESOLVE_REF_READING, &oid, - NULL); + fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", + RESOLVE_REF_READING, + &oid, + NULL); fake_av[1] = NULL; av = fake_av; ac = 1; @@ -815,8 +820,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) snarf_refs(all_heads, all_remotes); } - head = resolve_refdup("HEAD", RESOLVE_REF_READING, - &head_oid, NULL); + head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", + RESOLVE_REF_READING, + &head_oid, NULL); if (with_current_branch && head) { int has_head = 0; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 1c15421e60..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>...]"), @@ -50,7 +50,7 @@ static void show_one(const struct show_one_options *opts, if (!opts->deref_tags) return; - if (!peel_iterated_oid(oid, &peeled)) { + if (!peel_iterated_oid(the_repository, oid, &peeled)) { hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev); printf("%s %s^{}\n", hex, refname); } @@ -129,7 +129,8 @@ static int cmd_show_ref__exclude_existing(const struct exclude_existing_options char buf[1024]; int patternlen = opts->pattern ? strlen(opts->pattern) : 0; - for_each_ref(add_existing, &existing_refs); + refs_for_each_ref(get_main_ref_store(the_repository), add_existing, + &existing_refs); while (fgets(buf, sizeof(buf), stdin)) { char *ref; int len = strlen(buf); @@ -173,7 +174,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, struct object_id oid; if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && - !read_ref(*refs, &oid)) { + !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) { show_one(show_one_opts, *refs, &oid); } else if (!show_one_opts->quiet) @@ -188,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; }; @@ -205,14 +206,20 @@ static int cmd_show_ref__patterns(const struct patterns_options *opts, show_ref_data.patterns = patterns; if (opts->show_head) - head_ref(show_ref, &show_ref_data); - if (opts->heads_only || opts->tags_only) { - if (opts->heads_only) - for_each_fullref_in("refs/heads/", show_ref, &show_ref_data); + refs_head_ref(get_main_ref_store(the_repository), show_ref, + &show_ref_data); + 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); if (opts->tags_only) - for_each_fullref_in("refs/tags/", show_ref, &show_ref_data); + refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/tags/", NULL, show_ref, + &show_ref_data); } else { - for_each_ref(show_ref, &show_ref_data); + refs_for_each_ref(get_main_ref_store(the_repository), + show_ref, &show_ref_data); } if (!show_ref_data.found_match) return 1; @@ -286,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 062be1fbc0..80ccfc7a08 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "config.h" @@ -196,7 +195,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) commit = argv[0]; if (!commit) { - if (!ref_exists(ref_stash)) { + if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { fprintf_ln(stderr, _("No stash entries found.")); return -1; } @@ -244,7 +243,8 @@ static int do_clear_stash(void) if (repo_get_oid(the_repository, ref_stash, &obj)) return 0; - return delete_ref(NULL, ref_stash, &obj, 0); + return refs_delete_ref(get_main_ref_store(the_repository), NULL, + ref_stash, &obj, 0); } static int clear_stash(int argc, const char **argv, const char *prefix) @@ -273,7 +273,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) struct lock_file lock_file = LOCK_INIT; repo_read_index_preload(the_repository, NULL, 0); - if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL)) + if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL)) return -1; repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -287,8 +287,8 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size); opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; + opts.src_index = the_repository->index; + opts.dst_index = the_repository->index; opts.merge = 1; opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0; opts.update = update; @@ -299,7 +299,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) if (unpack_trees(nr_trees, t, &opts)) return -1; - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; @@ -430,7 +430,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) state.force = 1; state.quiet = 1; state.refresh_cache = 1; - state.istate = &the_index; + state.istate = the_repository->index; /* * Step 1: get a difference between orig_tree (which corresponding @@ -454,7 +454,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) /* Look up the path's position in the current index. */ p = diff_queued_diff.queue[i]; - pos = index_name_pos(&the_index, p->two->path, + pos = index_name_pos(the_repository->index, p->two->path, strlen(p->two->path)); /* @@ -465,10 +465,10 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) * path, but left it out of the working tree, then clear the * SKIP_WORKTREE bit and write it to the working tree. */ - if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) { + if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) { struct stat st; - ce = the_index.cache[pos]; + ce = the_repository->index->cache[pos]; if (!lstat(ce->name, &st)) { /* Conflicting path present; relocate it */ struct strbuf new_path = STRBUF_INIT; @@ -504,12 +504,12 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) if (pos < 0) option = ADD_CACHE_OK_TO_ADD; - ce = make_cache_entry(&the_index, + ce = make_cache_entry(the_repository->index, p->one->mode, &p->one->oid, p->one->path, 0, 0); - add_index_entry(&the_index, ce, option); + add_index_entry(the_repository->index, ce, option); } } diff_flush(&diff_opts); @@ -518,7 +518,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) * Step 4: write the new index to disk */ repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR); - if (write_locked_index(&the_index, &lock, + if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("could not write index")); } @@ -539,7 +539,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, NULL, NULL, NULL)) return error(_("could not write index")); - if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0, + if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0, NULL)) return error(_("cannot apply a stash in the middle of a merge")); @@ -562,14 +562,14 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, return error(_("conflicts in index. " "Try without --index.")); - discard_index(&the_index); + discard_index(the_repository->index); repo_read_index(the_repository); - if (write_index_as_tree(&index_tree, &the_index, + if (write_index_as_tree(&index_tree, the_repository->index, get_index_file(), 0, NULL)) return error(_("could not save index tree")); reset_head(); - discard_index(&the_index); + discard_index(the_repository->index); repo_read_index(the_repository); } } @@ -687,7 +687,8 @@ static int reject_reflog_ent(struct object_id *ooid UNUSED, static int reflog_is_empty(const char *refname) { - return !for_each_reflog_ent(refname, reject_reflog_ent, NULL); + return !refs_for_each_reflog_ent(get_main_ref_store(the_repository), + refname, reject_reflog_ent, NULL); } static int do_drop_stash(struct stash_info *info, int quiet) @@ -824,7 +825,7 @@ static int list_stash(int argc, const char **argv, const char *prefix) git_stash_list_usage, PARSE_OPT_KEEP_UNKNOWN_OPT); - if (!ref_exists(ref_stash)) + if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) return 0; cp.git_cmd = 1; @@ -875,8 +876,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op } unpack_tree_opt.head_idx = -1; - unpack_tree_opt.src_index = &the_index; - unpack_tree_opt.dst_index = &the_index; + unpack_tree_opt.src_index = the_repository->index; + unpack_tree_opt.dst_index = the_repository->index; unpack_tree_opt.merge = 1; unpack_tree_opt.fn = stash_worktree_untracked_merge; @@ -974,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); @@ -998,10 +1001,10 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms if (!stash_msg) stash_msg = "Created via \"git stash store\"."; - if (update_ref(stash_msg, ref_stash, w_commit, NULL, - REF_FORCE_CREATE_REFLOG, - quiet ? UPDATE_REFS_QUIET_ON_ERR : - UPDATE_REFS_MSG_ON_ERR)) { + if (refs_update_ref(get_main_ref_store(the_repository), stash_msg, ref_stash, w_commit, NULL, + REF_FORCE_CREATE_REFLOG, + quiet ? UPDATE_REFS_QUIET_ON_ERR : + UPDATE_REFS_MSG_ON_ERR)) { if (!quiet) { fprintf_ln(stderr, _("Cannot update %s with %s"), ref_stash, oid_to_hex(w_commit)); @@ -1017,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, @@ -1042,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, @@ -1205,8 +1214,8 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch, } cp_diff_tree.git_cmd = 1; - strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD", - oid_to_hex(&info->w_tree), "--", NULL); + strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary", + "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; goto done; @@ -1384,7 +1393,8 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b goto done; } - branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags); + branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + "HEAD", 0, NULL, &flags); if (flags & REF_ISSYMREF) skip_prefix(branch_ref, "refs/heads/", &branch_name); head_short_sha1 = repo_find_unique_abbrev(the_repository, @@ -1395,7 +1405,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf); commit_list_insert(head_commit, &parents); - if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0, + if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0, NULL) || commit_tree(commit_tree_label.buf, commit_tree_label.len, &info->i_tree, parents, &info->i_commit, NULL, NULL)) { @@ -1406,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) @@ -1451,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), @@ -1477,6 +1485,7 @@ done: strbuf_release(&commit_tree_label); strbuf_release(&msg); strbuf_release(&untracked_files); + free_commit_list(parents); return ret; } @@ -1540,9 +1549,9 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q char *ps_matched = xcalloc(ps->nr, 1); /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < the_index.cache_nr; i++) - ce_path_match(&the_index, the_index.cache[i], ps, + ensure_full_index(the_repository->index); + for (i = 0; i < the_repository->index->cache_nr; i++) + ce_path_match(the_repository->index, the_repository->index->cache[i], ps, ps_matched); if (report_path_error(ps_matched, ps)) { @@ -1566,7 +1575,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q goto done; } - if (!reflog_exists(ref_stash) && do_clear_stash()) { + if (!refs_reflog_exists(get_main_ref_store(the_repository), ref_stash) && do_clear_stash()) { ret = -1; if (!quiet) fprintf_ln(stderr, _("Cannot initialize stash")); @@ -1612,7 +1621,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q goto done; } } - discard_index(&the_index); + discard_index(the_repository->index); if (ps->nr) { struct child_process cp_add = CHILD_PROCESS_INIT; struct child_process cp_diff = CHILD_PROCESS_INIT; @@ -1662,7 +1671,28 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q } } - if (keep_index == 1 && !is_null_oid(&info.i_tree)) { + /* + * When keeping staged entries, we need to reset the working + * directory to match the state of our index. This can be + * skipped when the index is the empty tree, because there is + * nothing to reset in that case: + * + * - When the index has any file, regardless of whether + * staged or not, the tree cannot be empty by definition + * and thus we enter the condition. + * + * - When the index has no files, the only thing we need to + * care about is untracked files when `--include-untracked` + * is given. But as we already execute git-clean(1) further + * up to delete such untracked files we don't have to do + * anything here, either. + * + * We thus skip calling git-checkout(1) in this case, also + * because running it on an empty tree will cause it to fail + * due to the pathspec not matching anything. + */ + if (keep_index == 1 && !is_null_oid(&info.i_tree) && + !is_empty_tree_oid(&info.i_tree, the_repository->hash_algo)) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index e4e18adb57..f1218a1995 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "abspath.h" #include "environment.h" @@ -207,18 +206,18 @@ static int module_list_compute(const char **argv, if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); - for (i = 0; i < the_index.cache_nr; i++) { - const struct cache_entry *ce = the_index.cache[i]; + for (i = 0; i < the_repository->index->cache_nr; i++) { + const struct cache_entry *ce = the_repository->index->cache[i]; - if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce), + if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce), 0, ps_matched, 1) || !S_ISGITLINK(ce->ce_mode)) continue; ALLOC_GROW(list->entries, list->nr + 1, list->alloc); list->entries[list->nr++] = ce; - while (i + 1 < the_index.cache_nr && - !strcmp(ce->name, the_index.cache[i + 1]->name)) + while (i + 1 < the_repository->index->cache_nr && + !strcmp(ce->name, the_repository->index->cache[i + 1]->name)) /* * Skip entries with the same name in different stages * to make sure an entry is returned only once. @@ -257,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 @@ -303,6 +300,9 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, struct child_process cp = CHILD_PROCESS_INIT; char *displaypath; + if (validate_submodule_path(path) < 0) + exit(128); + displaypath = get_submodule_displaypath(path, info->prefix, info->super_prefix); @@ -376,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"); @@ -634,6 +633,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, .free_removed_argv_elements = 1, }; + if (validate_submodule_path(path) < 0) + exit(128); + if (!submodule_from_path(the_repository, null_oid(), path)) die(_("no submodule mapping found in .gitmodules for path '%s'"), path); @@ -674,7 +676,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, displaypath); } else if (!(flags & OPT_CACHED)) { struct object_id oid; - struct ref_store *refs = get_submodule_ref_store(path); + struct ref_store *refs = repo_get_submodule_ref_store(the_repository, + path); if (!refs) { print_status(flags, '-', path, ce_oid, displaypath); @@ -698,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"); @@ -898,7 +900,8 @@ static void generate_submodule_summary(struct summary_cb *info, if (!info->cached && oideq(&p->oid_dst, null_oid())) { if (S_ISGITLINK(p->mod_dst)) { - struct ref_store *refs = get_submodule_ref_store(p->sm_path); + struct ref_store *refs = repo_get_submodule_ref_store(the_repository, + p->sm_path); if (refs) refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst); @@ -907,7 +910,7 @@ static void generate_submodule_summary(struct summary_cb *info, int fd = open(p->sm_path, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0 || - index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB, + index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB, p->sm_path, 0)) error(_("couldn't hash object from '%s'"), p->sm_path); } else { @@ -1238,6 +1241,9 @@ static void sync_submodule(const char *path, const char *prefix, if (!is_submodule_active(the_repository, path)) return; + if (validate_submodule_path(path) < 0) + exit(128); + sub = submodule_from_path(the_repository, null_oid(), path); if (sub && sub->url) { @@ -1296,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"); @@ -1381,6 +1385,9 @@ static void deinit_submodule(const char *path, const char *prefix, struct strbuf sb_config = STRBUF_INIT; char *sub_git_dir = xstrfmt("%s/.git", path); + if (validate_submodule_path(path) < 0) + exit(128); + sub = submodule_from_path(the_repository, null_oid(), path); if (!sub || !sub->name) @@ -1662,16 +1669,42 @@ static char *clone_submodule_sm_gitdir(const char *name) return sm_gitdir; } +static int dir_contains_only_dotgit(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *e; + int ret = 1; + + if (!dir) + return 0; + + e = readdir_skip_dot_and_dotdot(dir); + if (!e) + ret = 0; + else if (strcmp(DEFAULT_GIT_DIR_ENVIRONMENT, e->d_name) || + (e = readdir_skip_dot_and_dotdot(dir))) { + error("unexpected item '%s' in '%s'", e->d_name, path); + ret = 0; + } + + closedir(dir); + return ret; +} + static int clone_submodule(const struct module_clone_data *clone_data, struct string_list *reference) { char *p; char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name); char *sm_alternate = NULL, *error_strategy = NULL; + struct stat st; struct child_process cp = CHILD_PROCESS_INIT; const char *clone_data_path = clone_data->path; char *to_free = NULL; + if (validate_submodule_path(clone_data_path) < 0) + exit(128); + if (!is_absolute_path(clone_data->path)) clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(), clone_data->path); @@ -1681,6 +1714,10 @@ static int clone_submodule(const struct module_clone_data *clone_data, "git dir"), sm_gitdir); if (!file_exists(sm_gitdir)) { + if (clone_data->require_init && !stat(clone_data_path, &st) && + !is_empty_dir(clone_data_path)) + die(_("directory not empty: '%s'"), clone_data_path); + if (safe_create_leading_directories_const(sm_gitdir) < 0) die(_("could not create directory '%s'"), sm_gitdir); @@ -1725,10 +1762,18 @@ static int clone_submodule(const struct module_clone_data *clone_data, if(run_command(&cp)) die(_("clone of '%s' into submodule path '%s' failed"), clone_data->url, clone_data_path); + + if (clone_data->require_init && !stat(clone_data_path, &st) && + !dir_contains_only_dotgit(clone_data_path)) { + char *dot_git = xstrfmt("%s/.git", clone_data_path); + unlink(dot_git); + free(dot_git); + die(_("directory not empty: '%s'"), clone_data_path); + } } else { char *path; - if (clone_data->require_init && !access(clone_data_path, X_OK) && + if (clone_data->require_init && !stat(clone_data_path, &st) && !is_empty_dir(clone_data_path)) die(_("directory not empty: '%s'"), clone_data_path); if (safe_create_leading_directories_const(clone_data_path) < 0) @@ -1738,6 +1783,23 @@ static int clone_submodule(const struct module_clone_data *clone_data, free(path); } + /* + * We already performed this check at the beginning of this function, + * before cloning the objects. This tries to detect racy behavior e.g. + * in parallel clones, where another process could easily have made the + * gitdir nested _after_ it was created. + * + * To prevent further harm coming from this unintentionally-nested + * gitdir, let's disable it by deleting the `HEAD` file. + */ + if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) { + char *head = xstrfmt("%s/HEAD", sm_gitdir); + unlink(head); + free(head); + die(_("refusing to create/use '%s' in another submodule's " + "git dir"), sm_gitdir); + } + connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0); p = git_pathdup_submodule(clone_data_path, "config"); @@ -2390,7 +2452,9 @@ static int remote_submodule_branch(const char *path, const char **branch) } if (!strcmp(*branch, ".")) { - const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); + const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + "HEAD", 0, NULL, + NULL); if (!refname) return die_message(_("No such ref: %s"), "HEAD"); @@ -2466,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"); @@ -2517,6 +2580,9 @@ static int update_submodule(struct update_data *update_data) { int ret; + if (validate_submodule_path(update_data->sm_path) < 0) + return -1; + ret = determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, @@ -2527,7 +2593,8 @@ static int update_submodule(struct update_data *update_data) if (update_data->just_cloned) oidcpy(&update_data->suboid, null_oid()); - else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid)) + else if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path, + "HEAD", &update_data->suboid)) return die_message(_("Unable to find current revision in submodule path '%s'"), update_data->displaypath); @@ -2554,7 +2621,8 @@ static int update_submodule(struct update_data *update_data) update_data->sm_path); } - if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid)) + if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path, + remote_ref, &update_data->oid)) return die_message(_("Unable to find %s revision in submodule path '%s'"), remote_ref, update_data->sm_path); @@ -2624,12 +2692,21 @@ static int update_submodules(struct update_data *update_data) for (i = 0; i < suc.update_clone_nr; i++) { struct update_clone_data ucd = suc.update_clone[i]; - int code; + int code = 128; oidcpy(&update_data->oid, &ucd.oid); update_data->just_cloned = ucd.just_cloned; update_data->sm_path = ucd.sub->path; + /* + * Verify that the submodule path does not contain any + * symlinks; if it does, it might have been tampered with. + * TODO: allow exempting it via + * `safe.submodule.path` or something + */ + if (validate_submodule_path(update_data->sm_path) < 0) + goto fail; + code = ensure_core_worktree(update_data->sm_path); if (code) goto fail; @@ -2796,7 +2873,8 @@ static int push_check(int argc, const char **argv, const char *prefix UNUSED) argv++; argc--; /* Get the submodule's head ref and determine if it is detached */ - head = resolve_refdup("HEAD", 0, &head_oid, NULL); + head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", + 0, &head_oid, NULL); if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) @@ -3243,21 +3321,21 @@ static void die_on_index_match(const char *path, int force) char *ps_matched = xcalloc(ps.nr, 1); /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); + ensure_full_index(the_repository->index); /* * Since there is only one pathspec, we just need to * check ps_matched[0] to know if a cache entry matched. */ - for (i = 0; i < the_index.cache_nr; i++) { - ce_path_match(&the_index, the_index.cache[i], &ps, + for (i = 0; i < the_repository->index->cache_nr; i++) { + ce_path_match(the_repository->index, the_repository->index->cache[i], &ps, ps_matched); if (ps_matched[0]) { if (!force) die(_("'%s' already exists in the index"), path); - if (!S_ISGITLINK(the_index.cache[i]->ce_mode)) + if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode)) die(_("'%s' already exists in the index " "and is not a submodule"), path); break; @@ -3274,7 +3352,7 @@ static void die_on_repo_without_commits(const char *path) strbuf_addstr(&sb, path); if (is_nonbare_repository_dir(&sb)) { struct object_id oid; - if (resolve_gitlink_ref(path, "HEAD", &oid) < 0) + if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0) die(_("'%s' does not have a commit checked out"), path); } strbuf_release(&sb); @@ -3356,6 +3434,9 @@ static int module_add(int argc, const char **argv, const char *prefix) normalize_path_copy(add_data.sm_path, add_data.sm_path); strip_dir_trailing_slashes(add_data.sm_path); + if (validate_submodule_path(add_data.sm_path) < 0) + exit(128); + die_on_index_match(add_data.sm_path, force); die_on_repo_without_commits(add_data.sm_path); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index c9defe4d2e..81abdd170f 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -18,7 +18,8 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i const char *refname; resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE); - refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag); + refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + HEAD, resolve_flags, NULL, &flag); if (!refname) die("No such ref: %s", HEAD); @@ -31,7 +32,9 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, i if (print) { char *to_free = NULL; if (shorten) - refname = to_free = shorten_unambiguous_ref(refname, 0); + refname = to_free = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), + refname, + 0); puts(refname); free(to_free); } @@ -66,7 +69,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) die("deleting '%s' is not allowed", argv[0]); - return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF); + return refs_delete_ref(get_main_ref_store(the_repository), + NULL, argv[0], NULL, REF_NO_DEREF); } switch (argc) { @@ -79,7 +83,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) die("Refusing to point HEAD outside of refs/"); if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0) die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]); - ret = !!create_symref(argv[0], argv[1], msg); + ret = !!refs_update_symref(get_main_ref_store(the_repository), + argv[0], argv[1], msg); break; default: usage_with_options(git_symbolic_ref_usage, options); diff --git a/builtin/tag.c b/builtin/tag.c index 9a33cb50b4..a1fb218512 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -28,9 +28,11 @@ #include "date.h" #include "write-or-die.h" #include "object-file-convert.h" +#include "trailer.h" static const char * const git_tag_usage[] = { N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n" + " [(--trailer <token>[(=|:)<value>])...]\n" " <tagname> [<commit> | <object>]"), N_("git tag -d <tagname>..."), N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n" @@ -87,7 +89,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, for (p = argv; *p; p++) { strbuf_reset(&ref); strbuf_addf(&ref, "refs/tags/%s", *p); - if (read_ref(ref.buf, &oid)) { + if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) { error(_("tag '%s' not found."), *p); had_error = 1; continue; @@ -116,13 +118,13 @@ static int delete_tags(const char **argv) struct string_list_item *item; result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete); - if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF)) + if (refs_delete_refs(get_main_ref_store(the_repository), NULL, &refs_to_delete, REF_NO_DEREF)) result = 1; for_each_string_list_item(item, &refs_to_delete) { const char *name = item->string; struct object_id *oid = item->util; - if (!ref_exists(name)) + if (!refs_ref_exists(get_main_ref_store(the_repository), name)) printf(_("Deleted tag '%s' (was %s)\n"), item->string + 10, repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV)); @@ -290,10 +292,12 @@ static const char message_advice_nested_tag[] = static void create_tag(const struct object_id *object, const char *object_ref, const char *tag, struct strbuf *buf, struct create_tag_options *opt, - struct object_id *prev, struct object_id *result, char *path) + struct object_id *prev, struct object_id *result, + struct strvec *trailer_args, char *path) { enum object_type type; struct strbuf header = STRBUF_INIT; + int should_edit; type = oid_object_info(the_repository, object, NULL); if (type <= OBJ_NONE) @@ -313,13 +317,15 @@ static void create_tag(const struct object_id *object, const char *object_ref, tag, git_committer_info(IDENT_STRICT)); - if (!opt->message_given || opt->use_editor) { + should_edit = opt->use_editor || !opt->message_given; + if (should_edit || trailer_args->nr) { int fd; /* write the template message before editing: */ fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (opt->message_given) { + if (opt->message_given && buf->len) { + strbuf_complete(buf, '\n'); write_or_die(fd, buf->buf, buf->len); strbuf_reset(buf); } else if (!is_null_oid(prev)) { @@ -338,10 +344,19 @@ static void create_tag(const struct object_id *object, const char *object_ref, } close(fd); - if (launch_editor(path, buf, NULL)) { - fprintf(stderr, - _("Please supply the message using either -m or -F option.\n")); - exit(1); + if (trailer_args->nr && amend_file_with_trailers(path, trailer_args)) + die(_("unable to pass trailers to --trailers")); + + if (should_edit) { + if (launch_editor(path, buf, NULL)) { + fprintf(stderr, + _("Please supply the message using either -m or -F option.\n")); + exit(1); + } + } else if (trailer_args->nr) { + strbuf_reset(buf); + if (strbuf_read_file(buf, path, 0) < 0) + die_errno(_("failed to read '%s'"), path); } } @@ -463,6 +478,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; + struct strvec trailer_args = STRVEC_INIT; int icase = 0; int edit_flag = 0; struct option options[] = { @@ -479,6 +495,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F('m', "message", &msg, N_("message"), N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg), OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), + OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), + N_("add custom trailer(s)"), PARSE_OPT_NONEG), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")), OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_CLEANUP(&cleanup_arg), @@ -548,7 +566,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) opt.sign = 1; set_signing_key(keyid); } - create_tag_object = (opt.sign || annotate || msg.given || msgfile); + create_tag_object = (opt.sign || annotate || msg.given || msgfile || + edit_flag || trailer_args.nr); if ((create_tag_object || force) && (cmdmode != 0)) usage_with_options(git_tag_usage, options); @@ -630,8 +649,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (strbuf_check_tag_ref(&ref, tag)) die(_("'%s' is not a valid tag name."), tag); - if (read_ref(ref.buf, &prev)) - oidclr(&prev); + if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev)) + oidclr(&prev, the_repository->hash_algo); else if (!force) die(_("tag '%s' already exists"), tag); @@ -654,12 +673,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) opt.sign = 1; path = git_pathdup("TAG_EDITMSG"); create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object, - path); + &trailer_args, path); } - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction || ref_transaction_update(transaction, ref.buf, &object, &prev, + NULL, NULL, create_reflog ? REF_FORCE_CREATE_REFLOG : 0, reflog_msg.buf, &err) || ref_transaction_commit(transaction, &err)) { @@ -686,6 +707,7 @@ cleanup: strbuf_release(&reflog_msg); strbuf_release(&msg.buf); strbuf_release(&err); + strvec_clear(&trailer_args); free(msgfile); return ret; } 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-index.c b/builtin/update-index.c index 7bcaa1476c..d343416ae2 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -3,7 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "bulk-checkin.h" #include "config.h" @@ -247,16 +247,16 @@ done: static int mark_ce_flags(const char *path, int flag, int mark) { int namelen = strlen(path); - int pos = index_name_pos(&the_index, path, namelen); + int pos = index_name_pos(the_repository->index, path, namelen); if (0 <= pos) { - mark_fsmonitor_invalid(&the_index, the_index.cache[pos]); + mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]); if (mark) - the_index.cache[pos]->ce_flags |= flag; + the_repository->index->cache[pos]->ce_flags |= flag; else - the_index.cache[pos]->ce_flags &= ~flag; - the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE; - cache_tree_invalidate_path(&the_index, path); - the_index.cache_changed |= CE_ENTRY_CHANGED; + the_repository->index->cache[pos]->ce_flags &= ~flag; + the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE; + cache_tree_invalidate_path(the_repository->index, path); + the_repository->index->cache_changed |= CE_ENTRY_CHANGED; return 0; } return -1; @@ -266,7 +266,7 @@ static int remove_one_path(const char *path) { if (!allow_remove) return error("%s: does not exist and --remove not passed", path); - if (remove_file_from_index(&the_index, path)) + if (remove_file_from_index(the_repository->index, path)) return error("%s: cannot remove from the index", path); return 0; } @@ -291,24 +291,24 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len struct cache_entry *ce; /* Was the old index entry already up-to-date? */ - if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0)) + if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0)) return 0; - ce = make_empty_cache_entry(&the_index, len); + ce = make_empty_cache_entry(the_repository->index, len); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(0); ce->ce_namelen = len; - fill_stat_cache_info(&the_index, ce, st); + fill_stat_cache_info(the_repository->index, ce, st); ce->ce_mode = ce_mode_from_stat(old, st->st_mode); - if (index_path(&the_index, &ce->oid, path, st, + if (index_path(the_repository->index, &ce->oid, path, st, info_only ? 0 : HASH_WRITE_OBJECT)) { discard_cache_entry(ce); return -1; } option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_index_entry(&the_index, ce, option)) { + if (add_index_entry(the_repository->index, ce, option)) { discard_cache_entry(ce); return error("%s: cannot add to the index - missing --add option?", path); } @@ -341,15 +341,16 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len static int process_directory(const char *path, int len, struct stat *st) { struct object_id oid; - int pos = index_name_pos(&the_index, path, len); + int pos = index_name_pos(the_repository->index, path, len); /* Exact match: file or existing gitlink */ if (pos >= 0) { - const struct cache_entry *ce = the_index.cache[pos]; + const struct cache_entry *ce = the_repository->index->cache[pos]; if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ - if (resolve_gitlink_ref(path, "HEAD", &oid) < 0) + if (repo_resolve_gitlink_ref(the_repository, path, + "HEAD", &oid) < 0) return 0; return add_one_path(ce, path, len, st); @@ -360,8 +361,8 @@ static int process_directory(const char *path, int len, struct stat *st) /* Inexact match: is there perhaps a subdirectory match? */ pos = -pos-1; - while (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos++]; + while (pos < the_repository->index->cache_nr) { + const struct cache_entry *ce = the_repository->index->cache[pos++]; if (strncmp(ce->name, path, len)) break; @@ -375,7 +376,7 @@ static int process_directory(const char *path, int len, struct stat *st) } /* No match - should we add it as a gitlink? */ - if (!resolve_gitlink_ref(path, "HEAD", &oid)) + if (!repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid)) return add_one_path(NULL, path, len, st); /* Error out. */ @@ -391,8 +392,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno) if (has_symlink_leading_path(path, len)) return error("'%s' is beyond a symbolic link", path); - pos = index_name_pos(&the_index, path, len); - ce = pos < 0 ? NULL : the_index.cache[pos]; + pos = index_name_pos(the_repository->index, path, len); + ce = pos < 0 ? NULL : the_repository->index->cache[pos]; if (ce && ce_skip_worktree(ce)) { /* * working directory version is assumed "good" @@ -400,7 +401,7 @@ static int process_path(const char *path, struct stat *st, int stat_errno) * On the other hand, removing it from index should work */ if (!ignore_skip_worktree_entries && allow_remove && - remove_file_from_index(&the_index, path)) + remove_file_from_index(the_repository->index, path)) return error("%s: cannot remove from the index", path); return 0; } @@ -428,7 +429,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid, return error("Invalid path '%s'", path); len = strlen(path); - ce = make_empty_cache_entry(&the_index, len); + ce = make_empty_cache_entry(the_repository->index, len); oidcpy(&ce->oid, oid); memcpy(ce->name, path, len); @@ -439,7 +440,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid, ce->ce_flags |= CE_VALID; option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_index_entry(&the_index, ce, option)) + if (add_index_entry(the_repository->index, ce, option)) return error("%s: cannot add to the index - missing --add option?", path); report("add '%s'", path); @@ -451,11 +452,11 @@ static void chmod_path(char flip, const char *path) int pos; struct cache_entry *ce; - pos = index_name_pos(&the_index, path, strlen(path)); + pos = index_name_pos(the_repository->index, path, strlen(path)); if (pos < 0) goto fail; - ce = the_index.cache[pos]; - if (chmod_index_entry(&the_index, ce, flip) < 0) + ce = the_repository->index->cache[pos]; + if (chmod_index_entry(the_repository->index, ce, flip) < 0) goto fail; report("chmod %cx '%s'", flip, path); @@ -498,7 +499,7 @@ static void update_one(const char *path) } if (force_remove) { - if (remove_file_from_index(&the_index, path)) + if (remove_file_from_index(the_repository->index, path)) die("git update-index: unable to remove %s", path); report("remove '%s'", path); return; @@ -581,7 +582,7 @@ static void read_index_info(int nul_term_line) if (!mode) { /* mode == 0 means there is no such path -- remove */ - if (remove_file_from_index(&the_index, path_name)) + if (remove_file_from_index(the_repository->index, path_name)) die("git update-index: unable to remove %s", ptr); } @@ -622,12 +623,12 @@ static struct cache_entry *read_one_ent(const char *which, error("%s: not in %s branch.", path, which); return NULL; } - if (!the_index.sparse_index && mode == S_IFDIR) { + if (!the_repository->index->sparse_index && mode == S_IFDIR) { if (which) error("%s: not a blob in %s branch.", path, which); return NULL; } - ce = make_empty_cache_entry(&the_index, namelen); + ce = make_empty_cache_entry(the_repository->index, namelen); oidcpy(&ce->oid, &oid); memcpy(ce->name, path, namelen); @@ -642,12 +643,12 @@ static int unresolve_one(const char *path) struct string_list_item *item; int res = 0; - if (!the_index.resolve_undo) + if (!the_repository->index->resolve_undo) return res; - item = string_list_lookup(the_index.resolve_undo, path); + item = string_list_lookup(the_repository->index->resolve_undo, path); if (!item) return res; /* no resolve-undo record for the path */ - res = unmerge_index_entry(&the_index, path, item->util, 0); + res = unmerge_index_entry(the_repository->index, path, item->util, 0); FREE_AND_NULL(item->util); return res; } @@ -682,19 +683,19 @@ static int do_reupdate(const char **paths, PATHSPEC_PREFER_CWD, prefix, paths); - if (read_ref("HEAD", &head_oid)) + if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &head_oid)) /* If there is no HEAD, that means it is an initial * commit. Update everything in the index. */ has_head = 0; redo: - for (pos = 0; pos < the_index.cache_nr; pos++) { - const struct cache_entry *ce = the_index.cache[pos]; + for (pos = 0; pos < the_repository->index->cache_nr; pos++) { + const struct cache_entry *ce = the_repository->index->cache[pos]; struct cache_entry *old = NULL; int save_nr; char *path; - if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL)) + if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL)) continue; if (has_head) old = read_one_ent(NULL, &head_oid, @@ -710,7 +711,7 @@ static int do_reupdate(const char **paths, * to process each path individually */ if (S_ISSPARSEDIR(ce->ce_mode)) { - ensure_full_index(&the_index); + ensure_full_index(the_repository->index); goto redo; } @@ -718,12 +719,12 @@ static int do_reupdate(const char **paths, * path anymore, in which case, under 'allow_remove', * or worse yet 'allow_replace', active_nr may decrease. */ - save_nr = the_index.cache_nr; + save_nr = the_repository->index->cache_nr; path = xstrdup(ce->name); update_one(path); free(path); discard_cache_entry(old); - if (save_nr != the_index.cache_nr) + if (save_nr != the_repository->index->cache_nr) goto redo; } clear_pathspec(&pathspec); @@ -739,9 +740,9 @@ static int refresh(struct refresh_params *o, unsigned int flag) { setup_work_tree(); repo_read_index(the_repository); - *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL, + *o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL, NULL, NULL); - if (has_racy_timestamp(&the_index)) { + if (has_racy_timestamp(the_repository->index)) { /* * Even if nothing else has changed, updating the file * increases the chance that racy timestamps become @@ -750,7 +751,7 @@ static int refresh(struct refresh_params *o, unsigned int flag) * refresh_index() as these are no actual errors. * cmd_status() does the same. */ - the_index.cache_changed |= SOMETHING_CHANGED; + the_repository->index->cache_changed |= SOMETHING_CHANGED; } return 0; } @@ -787,7 +788,7 @@ static int resolve_undo_clear_callback(const struct option *opt UNUSED, { BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - resolve_undo_clear_index(&the_index); + resolve_undo_clear_index(the_repository->index); return 0; } @@ -888,7 +889,7 @@ static enum parse_opt_result unresolve_callback( *has_errors = do_unresolve(ctx->argc, ctx->argv, prefix, prefix ? strlen(prefix) : 0); if (*has_errors) - the_index.cache_changed = 0; + the_repository->index->cache_changed = 0; ctx->argv += ctx->argc - 1; ctx->argc = 1; @@ -909,7 +910,7 @@ static enum parse_opt_result reupdate_callback( setup_work_tree(); *has_errors = do_reupdate(ctx->argv + 1, prefix); if (*has_errors) - the_index.cache_changed = 0; + the_repository->index->cache_changed = 0; ctx->argv += ctx->argc - 1; ctx->argc = 1; @@ -1056,7 +1057,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (entries < 0) die("cache corrupted"); - the_index.updated_skipworktree = 1; + the_repository->index->updated_skipworktree = 1; /* * Custom copy of parse_options() because we want to handle @@ -1111,18 +1112,18 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { if (preferred_index_format < 0) { - printf(_("%d\n"), the_index.version); + printf(_("%d\n"), the_repository->index->version); } else if (preferred_index_format < INDEX_FORMAT_LB || INDEX_FORMAT_UB < preferred_index_format) { die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); } else { - if (the_index.version != preferred_index_format) - the_index.cache_changed |= SOMETHING_CHANGED; + if (the_repository->index->version != preferred_index_format) + the_repository->index->cache_changed |= SOMETHING_CHANGED; report(_("index-version: was %d, set to %d"), - the_index.version, preferred_index_format); - the_index.version = preferred_index_format; + the_repository->index->version, preferred_index_format); + the_repository->index->version = preferred_index_format; } } @@ -1159,16 +1160,16 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) warning(_("core.splitIndex is set to false; " "remove or change it, if you really want to " "enable split index")); - if (the_index.split_index) - the_index.cache_changed |= SPLIT_INDEX_ORDERED; + if (the_repository->index->split_index) + the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED; else - add_split_index(&the_index); + add_split_index(the_repository->index); } else if (!split_index) { if (git_config_get_split_index() == 1) warning(_("core.splitIndex is set to true; " "remove or change it, if you really want to " "disable split index")); - remove_split_index(&the_index); + remove_split_index(the_repository->index); } prepare_repo_settings(r); @@ -1180,7 +1181,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) warning(_("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache")); - remove_untracked_cache(&the_index); + remove_untracked_cache(the_repository->index); report(_("Untracked cache disabled")); break; case UC_TEST: @@ -1192,7 +1193,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) warning(_("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache")); - add_untracked_cache(&the_index); + add_untracked_cache(the_repository->index); report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); break; default: @@ -1222,7 +1223,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) "set it if you really want to " "enable fsmonitor")); } - add_fsmonitor(&the_index); + add_fsmonitor(the_repository->index); report(_("fsmonitor enabled")); } else if (!fsmonitor) { enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r); @@ -1230,17 +1231,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) warning(_("core.fsmonitor is set; " "remove it if you really want to " "disable fsmonitor")); - remove_fsmonitor(&the_index); + remove_fsmonitor(the_repository->index); report(_("fsmonitor disabled")); } - if (the_index.cache_changed || force_write) { + if (the_repository->index->cache_changed || force_write) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); unable_to_lock_die(get_index_file(), lock_error); } - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK)) die("Unable to write new index file"); } diff --git a/builtin/update-ref.c b/builtin/update-ref.c index e46afbc46d..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 @@ -204,12 +263,68 @@ static void parse_cmd_update(struct ref_transaction *transaction, if (ref_transaction_update(transaction, refname, &new_oid, have_old ? &old_oid : NULL, + NULL, 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_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); } @@ -233,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); } @@ -269,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; @@ -277,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) { @@ -290,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); } @@ -379,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) @@ -397,7 +611,8 @@ static void update_refs_stdin(void) struct ref_transaction *transaction; int i, j; - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) die("%s", err.buf); @@ -464,7 +679,8 @@ static void update_refs_stdin(void) * get a "start". */ state = cmd->state; - transaction = ref_transaction_begin(&err); + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + &err); if (!transaction) die("%s", err.buf); @@ -561,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); } @@ -571,11 +787,14 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) * For purposes of backwards compatibility, we treat * NULL_SHA1 as "don't care" here: */ - return delete_ref(msg, refname, - (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL, - default_flags); + return refs_delete_ref(get_main_ref_store(the_repository), + msg, refname, + (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL, + default_flags); else - return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL, - default_flags | create_reflog_flag, - UPDATE_REFS_DIE_ON_ERR); + return refs_update_ref(get_main_ref_store(the_repository), + msg, refname, &oid, + oldval ? &oldoid : NULL, + default_flags | create_reflog_flag, + UPDATE_REFS_DIE_ON_ERR); } diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 15afb97260..46d93278d9 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -9,6 +9,7 @@ #include "upload-pack.h" #include "serve.h" #include "commit.h" +#include "environment.h" static const char * const upload_pack_usage[] = { N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" @@ -39,6 +40,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("upload-pack"); disable_replace_refs(); save_commit_buffer = 0; + xsetenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 0); argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0); diff --git a/builtin/var.c b/builtin/var.c index cf5567208a..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>)"; @@ -46,12 +47,12 @@ static char *pager(int ident_flag UNUSED) static char *default_branch(int ident_flag UNUSED) { - return xstrdup_or_null(git_default_branch_name(1)); + return repo_default_branch_name(the_repository, 1); } 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 7c6c72536b..1d51e54fcd 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -433,7 +433,7 @@ static int add_worktree(const char *path, const char *refname, /* is 'refname' a branch or commit? */ if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && - ref_exists(symref.buf)) { + refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) { is_branch = 1; if (!opts->force) die_if_checked_out(symref.buf, 0); @@ -509,7 +509,7 @@ static int add_worktree(const char *path, const char *refname, } wt_refs = get_worktree_ref_store(wt); - ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb); + ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb); if (ret) goto done; @@ -517,7 +517,7 @@ static int add_worktree(const char *path, const char *refname, ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR); else - ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL); + ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL); if (ret) goto done; @@ -605,7 +605,7 @@ static void print_preparing_worktree_line(int detach, } else { struct strbuf s = STRBUF_INIT; if (!detach && !strbuf_check_branch_ref(&s, branch) && - ref_exists(s.buf)) + refs_ref_exists(get_main_ref_store(the_repository), s.buf)) fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"), branch); else { @@ -647,9 +647,9 @@ static int first_valid_ref(const char *refname UNUSED, */ static int can_use_local_refs(const struct add_opts *opts) { - if (head_ref(first_valid_ref, NULL)) { + if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; - } else if (for_each_branch_ref(first_valid_ref, NULL)) { + } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { if (!opts->quiet) { struct strbuf path = STRBUF_INIT; struct strbuf contents = STRBUF_INIT; @@ -689,7 +689,7 @@ static int can_use_remote_refs(const struct add_opts *opts) { if (!guess_remote) { return 0; - } else if (for_each_remote_ref(first_valid_ref, NULL)) { + } else if (refs_for_each_remote_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; } else if (!opts->force && remote_get(NULL)) { die(_("No local or remote refs exist despite at least one remote\n" @@ -736,18 +736,17 @@ 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) && - ref_exists(ref.buf); + refs_ref_exists(get_main_ref_store(the_repository), + ref.buf); strbuf_release(&ref); if (branch_exists) return branchname; @@ -755,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; @@ -768,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; @@ -838,7 +838,7 @@ static int add(int ac, const char **av, const char *prefix) if (!opts.force && !strbuf_check_branch_ref(&symref, new_branch) && - ref_exists(symref.buf)) + refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) die_if_checked_out(symref.buf, 0); strbuf_release(&symref); } @@ -858,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) { @@ -922,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; } @@ -974,7 +977,9 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) if (wt->is_detached) strbuf_addstr(&sb, "(detached HEAD)"); else if (wt->head_ref) { - char *ref = shorten_unambiguous_ref(wt->head_ref, 0); + char *ref = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), + wt->head_ref, + 0); strbuf_addf(&sb, "[%s]", ref); free(ref); } else diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 66e83d0ecb..8c75b4609b 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -3,7 +3,7 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_VARIABLE + #include "builtin.h" #include "config.h" #include "environment.h" @@ -44,8 +44,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags, - tree_prefix); + ret = write_index_as_tree(&oid, the_repository->index, get_index_file(), + flags, tree_prefix); switch (ret) { case 0: printf("%s\n", oid_to_hex(&oid)); |
