diff options
Diffstat (limited to 'builtin')
108 files changed, 5016 insertions, 3348 deletions
diff --git a/builtin/add.c b/builtin/add.c index f84372964c..61dd386d10 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006 Linus Torvalds */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "builtin.h" @@ -42,8 +42,8 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only) { int i, ret = 0; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr; i++) { + struct cache_entry *ce = the_index.cache[i]; int err; if (!include_sparse && @@ -55,7 +55,7 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only) continue; if (!show_only) - err = chmod_cache_entry(ce, flip); + err = chmod_index_entry(&the_index, ce, flip); else err = S_ISREG(ce->ce_mode) ? 0 : -1; @@ -88,7 +88,7 @@ static int fix_unmerged_status(struct diff_filepair *p, } static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt, void *cbdata) + struct diff_options *opt UNUSED, void *cbdata) { int i; struct update_callback_data *data = cbdata; @@ -159,8 +159,8 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) { int i, retval = 0; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr; i++) { + struct cache_entry *ce = the_index.cache[i]; if (!include_sparse && (ce_skip_worktree(ce) || @@ -172,7 +172,8 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) continue; /* do not touch non blobs */ if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) continue; - retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE); + retval |= add_file_to_index(&the_index, ce->name, + flags | ADD_CACHE_RENORMALIZE); } return retval; @@ -237,59 +238,14 @@ static int refresh(int verbose, const struct pathspec *pathspec) return ret; } -int run_add_interactive(const char *revision, const char *patch_mode, - const struct pathspec *pathspec) -{ - int status, i; - struct strvec argv = STRVEC_INIT; - int use_builtin_add_i = - git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1); - - if (use_builtin_add_i < 0 && - git_config_get_bool("add.interactive.usebuiltin", - &use_builtin_add_i)) - use_builtin_add_i = 1; - - if (use_builtin_add_i != 0) { - enum add_p_mode mode; - - if (!patch_mode) - return !!run_add_i(the_repository, pathspec); - - if (!strcmp(patch_mode, "--patch")) - mode = ADD_P_ADD; - else if (!strcmp(patch_mode, "--patch=stash")) - mode = ADD_P_STASH; - else if (!strcmp(patch_mode, "--patch=reset")) - mode = ADD_P_RESET; - else if (!strcmp(patch_mode, "--patch=checkout")) - mode = ADD_P_CHECKOUT; - else if (!strcmp(patch_mode, "--patch=worktree")) - mode = ADD_P_WORKTREE; - else - die("'%s' not supported", patch_mode); - - return !!run_add_p(the_repository, mode, revision, pathspec); - } - - strvec_push(&argv, "add--interactive"); - if (patch_mode) - strvec_push(&argv, patch_mode); - if (revision) - strvec_push(&argv, revision); - strvec_push(&argv, "--"); - for (i = 0; i < pathspec->nr; i++) - /* pass original pathspec, to be re-parsed */ - strvec_push(&argv, pathspec->items[i].original); - - status = run_command_v_opt(argv.v, RUN_GIT_CMD); - strvec_clear(&argv); - return status; -} - int interactive_add(const char **argv, const char *prefix, int patch) { struct pathspec pathspec; + int unused; + + 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.")); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | @@ -297,9 +253,10 @@ int interactive_add(const char **argv, const char *prefix, int patch) PATHSPEC_PREFIX_ORIGIN, prefix, argv); - return run_add_interactive(NULL, - patch ? "--patch" : NULL, - &pathspec); + if (patch) + return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec); + else + return !!run_add_i(the_repository, &pathspec); } static int edit_patch(int argc, const char **argv, const char *prefix) @@ -312,7 +269,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("Could not read the index")); repo_init_revisions(the_repository, &rev, prefix); @@ -544,7 +501,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); /* * Check the "pathspec '%s' did not match any files" block @@ -587,7 +544,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); - if (read_cache_preload(&pathspec) < 0) + if (repo_read_index_preload(the_repository, &pathspec, 0) < 0) die(_("index file corrupt")); die_in_unpopulated_submodule(&the_index, prefix); @@ -695,6 +652,6 @@ finish: die(_("Unable to write new index file")); dir_clear(&dir); - UNLEAK(pathspec); + clear_pathspec(&pathspec); return exit_status; } diff --git a/builtin/am.c b/builtin/am.c index 93bec62afa..5e6b237c42 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -3,11 +3,12 @@ * * Based on git-am.sh by Junio C Hamano. */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "builtin.h" #include "exec-cmd.h" +#include "hex.h" #include "parse-options.h" #include "dir.h" #include "run-command.h" @@ -117,6 +118,7 @@ struct am_state { /* various operating modes and command line options */ int interactive; + int no_verify; int threeway; int quiet; int signoff; /* enum signoff_type */ @@ -472,10 +474,12 @@ static void am_destroy(const struct am_state *state) */ static int run_applypatch_msg_hook(struct am_state *state) { - int ret; + int ret = 0; assert(state->msg); - ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL); + + if (!state->no_verify) + ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL); if (!ret) { FREE_AND_NULL(state->msg); @@ -492,24 +496,12 @@ static int run_applypatch_msg_hook(struct am_state *state) */ static int run_post_rewrite_hook(const struct am_state *state) { - struct child_process cp = CHILD_PROCESS_INIT; - const char *hook = find_hook("post-rewrite"); - int ret; + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; - if (!hook) - return 0; - - strvec_push(&cp.args, hook); - strvec_push(&cp.args, "rebase"); - - cp.in = xopen(am_path(state, "rewritten"), O_RDONLY); - cp.stdout_to_stderr = 1; - cp.trace2_hook_name = "post-rewrite"; + strvec_push(&opt.args, "rebase"); + opt.path_to_stdin = am_path(state, "rewritten"); - ret = run_command(&cp); - - close(cp.in); - return ret; + return run_hooks_opt("post-rewrite", &opt); } /** @@ -1476,6 +1468,7 @@ static int run_apply(const struct am_state *state, const char *index_file) int res, opts_left; int force_apply = 0; int options = 0; + const char **apply_argv; if (init_apply_state(&apply_state, the_repository, NULL)) BUG("init_apply_state() failed"); @@ -1483,7 +1476,14 @@ static int run_apply(const struct am_state *state, const char *index_file) strvec_push(&apply_opts, "apply"); strvec_pushv(&apply_opts, state->git_apply_opts.v); - opts_left = apply_parse_options(apply_opts.nr, apply_opts.v, + /* + * Build a copy that apply_parse_options() can rearrange. + * apply_opts.v keeps referencing the allocated strings for + * strvec_clear() to release. + */ + DUP_ARRAY(apply_argv, apply_opts.v, apply_opts.nr); + + opts_left = apply_parse_options(apply_opts.nr, apply_argv, &apply_state, &force_apply, &options, NULL); @@ -1513,14 +1513,15 @@ static int run_apply(const struct am_state *state, const char *index_file) strvec_clear(&apply_paths); strvec_clear(&apply_opts); clear_apply_state(&apply_state); + free(apply_argv); if (res) return res; if (index_file) { /* Reload index as apply_all_patches() will have modified it. */ - discard_cache(); - read_cache_from(index_file); + discard_index(&the_index); + read_index_from(&the_index, index_file, get_git_dir()); } return 0; @@ -1562,8 +1563,8 @@ 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_cache(); - read_cache_from(index_path); + discard_index(&the_index); + read_index_from(&the_index, index_path, get_git_dir()); if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL)) return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); @@ -1596,8 +1597,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa say(state, stdout, _("Falling back to patching base and 3-way merge...")); - discard_cache(); - read_cache(); + discard_index(&the_index); + repo_read_index(the_repository); /* * This is not so wrong. Depending on which base we picked, orig_tree @@ -1640,10 +1641,10 @@ static void do_commit(const struct am_state *state) const char *reflog_msg, *author, *committer = NULL; struct strbuf sb = STRBUF_INIT; - if (run_hooks("pre-applypatch")) + if (!state->no_verify && run_hooks("pre-applypatch")) exit(1); - if (write_cache_as_tree(&tree, 0, NULL)) + if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL)) die(_("git write-tree failed to write a tree")); if (!get_oid_commit("HEAD", &parent)) { @@ -1781,7 +1782,8 @@ static void am_run(struct am_state *state, int resume) unlink(am_path(state, "dirtyindex")); - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0, + NULL, NULL, NULL) < 0) die(_("unable to write index file")); if (repo_index_has_changes(the_repository, NULL, &sb)) { @@ -1930,7 +1932,7 @@ static void am_resolve(struct am_state *state, int allow_empty) } } - if (unmerged_cache()) { + if (unmerged_index(&the_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.")); @@ -1967,9 +1969,9 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) if (parse_tree(head) || parse_tree(remote)) return -1; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); - refresh_cache(REFRESH_QUIET); + refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; @@ -2007,7 +2009,7 @@ static int merge_tree(struct tree *tree) if (parse_tree(tree)) return -1; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; @@ -2045,12 +2047,12 @@ static int clean_index(const struct object_id *head, const struct object_id *rem if (!remote_tree) return error(_("Could not parse object '%s'."), oid_to_hex(remote)); - read_cache_unmerged(); + repo_read_index_unmerged(the_repository); if (fast_forward_to(head_tree, head_tree, 1)) return -1; - if (write_cache_as_tree(&index, 0, NULL)) + if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL)) return -1; index_tree = parse_tree_indirect(&index); @@ -2187,14 +2189,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode) int len; if (!is_null_oid(&state->orig_commit)) { - const char *av[4] = { "show", NULL, "--", NULL }; - char *new_oid_str; - int ret; + struct child_process cmd = CHILD_PROCESS_INIT; - av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit)); - ret = run_command_v_opt(av, RUN_GIT_CMD); - free(new_oid_str); - return ret; + strvec_pushl(&cmd.args, "show", oid_to_hex(&state->orig_commit), + "--", NULL); + cmd.git_cmd = 1; + return run_command(&cmd); } switch (sub_mode) { @@ -2301,17 +2301,6 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar return 0; } -static int git_am_config(const char *k, const char *v, void *cb) -{ - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; - - return git_default_config(k, v, NULL); -} - int cmd_am(int argc, const char **argv, const char *prefix) { struct am_state state; @@ -2331,6 +2320,8 @@ int cmd_am(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_BOOL('i', "interactive", &state.interactive, N_("run interactively")), + OPT_BOOL('n', "no-verify", &state.no_verify, + N_("bypass pre-applypatch and applypatch-msg hooks")), OPT_HIDDEN_BOOL('b', "binary", &binary, N_("historical option -- no-op")), OPT_BOOL('3', "3way", &state.threeway, @@ -2433,7 +2424,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(usage, options); - git_config(git_am_config, NULL); + git_config(git_default_config, NULL); am_state_init(&state); diff --git a/builtin/archive.c b/builtin/archive.c index 7176b041b6..d0a583ea95 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -75,13 +75,13 @@ static int run_remote_archiver(int argc, const char **argv, #define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ PARSE_OPT_KEEP_ARGV0 | \ - PARSE_OPT_KEEP_UNKNOWN | \ + PARSE_OPT_KEEP_UNKNOWN_OPT | \ PARSE_OPT_NO_INTERNAL_HELP ) int cmd_archive(int argc, const char **argv, const char *prefix) { const char *exec = "git-upload-archive"; - const char *output = NULL; + char *output = NULL; const char *remote = NULL; struct option local_opts[] = { OPT_FILENAME('o', "output", &output, @@ -106,5 +106,6 @@ 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); } diff --git a/builtin/bisect--helper.c b/builtin/bisect.c index 8a052c7111..c64c8d715a 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "hex.h" #include "parse-options.h" #include "bisect.h" #include "refs.h" @@ -15,23 +16,44 @@ static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") -static GIT_PATH_FUNC(git_path_head_name, "head-name") static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") -static const char * const git_bisect_helper_usage[] = { - N_("git bisect--helper --bisect-reset [<commit>]"), - "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]", - N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]" - " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"), - "git bisect--helper --bisect-next", - N_("git bisect--helper --bisect-state (bad|new) [<rev>]"), - N_("git bisect--helper --bisect-state (good|old) [<rev>...]"), - N_("git bisect--helper --bisect-replay <filename>"), - N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"), - "git bisect--helper --bisect-visualize", - N_("git bisect--helper --bisect-run <cmd>..."), +#define BUILTIN_GIT_BISECT_START_USAGE \ + N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \ + " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \ + " [<pathspec>...]") +#define BUILTIN_GIT_BISECT_STATE_USAGE \ + N_("git bisect (good|bad) [<rev>...]") +#define BUILTIN_GIT_BISECT_TERMS_USAGE \ + "git bisect terms [--term-good | --term-bad]" +#define BUILTIN_GIT_BISECT_SKIP_USAGE \ + N_("git bisect skip [(<rev>|<range>)...]") +#define BUILTIN_GIT_BISECT_NEXT_USAGE \ + "git bisect next" +#define BUILTIN_GIT_BISECT_RESET_USAGE \ + N_("git bisect reset [<commit>]") +#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \ + "git bisect visualize" +#define BUILTIN_GIT_BISECT_REPLAY_USAGE \ + N_("git bisect replay <logfile>") +#define BUILTIN_GIT_BISECT_LOG_USAGE \ + "git bisect log" +#define BUILTIN_GIT_BISECT_RUN_USAGE \ + N_("git bisect run <cmd>...") + +static const char * const git_bisect_usage[] = { + BUILTIN_GIT_BISECT_START_USAGE, + BUILTIN_GIT_BISECT_STATE_USAGE, + BUILTIN_GIT_BISECT_TERMS_USAGE, + BUILTIN_GIT_BISECT_SKIP_USAGE, + BUILTIN_GIT_BISECT_NEXT_USAGE, + BUILTIN_GIT_BISECT_RESET_USAGE, + BUILTIN_GIT_BISECT_VISUALIZE_USAGE, + BUILTIN_GIT_BISECT_REPLAY_USAGE, + BUILTIN_GIT_BISECT_LOG_USAGE, + BUILTIN_GIT_BISECT_RUN_USAGE, NULL }; @@ -220,18 +242,18 @@ static int bisect_reset(const char *commit) } if (!ref_exists("BISECT_HEAD")) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&argv, "checkout", branch.buf, "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "checkout", "--ignore-other-worktrees", + branch.buf, "--", NULL); + if (run_command(&cmd)) { error(_("could not check out original" " HEAD '%s'. Try 'git bisect" " reset <commit>'."), branch.buf); strbuf_release(&branch); - strvec_clear(&argv); return -1; } - strvec_clear(&argv); } strbuf_release(&branch); @@ -329,8 +351,9 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd) return 0; } -static int inc_nr(const char *refname, const struct object_id *oid, - int flag, void *cb_data) +static int inc_nr(const char *refname UNUSED, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cb_data) { unsigned int *nr = (unsigned int *)cb_data; (*nr)++; @@ -518,7 +541,7 @@ finish: } static int add_bisect_ref(const char *refname, const struct object_id *oid, - int flags, void *cb) + int flags UNUSED, void *cb) { struct add_bisect_ref_data *data = cb; @@ -656,7 +679,8 @@ static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char return bisect_next(terms, prefix); } -static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc) +static enum bisect_error bisect_start(struct bisect_terms *terms, int argc, + const char **argv) { int no_checkout = 0; int first_parent_only = 0; @@ -764,11 +788,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a strbuf_read_file(&start_head, git_path_bisect_start(), 0); strbuf_trim(&start_head); if (!no_checkout) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&argv, "checkout", start_head.buf, + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "checkout", start_head.buf, "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + if (run_command(&cmd)) { res = error(_("checking out '%s' failed." " Try 'git bisect start " "<valid-branch>'."), @@ -784,13 +809,6 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a strbuf_addstr(&start_head, oid_to_hex(&head_oid)); } else if (!get_oid(head, &head_oid) && skip_prefix(head, "refs/heads/", &head)) { - /* - * This error message should only be triggered by - * cogito usage, and cogito users should understand - * it relates to cg-seek. - */ - if (!is_empty_or_missing_file(git_path_head_name())) - return error(_("won't bisect on cg-seek'ed tree")); strbuf_addstr(&start_head, head); } else { return error(_("bad HEAD - strange symbolic ref")); @@ -885,13 +903,13 @@ static int bisect_autostart(struct bisect_terms *terms) yesno = git_prompt(_("Do you want me to do it for you " "[Y/n]? "), PROMPT_ECHO); res = tolower(*yesno) == 'n' ? - -1 : bisect_start(terms, empty_strvec, 0); + -1 : bisect_start(terms, 0, empty_strvec); return res; } -static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv, - int argc) +static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, + const char **argv) { const char *state; int i, verify_expected = 1; @@ -1010,7 +1028,7 @@ static int process_replay_line(struct bisect_terms *terms, struct strbuf *line) struct strvec argv = STRVEC_INIT; int res; sq_dequote_to_strvec(rev, &argv); - res = bisect_start(terms, argv.v, argv.nr); + res = bisect_start(terms, argv.nr, argv.v); strvec_clear(&argv); return res; } @@ -1060,7 +1078,8 @@ static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *f return bisect_auto_next(terms, NULL); } -static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc) +static enum bisect_error bisect_skip(struct bisect_terms *terms, int argc, + const char **argv) { int i; enum bisect_error res; @@ -1090,59 +1109,68 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar strvec_push(&argv_state, argv[i]); } } - res = bisect_state(terms, argv_state.v, argv_state.nr); + res = bisect_state(terms, argv_state.nr, argv_state.v); strvec_clear(&argv_state); return res; } -static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc) +static int bisect_visualize(struct bisect_terms *terms, int argc, + const char **argv) { - struct strvec args = STRVEC_INIT; - int flags = RUN_COMMAND_NO_STDIN, res = 0; + struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf sb = STRBUF_INIT; if (bisect_next_check(terms, NULL) != 0) return BISECT_FAILED; + cmd.no_stdin = 1; if (!argc) { if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") || getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) { - strvec_push(&args, "gitk"); + strvec_push(&cmd.args, "gitk"); } else { - strvec_push(&args, "log"); - flags |= RUN_GIT_CMD; + strvec_push(&cmd.args, "log"); + cmd.git_cmd = 1; } } else { if (argv[0][0] == '-') { - strvec_push(&args, "log"); - flags |= RUN_GIT_CMD; + strvec_push(&cmd.args, "log"); + cmd.git_cmd = 1; } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git")) - flags |= RUN_GIT_CMD; + cmd.git_cmd = 1; - strvec_pushv(&args, argv); + strvec_pushv(&cmd.args, argv); } - strvec_pushl(&args, "--bisect", "--", NULL); + strvec_pushl(&cmd.args, "--bisect", "--", NULL); strbuf_read_file(&sb, git_path_bisect_names(), 0); - sq_dequote_to_strvec(sb.buf, &args); + sq_dequote_to_strvec(sb.buf, &cmd.args); strbuf_release(&sb); - res = run_command_v_opt(args.v, flags); - strvec_clear(&args); - return res; + return run_command(&cmd); } -static int get_first_good(const char *refname, const struct object_id *oid, - int flag, void *cb_data) +static int get_first_good(const char *refname UNUSED, + const struct object_id *oid, + int flag UNUSED, void *cb_data) { oidcpy(cb_data, oid); return 1; } -static int verify_good(const struct bisect_terms *terms, - const char **quoted_argv) +static int do_bisect_run(const char *command) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + + printf(_("running %s\n"), command); + cmd.use_shell = 1; + strvec_push(&cmd.args, command); + return run_command(&cmd); +} + +static int verify_good(const struct bisect_terms *terms, const char *command) { int rc; enum bisect_error res; @@ -1162,8 +1190,7 @@ static int verify_good(const struct bisect_terms *terms, if (res != BISECT_OK) return -1; - printf(_("running %s\n"), quoted_argv[0]); - rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL); + rc = do_bisect_run(command); res = bisect_checkout(¤t_rev, no_checkout); if (res != BISECT_OK) @@ -1172,11 +1199,10 @@ static int verify_good(const struct bisect_terms *terms, return rc; } -static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) +static int bisect_run(struct bisect_terms *terms, int argc, const char **argv) { int res = BISECT_OK; struct strbuf command = STRBUF_INIT; - struct strvec run_args = STRVEC_INIT; const char *new_state; int temporary_stdout_fd, saved_stdout; int is_first_run = 1; @@ -1184,18 +1210,15 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) if (bisect_next_check(terms, NULL)) return BISECT_FAILED; - if (argc) - sq_quote_argv(&command, argv); - else { + if (!argc) { error(_("bisect run failed: no command provided.")); return BISECT_FAILED; } - strvec_push(&run_args, command.buf); - + sq_quote_argv(&command, argv); + strbuf_ltrim(&command); while (1) { - printf(_("running %s\n"), command.buf); - res = run_command_v_opt(run_args.v, RUN_USING_SHELL); + res = do_bisect_run(command.buf); /* * Exit code 126 and 127 can either come from the shell @@ -1205,10 +1228,10 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) * missing or non-executable script. */ if (is_first_run && (res == 126 || res == 127)) { - int rc = verify_good(terms, run_args.v); + int rc = verify_good(terms, command.buf); is_first_run = 0; - if (rc < 0) { - error(_("unable to verify '%s' on good" + if (rc < 0 || 128 <= rc) { + error(_("unable to verify %s on good" " revision"), command.buf); res = BISECT_FAILED; break; @@ -1223,7 +1246,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) if (res < 0 || 128 <= res) { error(_("bisect run failed: exit code %d from" - " '%s' is < 0 or >= 128"), res, command.buf); + " %s is < 0 or >= 128"), res, command.buf); break; } @@ -1245,7 +1268,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) saved_stdout = dup(1); dup2(temporary_stdout_fd, 1); - res = bisect_state(terms, &new_state, 1); + res = bisect_state(terms, 1, &new_state); fflush(stdout); dup2(saved_stdout, 1); @@ -1257,14 +1280,14 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) if (res == BISECT_ONLY_SKIPPED_LEFT) error(_("bisect run cannot continue any more")); else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) { - printf(_("bisect run success")); + puts(_("bisect run success")); res = BISECT_OK; } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { - printf(_("bisect found first bad commit")); + puts(_("bisect found first bad commit")); res = BISECT_OK; } else if (res) { - error(_("bisect run failed: 'git bisect--helper --bisect-state" - " %s' exited with error code %d"), new_state, res); + error(_("bisect run failed: 'git bisect %s'" + " exited with error code %d"), new_state, res); } else { continue; } @@ -1272,126 +1295,147 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) } strbuf_release(&command); - strvec_clear(&run_args); return res; } -int cmd_bisect__helper(int argc, const char **argv, const char *prefix) +static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED) +{ + if (argc > 1) + return error(_("'%s' requires either no argument or a commit"), + "git bisect reset"); + return bisect_reset(argc ? argv[0] : NULL); +} + +static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED) +{ + int res; + struct bisect_terms terms = { 0 }; + + if (argc > 1) + return error(_("'%s' requires 0 or 1 argument"), + "git bisect terms"); + res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL); + free_terms(&terms); + return res; +} + +static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED) +{ + int res; + struct bisect_terms terms = { 0 }; + + set_terms(&terms, "bad", "good"); + res = bisect_start(&terms, argc, argv); + free_terms(&terms); + return res; +} + +static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix) +{ + int res; + struct bisect_terms terms = { 0 }; + + if (argc) + return error(_("'%s' requires 0 arguments"), + "git bisect next"); + get_terms(&terms); + res = bisect_next(&terms, prefix); + free_terms(&terms); + return res; +} + +static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED) +{ + return bisect_log(); +} + +static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED) +{ + int res; + struct bisect_terms terms = { 0 }; + + if (argc != 1) + return error(_("no logfile given")); + set_terms(&terms, "bad", "good"); + res = bisect_replay(&terms, argv[0]); + free_terms(&terms); + return res; +} + +static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED) +{ + int res; + struct bisect_terms terms = { 0 }; + + set_terms(&terms, "bad", "good"); + get_terms(&terms); + res = bisect_skip(&terms, argc, argv); + free_terms(&terms); + return res; +} + +static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED) { - enum { - BISECT_RESET = 1, - BISECT_NEXT_CHECK, - BISECT_TERMS, - BISECT_START, - BISECT_AUTOSTART, - BISECT_NEXT, - BISECT_STATE, - BISECT_LOG, - BISECT_REPLAY, - BISECT_SKIP, - BISECT_VISUALIZE, - BISECT_RUN, - } cmdmode = 0; - int res = 0, nolog = 0; + int res; + struct bisect_terms terms = { 0 }; + + get_terms(&terms); + res = bisect_visualize(&terms, argc, argv); + free_terms(&terms); + return res; +} + +static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED) +{ + int res; + struct bisect_terms terms = { 0 }; + + if (!argc) + return error(_("'%s' failed: no command provided."), "git bisect run"); + get_terms(&terms); + res = bisect_run(&terms, argc, argv); + free_terms(&terms); + return res; +} + +int cmd_bisect(int argc, const char **argv, const char *prefix) +{ + int res = 0; + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { - OPT_CMDMODE(0, "bisect-reset", &cmdmode, - N_("reset the bisection state"), BISECT_RESET), - OPT_CMDMODE(0, "bisect-next-check", &cmdmode, - N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK), - OPT_CMDMODE(0, "bisect-terms", &cmdmode, - N_("print out the bisect terms"), BISECT_TERMS), - OPT_CMDMODE(0, "bisect-start", &cmdmode, - N_("start the bisect session"), BISECT_START), - OPT_CMDMODE(0, "bisect-next", &cmdmode, - N_("find the next bisection commit"), BISECT_NEXT), - OPT_CMDMODE(0, "bisect-state", &cmdmode, - N_("mark the state of ref (or refs)"), BISECT_STATE), - OPT_CMDMODE(0, "bisect-log", &cmdmode, - N_("list the bisection steps so far"), BISECT_LOG), - OPT_CMDMODE(0, "bisect-replay", &cmdmode, - N_("replay the bisection process from the given file"), BISECT_REPLAY), - OPT_CMDMODE(0, "bisect-skip", &cmdmode, - N_("skip some commits for checkout"), BISECT_SKIP), - OPT_CMDMODE(0, "bisect-visualize", &cmdmode, - N_("visualize the bisection"), BISECT_VISUALIZE), - OPT_CMDMODE(0, "bisect-run", &cmdmode, - N_("use <cmd>... to automatically bisect"), BISECT_RUN), - OPT_BOOL(0, "no-log", &nolog, - N_("no log for BISECT_WRITE")), + OPT_SUBCOMMAND("reset", &fn, cmd_bisect__reset), + OPT_SUBCOMMAND("terms", &fn, cmd_bisect__terms), + OPT_SUBCOMMAND("start", &fn, cmd_bisect__start), + OPT_SUBCOMMAND("next", &fn, cmd_bisect__next), + OPT_SUBCOMMAND("log", &fn, cmd_bisect__log), + OPT_SUBCOMMAND("replay", &fn, cmd_bisect__replay), + OPT_SUBCOMMAND("skip", &fn, cmd_bisect__skip), + OPT_SUBCOMMAND("visualize", &fn, cmd_bisect__visualize), + OPT_SUBCOMMAND("view", &fn, cmd_bisect__visualize), + OPT_SUBCOMMAND("run", &fn, cmd_bisect__run), OPT_END() }; - struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL }; + argc = parse_options(argc, argv, prefix, options, git_bisect_usage, + PARSE_OPT_SUBCOMMAND_OPTIONAL); - argc = parse_options(argc, argv, prefix, options, - git_bisect_helper_usage, - PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN); + if (!fn) { + struct bisect_terms terms = { 0 }; - if (!cmdmode) - usage_with_options(git_bisect_helper_usage, options); + if (!argc) + usage_msg_opt(_("need a command"), git_bisect_usage, options); - switch (cmdmode) { - case BISECT_RESET: - if (argc > 1) - return error(_("--bisect-reset requires either no argument or a commit")); - res = bisect_reset(argc ? argv[0] : NULL); - break; - case BISECT_TERMS: - if (argc > 1) - return error(_("--bisect-terms requires 0 or 1 argument")); - res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL); - break; - case BISECT_START: - set_terms(&terms, "bad", "good"); - res = bisect_start(&terms, argv, argc); - break; - case BISECT_NEXT: - if (argc) - return error(_("--bisect-next requires 0 arguments")); - get_terms(&terms); - res = bisect_next(&terms, prefix); - break; - case BISECT_STATE: - set_terms(&terms, "bad", "good"); - get_terms(&terms); - res = bisect_state(&terms, argv, argc); - break; - case BISECT_LOG: - if (argc) - return error(_("--bisect-log requires 0 arguments")); - res = bisect_log(); - break; - case BISECT_REPLAY: - if (argc != 1) - return error(_("no logfile given")); - set_terms(&terms, "bad", "good"); - res = bisect_replay(&terms, argv[0]); - break; - case BISECT_SKIP: set_terms(&terms, "bad", "good"); get_terms(&terms); - res = bisect_skip(&terms, argv, argc); - break; - case BISECT_VISUALIZE: - get_terms(&terms); - res = bisect_visualize(&terms, argv, argc); - break; - case BISECT_RUN: - if (!argc) - return error(_("bisect run failed: no command provided.")); - get_terms(&terms); - res = bisect_run(&terms, argv, argc); - break; - default: - BUG("unknown subcommand %d", cmdmode); + if (check_and_set_terms(&terms, argv[0])) + usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage, + options, argv[0]); + res = bisect_state(&terms, argc, argv); + free_terms(&terms); + } else { + argc--; + argv++; + res = fn(argc, argv, prefix); } - free_terms(&terms); - - /* - * Handle early success - * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all - */ - if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND)) - res = BISECT_OK; - return -res; + return is_bisect_success(res) ? 0 : -res; } diff --git a/builtin/blame.c b/builtin/blame.c index 02e39420b6..fdd9f0c0fc 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -5,10 +5,12 @@ * See COPYING for licensing conditions */ -#include "cache.h" +#include "git-compat-util.h" +#include "alloc.h" #include "config.h" #include "color.h" #include "builtin.h" +#include "hex.h" #include "repository.h" #include "commit.h" #include "diff.h" @@ -30,6 +32,7 @@ #include "tag.h" static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); +static char annotate_usage[] = N_("git annotate [<options>] [<rev-opts>] [<rev>] [--] <file>"); static const char *blame_opt_usage[] = { blame_usage, @@ -38,6 +41,13 @@ static const char *blame_opt_usage[] = { NULL }; +static const char *annotate_opt_usage[] = { + annotate_usage, + "", + N_("<rev-opts> are documented in git-rev-list(1)"), + NULL +}; + static int longest_file; static int longest_author; static int max_orig_digits; @@ -899,6 +909,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) 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; setup_default_color_by_age(); git_config(git_blame_config, &output_option); @@ -914,12 +926,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_options_start(&ctx, argc, argv, prefix, options, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); for (;;) { - switch (parse_options_step(&ctx, options, blame_opt_usage)) { + switch (parse_options_step(&ctx, options, opt_usage)) { case PARSE_OPT_NON_OPTION: case PARSE_OPT_UNKNOWN: break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: + case PARSE_OPT_SUBCOMMAND: exit(129); case PARSE_OPT_COMPLETE: exit(0); @@ -933,7 +946,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) ctx.argv[0] = "--children"; reverse = 1; } - parse_revision_opt(&revs, &ctx, options, blame_opt_usage); + parse_revision_opt(&revs, &ctx, options, opt_usage); } parse_done: revision_opts_finish(&revs); @@ -1039,7 +1052,7 @@ parse_done: switch (argc - dashdash_pos - 1) { case 2: /* (1b) */ if (argc != 4) - usage_with_options(blame_opt_usage, options); + usage_with_options(opt_usage, options); /* reorder for the new way: <rev> -- <path> */ argv[1] = argv[3]; argv[3] = argv[2]; @@ -1050,11 +1063,11 @@ parse_done: argv[argc] = NULL; break; default: - usage_with_options(blame_opt_usage, options); + usage_with_options(opt_usage, options); } } else { if (argc < 2) - usage_with_options(blame_opt_usage, options); + usage_with_options(opt_usage, options); if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */ path = add_prefix(prefix, argv[1]); argv[1] = argv[2]; @@ -1112,7 +1125,7 @@ parse_done: nth_line_cb, &sb, lno, anchor, &bottom, &top, sb.path, the_repository->index)) - usage(blame_usage); + usage(str_usage); if ((!lno && (top || bottom)) || lno < bottom) die(Q_("file %s has only %lu line", "file %s has only %lu lines", diff --git a/builtin/branch.c b/builtin/branch.c index 5d00d0b8d3..f63fd45edb 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -150,7 +150,7 @@ static int branch_merged(int kind, const char *name, if (!reference_rev) reference_rev = head_rev; - merged = in_merge_bases(rev, reference_rev); + merged = reference_rev ? in_merge_bases(rev, reference_rev) : 0; /* * After the safety valve is fully redefined to "check with @@ -160,7 +160,7 @@ static int branch_merged(int kind, const char *name, * a gentle reminder is in order. */ if ((head_rev != reference_rev) && - in_merge_bases(rev, head_rev) != merged) { + (head_rev ? in_merge_bases(rev, head_rev) : 0) != merged) { if (merged) warning(_("deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD."), @@ -204,7 +204,6 @@ static void delete_branch_config(const char *branchname) static int delete_branches(int argc, const char **argv, int force, int kinds, int quiet) { - struct worktree **worktrees; struct commit *head_rev = NULL; struct object_id oid; char *name = NULL; @@ -236,13 +235,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } branch_name_pos = strcspn(fmt, "%"); - if (!force) { + if (!force) head_rev = lookup_commit_reference(the_repository, &head_oid); - if (!head_rev) - die(_("Couldn't look up commit object for HEAD")); - } - - worktrees = get_worktrees(); for (i = 0; i < argc; i++, strbuf_reset(&bname)) { char *target = NULL; @@ -253,12 +247,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, name = mkpathdup(fmt, bname.buf); if (kinds == FILTER_REFS_BRANCHES) { - const struct worktree *wt = - find_shared_symref(worktrees, "HEAD", name); - if (wt) { + const char *path; + if ((path = branch_checked_out(name))) { error(_("Cannot delete branch '%s' " "checked out at '%s'"), - bname.buf, wt->path); + bname.buf, path); ret = 1; continue; } @@ -315,7 +308,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, free(name); strbuf_release(&bname); - free_worktrees(worktrees); return ret; } @@ -525,13 +517,6 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int const char *interpreted_newname = NULL; int recovery = 0; - if (!oldname) { - if (copy) - die(_("cannot copy the current branch while not on any.")); - else - die(_("cannot rename the current branch while not on any.")); - } - if (strbuf_check_branch_ref(&oldref, oldname)) { /* * Bad name --- this could be an attempt to rename a @@ -543,6 +528,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int die(_("Invalid branch name: '%s'"), oldname); } + if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) { + if (copy && !strcmp(head, oldname)) + die(_("No commit on branch '%s' yet."), oldname); + else + die(_("No branch named '%s'."), oldname); + } + /* * A command like "git branch -M currentbranch currentbranch" cannot * cause the worktree to become inconsistent with HEAD, so allow it. @@ -589,13 +581,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int strbuf_release(&logmsg); strbuf_addf(&oldsection, "branch.%s", interpreted_oldname); - strbuf_release(&oldref); strbuf_addf(&newsection, "branch.%s", interpreted_newname); - strbuf_release(&newref); if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0) die(_("Branch is renamed, but update of config-file failed")); - if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0) + if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0) die(_("Branch is copied, but update of config-file failed")); + strbuf_release(&oldref); + strbuf_release(&newref); strbuf_release(&oldsection); strbuf_release(&newsection); } @@ -604,10 +596,11 @@ static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION") static int edit_branch_description(const char *branch_name) { + int exists; struct strbuf buf = STRBUF_INIT; struct strbuf name = STRBUF_INIT; - read_branch_desc(&buf, branch_name); + exists = !read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); strbuf_commented_addf(&buf, @@ -624,7 +617,8 @@ static int edit_branch_description(const char *branch_name) strbuf_stripspace(&buf, 1); strbuf_addf(&name, "branch.%s.description", branch_name); - git_config_set(name.buf, buf.len ? buf.buf : NULL); + if (buf.len || exists) + git_config_set(name.buf, buf.len ? buf.buf : NULL); strbuf_release(&name); strbuf_release(&buf); @@ -796,53 +790,56 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } else if (edit_description) { const char *branch_name; struct strbuf branch_ref = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + int ret = 1; /* assume failure */ if (!argc) { if (filter.detached) die(_("Cannot give description to detached HEAD")); branch_name = head; - } else if (argc == 1) - branch_name = argv[0]; - else + } else if (argc == 1) { + strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL); + branch_name = buf.buf; + } else { die(_("cannot edit description of more than one branch")); + } strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); - if (!ref_exists(branch_ref.buf)) { - strbuf_release(&branch_ref); - - if (!argc) - return error(_("No commit on branch '%s' yet."), - branch_name); - else - return error(_("No branch named '%s'."), - branch_name); - } + if (!ref_exists(branch_ref.buf)) + error((!argc || !strcmp(head, branch_name)) + ? _("No commit on branch '%s' yet.") + : _("No branch named '%s'."), + branch_name); + else if (!edit_branch_description(branch_name)) + ret = 0; /* happy */ + strbuf_release(&branch_ref); + strbuf_release(&buf); - if (edit_branch_description(branch_name)) - return 1; - } else if (copy) { - if (!argc) - die(_("branch name required")); - else if (argc == 1) - copy_or_rename_branch(head, argv[0], 1, copy > 1); - else if (argc == 2) - copy_or_rename_branch(argv[0], argv[1], 1, copy > 1); - else - die(_("too many branches for a copy operation")); - } else if (rename) { + return ret; + } else if (copy || rename) { if (!argc) die(_("branch name required")); + else if ((argc == 1) && filter.detached) + die(copy? _("cannot copy the current branch while not on any.") + : _("cannot rename the current branch while not on any.")); else if (argc == 1) - copy_or_rename_branch(head, argv[0], 0, rename > 1); + copy_or_rename_branch(head, argv[0], copy, copy + rename > 1); else if (argc == 2) - copy_or_rename_branch(argv[0], argv[1], 0, rename > 1); + copy_or_rename_branch(argv[0], argv[1], copy, copy + rename > 1); else - die(_("too many arguments for a rename operation")); + die(copy? _("too many branches for a copy operation") + : _("too many arguments for a rename operation")); } else if (new_upstream) { - struct branch *branch = branch_get(argv[0]); + struct branch *branch; + struct strbuf buf = STRBUF_INIT; - if (argc > 1) + if (!argc) + branch = branch_get(NULL); + else if (argc == 1) { + strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL); + branch = branch_get(buf.buf); + } else die(_("too many arguments to set new upstream")); if (!branch) { @@ -853,17 +850,26 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("no such branch '%s'"), argv[0]); } - if (!ref_exists(branch->refname)) + if (!ref_exists(branch->refname)) { + if (!argc || !strcmp(head, branch->name)) + die(_("No commit on branch '%s' yet."), branch->name); die(_("branch '%s' does not exist"), branch->name); + } dwim_and_setup_tracking(the_repository, branch->name, new_upstream, BRANCH_TRACK_OVERRIDE, quiet); + strbuf_release(&buf); } else if (unset_upstream) { - struct branch *branch = branch_get(argv[0]); + struct branch *branch; struct strbuf buf = STRBUF_INIT; - if (argc > 1) + if (!argc) + branch = branch_get(NULL); + else if (argc == 1) { + strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL); + branch = branch_get(buf.buf); + } else die(_("too many arguments to unset upstream")); if (!branch) { @@ -876,6 +882,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch_has_merge_config(branch)) die(_("Branch '%s' has no upstream information"), branch->name); + strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", branch->name); git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_reset(&buf); diff --git a/builtin/bugreport.c b/builtin/bugreport.c index 9de32bc96e..5bc254be80 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -5,6 +5,7 @@ #include "compat/compiler.h" #include "hook.h" #include "hook-list.h" +#include "diagnose.h" static void get_system_info(struct strbuf *sys_info) @@ -59,7 +60,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit) } static const char * const bugreport_usage[] = { - N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"), + N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n" + " [--diagnose[=<mode>]]"), NULL }; @@ -98,16 +100,22 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) int report = -1; time_t now = time(NULL); struct tm tm; + enum diagnose_mode diagnose = DIAGNOSE_NONE; char *option_output = NULL; char *option_suffix = "%Y-%m-%d-%H%M"; const char *user_relative_path = NULL; char *prefixed_filename; + size_t output_path_len; + int ret; const struct option bugreport_options[] = { + OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"), + N_("create an additional zip archive of detailed diagnostics (default 'stats')"), + PARSE_OPT_OPTARG, option_parse_diagnose), OPT_STRING('o', "output-directory", &option_output, N_("path"), - N_("specify a destination for the bugreport file")), + N_("specify a destination for the bugreport file(s)")), OPT_STRING('s', "suffix", &option_suffix, N_("format"), - N_("specify a strftime format suffix for the filename")), + N_("specify a strftime format suffix for the filename(s)")), OPT_END() }; @@ -119,6 +127,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) option_output ? option_output : ""); strbuf_addstr(&report_path, prefixed_filename); strbuf_complete(&report_path, '/'); + output_path_len = report_path.len; strbuf_addstr(&report_path, "git-bugreport-"); strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0); @@ -133,6 +142,20 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) report_path.buf); } + /* Prepare diagnostics, if requested */ + if (diagnose != DIAGNOSE_NONE) { + struct strbuf zip_path = STRBUF_INIT; + strbuf_add(&zip_path, report_path.buf, output_path_len); + strbuf_addstr(&zip_path, "git-diagnostics-"); + strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0); + strbuf_addstr(&zip_path, ".zip"); + + if (create_diagnostics_archive(&zip_path, diagnose)) + die_errno(_("unable to create diagnostics archive %s"), zip_path.buf); + + strbuf_release(&zip_path); + } + /* Prepare the report contents */ get_bug_template(&buffer); @@ -160,7 +183,9 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) user_relative_path); free(prefixed_filename); - UNLEAK(buffer); - UNLEAK(report_path); - return !!launch_editor(report_path.buf, NULL, NULL); + strbuf_release(&buffer); + + ret = !!launch_editor(report_path.buf, NULL, NULL); + strbuf_release(&report_path); + return ret; } diff --git a/builtin/bundle.c b/builtin/bundle.c index 2adad545a2..666f01bccd 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -11,32 +11,42 @@ * bundle supporting "fetch", "pull", and "ls-remote". */ -static const char * const builtin_bundle_usage[] = { - N_("git bundle create [<options>] <file> <git-rev-list args>"), - N_("git bundle verify [<options>] <file>"), - N_("git bundle list-heads <file> [<refname>...]"), - N_("git bundle unbundle <file> [<refname>...]"), - NULL +#define BUILTIN_BUNDLE_CREATE_USAGE \ + N_("git bundle create [-q | --quiet | --progress]\n" \ + " [--version=<version>] <file> <git-rev-list-args>") +#define BUILTIN_BUNDLE_VERIFY_USAGE \ + N_("git bundle verify [-q | --quiet] <file>") +#define BUILTIN_BUNDLE_LIST_HEADS_USAGE \ + N_("git bundle list-heads <file> [<refname>...]") +#define BUILTIN_BUNDLE_UNBUNDLE_USAGE \ + N_("git bundle unbundle [--progress] <file> [<refname>...]") + +static char const * const builtin_bundle_usage[] = { + BUILTIN_BUNDLE_CREATE_USAGE, + BUILTIN_BUNDLE_VERIFY_USAGE, + BUILTIN_BUNDLE_LIST_HEADS_USAGE, + BUILTIN_BUNDLE_UNBUNDLE_USAGE, + NULL, }; static const char * const builtin_bundle_create_usage[] = { - N_("git bundle create [<options>] <file> <git-rev-list args>"), - NULL + BUILTIN_BUNDLE_CREATE_USAGE, + NULL }; static const char * const builtin_bundle_verify_usage[] = { - N_("git bundle verify [<options>] <file>"), - NULL + BUILTIN_BUNDLE_VERIFY_USAGE, + NULL }; static const char * const builtin_bundle_list_heads_usage[] = { - N_("git bundle list-heads <file> [<refname>...]"), - NULL + BUILTIN_BUNDLE_LIST_HEADS_USAGE, + NULL }; static const char * const builtin_bundle_unbundle_usage[] = { - N_("git bundle unbundle <file> [<refname>...]"), - NULL + BUILTIN_BUNDLE_UNBUNDLE_USAGE, + NULL }; static int parse_options_cmd_bundle(int argc, @@ -45,17 +55,16 @@ static int parse_options_cmd_bundle(int argc, const char * const usagestr[], const struct option options[], char **bundle_file) { - int newargc; - newargc = parse_options(argc, argv, NULL, options, usagestr, + argc = parse_options(argc, argv, NULL, options, usagestr, PARSE_OPT_STOP_AT_NON_OPTION); - if (argc < 1) - usage_with_options(usagestr, options); - *bundle_file = prefix_filename(prefix, argv[0]); - return newargc; + if (!argc) + usage_msg_opt(_("need a <file> argument"), usagestr, options); + *bundle_file = prefix_filename_except_for_dash(prefix, argv[0]); + return argc; } static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { - int all_progress_implied = 0; + int all_progress_implied = 1; int progress = isatty(STDERR_FILENO); struct strvec pack_opts; int version = -1; @@ -65,11 +74,12 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { N_("do not show progress meter"), 0), OPT_SET_INT(0, "progress", &progress, N_("show progress meter"), 1), - OPT_SET_INT(0, "all-progress", &progress, - N_("show progress meter during object writing phase"), 2), - OPT_BOOL(0, "all-progress-implied", - &all_progress_implied, - N_("similar to --all-progress when progress meter is shown")), + OPT_SET_INT_F(0, "all-progress", &progress, + N_("historical; same as --progress"), 2, + PARSE_OPT_HIDDEN), + OPT_HIDDEN_BOOL(0, "all-progress-implied", + &all_progress_implied, + N_("historical; does nothing")), OPT_INTEGER(0, "version", &version, N_("specify bundle format version")), OPT_END() @@ -98,6 +108,23 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { return ret; } +/* + * Similar to read_bundle_header(), but handle "-" as stdin. + */ +static int open_bundle(const char *path, struct bundle_header *header, + const char **name) +{ + if (!strcmp(path, "-")) { + if (name) + *name = "<stdin>"; + return read_bundle_header_fd(0, header, "<stdin>"); + } + + if (name) + *name = path; + return read_bundle_header(path, header); +} + static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; @@ -109,22 +136,24 @@ static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { OPT_END() }; char *bundle_file; + const char *name; argc = parse_options_cmd_bundle(argc, argv, prefix, builtin_bundle_verify_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) { ret = 1; goto cleanup; } close(bundle_fd); - if (verify_bundle(the_repository, &header, !quiet)) { + if (verify_bundle(the_repository, &header, + quiet ? VERIFY_BUNDLE_QUIET : VERIFY_BUNDLE_VERBOSE)) { ret = 1; goto cleanup; } - fprintf(stderr, _("%s is okay\n"), bundle_file); + fprintf(stderr, _("%s is okay\n"), name); ret = 0; cleanup: free(bundle_file); @@ -145,7 +174,7 @@ static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix builtin_bundle_list_heads_usage, options, &bundle_file); /* bundle internals use argv[1] as further parameters */ - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { ret = 1; goto cleanup; } @@ -175,7 +204,7 @@ 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 ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) { + if ((bundle_fd = open_bundle(bundle_file, &header, NULL)) < 0) { ret = 1; goto cleanup; } @@ -185,7 +214,7 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) strvec_pushl(&extra_index_pack_args, "-v", "--progress-title", _("Unbundling objects"), NULL); ret = !!unbundle(the_repository, &header, bundle_fd, - &extra_index_pack_args) || + &extra_index_pack_args, 0) || list_bundle_refs(&header, argc, argv); bundle_header_release(&header); cleanup: @@ -195,30 +224,19 @@ cleanup: int cmd_bundle(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("create", &fn, cmd_bundle_create), + OPT_SUBCOMMAND("verify", &fn, cmd_bundle_verify), + OPT_SUBCOMMAND("list-heads", &fn, cmd_bundle_list_heads), + OPT_SUBCOMMAND("unbundle", &fn, cmd_bundle_unbundle), OPT_END() }; - int result; argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + 0); packet_trace_identity("bundle"); - if (argc < 2) - usage_with_options(builtin_bundle_usage, options); - - else if (!strcmp(argv[0], "create")) - result = cmd_bundle_create(argc, argv, prefix); - else if (!strcmp(argv[0], "verify")) - result = cmd_bundle_verify(argc, argv, prefix); - else if (!strcmp(argv[0], "list-heads")) - result = cmd_bundle_list_heads(argc, argv, prefix); - else if (!strcmp(argv[0], "unbundle")) - result = cmd_bundle_unbundle(argc, argv, prefix); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_bundle_usage, options); - } - return result ? 1 : 0; + return !!fn(argc, argv, prefix); } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 50cf38999d..9e7e03ade4 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -3,11 +3,14 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" +#include "alloc.h" #include "config.h" #include "builtin.h" #include "diff.h" +#include "hex.h" +#include "ident.h" #include "parse-options.h" #include "userdiff.h" #include "streaming.h" @@ -15,7 +18,9 @@ #include "oid-array.h" #include "packfile.h" #include "object-store.h" +#include "replace-object.h" #include "promisor-remote.h" +#include "mailmap.h" enum batch_mode { BATCH_MODE_CONTENTS, @@ -31,11 +36,28 @@ struct batch_options { int all_objects; int unordered; int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */ + int nul_terminated; const char *format; }; static const char *force_path; +static struct string_list mailmap = STRING_LIST_INIT_NODUP; +static int use_mailmap; + +static char *replace_idents_using_mailmap(char *, size_t *); + +static char *replace_idents_using_mailmap(char *object_buf, size_t *size) +{ + struct strbuf sb = STRBUF_INIT; + const char *headers[] = { "author ", "committer ", "tagger ", NULL }; + + strbuf_attach(&sb, object_buf, *size, *size + 1); + apply_mailmap_to_header(&sb, headers, &mailmap); + *size = sb.len; + return strbuf_detach(&sb, NULL); +} + static int filter_object(const char *path, unsigned mode, const struct object_id *oid, char **buf, unsigned long *size) @@ -71,6 +93,7 @@ static int stream_blob(const struct object_id *oid) static int cat_one_file(int opt, const char *exp_type, const char *obj_name, int unknown_type) { + int ret; struct object_id oid; enum object_type type; char *buf; @@ -106,16 +129,31 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (sb.len) { printf("%s\n", sb.buf); strbuf_release(&sb); - return 0; + ret = 0; + goto cleanup; } break; case 's': oi.sizep = &size; + + if (use_mailmap) { + oi.typep = &type; + oi.contentp = (void**)&buf; + } + if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) die("git cat-file: could not get object info"); + + if (use_mailmap && (type == OBJ_COMMIT || type == OBJ_TAG)) { + size_t s = size; + buf = replace_idents_using_mailmap(buf, &s); + size = cast_size_t_to_ulong(s); + } + printf("%"PRIuMAX"\n", (uintmax_t)size); - return 0; + ret = 0; + goto cleanup; case 'e': return !has_object_file(&oid); @@ -123,8 +161,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, case 'w': if (filter_object(path, obj_context.mode, - &oid, &buf, &size)) - return -1; + &oid, &buf, &size)) { + ret = -1; + goto cleanup; + } break; case 'c': @@ -143,15 +183,24 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, const char *ls_args[3] = { NULL }; ls_args[0] = "ls-tree"; ls_args[1] = obj_name; - return cmd_ls_tree(2, ls_args, NULL); + ret = cmd_ls_tree(2, ls_args, NULL); + goto cleanup; } - if (type == OBJ_BLOB) - return stream_blob(&oid); + if (type == OBJ_BLOB) { + ret = stream_blob(&oid); + goto cleanup; + } buf = read_object_file(&oid, &type, &size); if (!buf) die("Cannot read object %s", obj_name); + if (use_mailmap) { + size_t s = size; + buf = replace_idents_using_mailmap(buf, &s); + size = cast_size_t_to_ulong(s); + } + /* otherwise just spit out the data */ break; @@ -172,8 +221,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, } else oidcpy(&blob_oid, &oid); - if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) - return stream_blob(&blob_oid); + if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) { + ret = stream_blob(&blob_oid); + goto cleanup; + } /* * we attempted to dereference a tag to a blob * and failed; there may be new dereference @@ -183,6 +234,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, } buf = read_object_with_reference(the_repository, &oid, exp_type_id, &size, NULL); + + if (use_mailmap) { + size_t s = size; + buf = replace_idents_using_mailmap(buf, &s); + size = cast_size_t_to_ulong(s); + } break; } default: @@ -193,9 +250,11 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, die("git cat-file %s: bad file", obj_name); write_or_die(1, buf, size); + ret = 0; +cleanup: free(buf); free(obj_context.path); - return 0; + return ret; } struct expand_data { @@ -348,11 +407,18 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d void *contents; contents = read_object_file(oid, &type, &size); + + if (use_mailmap) { + size_t s = size; + contents = replace_idents_using_mailmap(contents, &s); + size = cast_size_t_to_ulong(s); + } + if (!contents) die("object %s disappeared", oid_to_hex(oid)); if (type != data->type) die("object %s changed type!?", oid_to_hex(oid)); - if (data->info.sizep && size != data->size) + if (data->info.sizep && size != data->size && !use_mailmap) die("object %s changed size!?", oid_to_hex(oid)); batch_write(opt, contents, size); @@ -382,6 +448,9 @@ static void batch_object_write(const char *obj_name, if (!data->skip_object_info) { int ret; + if (use_mailmap) + data->info.typep = &data->type; + if (pack) ret = packed_object_info(the_repository, pack, offset, &data->info); @@ -395,6 +464,18 @@ static void batch_object_write(const char *obj_name, fflush(stdout); return; } + + if (use_mailmap && (data->type == OBJ_COMMIT || data->type == OBJ_TAG)) { + size_t s = data->size; + char *buf = NULL; + + buf = repo_read_object_file(the_repository, &data->oid, &data->type, + &data->size); + buf = replace_idents_using_mailmap(buf, &s); + data->size = cast_size_t_to_ulong(s); + + free(buf); + } } strbuf_reset(scratch); @@ -482,7 +563,7 @@ static int batch_object_cb(const struct object_id *oid, void *vdata) } static int collect_loose_object(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { oid_array_append(data, oid); @@ -490,8 +571,8 @@ static int collect_loose_object(const struct object_id *oid, } static int collect_packed_object(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, void *data) { oid_array_append(data, oid); @@ -514,7 +595,7 @@ static int batch_unordered_object(const struct object_id *oid, } static int batch_unordered_loose(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { return batch_unordered_object(oid, NULL, 0, data); @@ -602,12 +683,20 @@ static void batch_objects_command(struct batch_options *opt, struct queued_cmd *queued_cmd = NULL; size_t alloc = 0, nr = 0; - while (!strbuf_getline(&input, stdin)) { - int i; + while (1) { + int i, ret; const struct parse_cmd *cmd = NULL; const char *p = NULL, *cmd_end; struct queued_cmd call = {0}; + if (opt->nul_terminated) + ret = strbuf_getline_nul(&input, stdin); + else + ret = strbuf_getline(&input, stdin); + + if (ret) + break; + if (!input.len) die(_("empty command in input")); if (isspace(*input.buf)) @@ -655,6 +744,7 @@ static void batch_objects_command(struct batch_options *opt, free_cmds(queued_cmd, &nr); } + free_cmds(queued_cmd, &nr); free(queued_cmd); strbuf_release(&input); } @@ -750,7 +840,16 @@ static int batch_objects(struct batch_options *opt) goto cleanup; } - while (strbuf_getline(&input, stdin) != EOF) { + while (1) { + int ret; + if (opt->nul_terminated) + ret = strbuf_getline_nul(&input, stdin); + else + ret = strbuf_getline(&input, stdin); + + if (ret == EOF) + break; + if (data.split_on_whitespace) { /* * Split at first whitespace, tying off the beginning @@ -826,7 +925,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"), N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n" " [--buffer] [--follow-symlinks] [--unordered]\n" - " [--textconv | --filters]"), + " [--textconv | --filters] [-z]"), N_("git cat-file (--textconv | --filters)\n" " [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"), NULL @@ -843,6 +942,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'), OPT_BOOL(0, "allow-unknown-type", &unknown_type, N_("allow -s and -t to work with broken/corrupt objects")), + OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")), + OPT_ALIAS(0, "mailmap", "use-mailmap"), /* Batch mode */ OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")), OPT_CALLBACK_F(0, "batch", &batch, N_("format"), @@ -853,6 +954,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) N_("like --batch, but don't emit <contents>"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, batch_option_callback), + OPT_BOOL('z', NULL, &batch.nul_terminated, N_("stdin is NUL-terminated")), OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"), N_("read commands from stdin"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, @@ -885,6 +987,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) opt_cw = (opt == 'c' || opt == 'w'); opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's'); + if (use_mailmap) + read_mailmap(&mailmap); + /* --batch-all-objects? */ if (opt == 'b') batch.all_objects = 1; @@ -908,6 +1013,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) else if (batch.all_objects) usage_msg_optf(_("'%s' requires a batch mode"), usage, options, "--batch-all-objects"); + else if (batch.nul_terminated) + usage_msg_optf(_("'%s' requires a batch mode"), usage, options, + "-z"); /* Batch defaults */ if (batch.buffer_output < 0) diff --git a/builtin/check-attr.c b/builtin/check-attr.c index dd83397786..d7a40e674c 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -1,4 +1,4 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "cache.h" #include "config.h" @@ -9,9 +9,10 @@ static int all_attrs; static int cached_attrs; static int stdin_paths; +static char *source; static const char * const check_attr_usage[] = { -N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."), -N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"), +N_("git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>..."), +N_("git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"), NULL }; @@ -23,6 +24,7 @@ static const struct option check_attr_options[] = { OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")), OPT_BOOL('z', NULL, &nul_term_line, N_("terminate input and output records by a NUL character")), + OPT_STRING(0, "source", &source, N_("<tree-ish>"), N_("which tree-ish to check attributes at")), OPT_END() }; @@ -55,27 +57,26 @@ static void output_attr(struct attr_check *check, const char *file) } } -static void check_attr(const char *prefix, - struct attr_check *check, - int collect_all, +static void check_attr(const char *prefix, struct attr_check *check, + const struct object_id *tree_oid, int collect_all, const char *file) + { char *full_path = prefix_path(prefix, prefix ? strlen(prefix) : 0, file); if (collect_all) { - git_all_attrs(&the_index, full_path, check); + git_all_attrs(&the_index, tree_oid, full_path, check); } else { - git_check_attr(&the_index, full_path, check); + git_check_attr(&the_index, tree_oid, full_path, check); } output_attr(check, file); free(full_path); } -static void check_attr_stdin_paths(const char *prefix, - struct attr_check *check, - int collect_all) +static void check_attr_stdin_paths(const char *prefix, struct attr_check *check, + const struct object_id *tree_oid, int collect_all) { struct strbuf buf = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT; @@ -89,7 +90,7 @@ static void check_attr_stdin_paths(const char *prefix, die("line is badly quoted"); strbuf_swap(&buf, &unquoted); } - check_attr(prefix, check, collect_all, buf.buf); + check_attr(prefix, check, tree_oid, collect_all, buf.buf); maybe_flush_or_die(stdout, "attribute to stdout"); } strbuf_release(&buf); @@ -105,6 +106,8 @@ static NORETURN void error_with_usage(const char *msg) int cmd_check_attr(int argc, const char **argv, const char *prefix) { struct attr_check *check; + struct object_id *tree_oid = NULL; + struct object_id initialized_oid; int cnt, i, doubledash, filei; if (!is_bare_repository()) @@ -115,7 +118,7 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); - if (read_cache() < 0) { + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } @@ -176,11 +179,17 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) } } + if (source) { + if (repo_get_oid_tree(the_repository, source, &initialized_oid)) + die("%s: not a valid tree-ish source", source); + tree_oid = &initialized_oid; + } + if (stdin_paths) - check_attr_stdin_paths(prefix, check, all_attrs); + check_attr_stdin_paths(prefix, check, tree_oid, all_attrs); else { for (i = filei; i < argc; i++) - check_attr(prefix, check, all_attrs, argv[i]); + check_attr(prefix, check, tree_oid, all_attrs, argv[i]); maybe_flush_or_die(stdout, "attribute to stdout"); } diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 2191256965..ab776061c7 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -1,4 +1,4 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "cache.h" #include "config.h" @@ -179,7 +179,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) die(_("--non-matching is only valid with --verbose")); /* read_cache() is only necessary so we can watch out for submodules. */ - if (!no_index && read_cache() < 0) + if (!no_index && repo_read_index(the_repository) < 0) die(_("index file corrupt")); setup_standard_excludes(&dir); diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index 7dc47e4793..96db3ddb4b 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "ident.h" #include "mailmap.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c index bc67d3f0a8..fd0e5f8683 100644 --- a/builtin/check-ref-format.c +++ b/builtin/check-ref-format.c @@ -57,6 +57,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix) int normalize = 0; int flags = 0; const char *refname; + char *to_free = NULL; + int ret = 1; if (argc == 2 && !strcmp(argv[1], "-h")) usage(builtin_check_ref_format_usage); @@ -81,11 +83,14 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix) refname = argv[i]; if (normalize) - refname = collapse_slashes(refname); + refname = to_free = collapse_slashes(refname); if (check_refname_format(refname, flags)) - return 1; + goto cleanup; if (normalize) printf("%s\n", refname); - return 0; + ret = 0; +cleanup: + free(to_free); + return ret; } diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c index ede7dc32a4..0a7d762573 100644 --- a/builtin/checkout--worker.c +++ b/builtin/checkout--worker.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "alloc.h" #include "config.h" #include "entry.h" #include "parallel-checkout.h" diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 97e06e8c52..cf6fba97ba 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -4,7 +4,7 @@ * Copyright (C) 2005 Linus Torvalds * */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" #include "dir.h" @@ -65,7 +65,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 = cache_name_pos(name, namelen); + int pos = index_name_pos(&the_index, name, namelen); int has_same_name = 0; int is_file = 0; int is_skipped = 1; @@ -75,8 +75,8 @@ static int checkout_file(const char *name, const char *prefix) if (pos < 0) pos = -pos - 1; - while (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; + while (pos < the_index.cache_nr) { + struct cache_entry *ce = the_index.cache[pos]; if (ce_namelen(ce) != namelen || memcmp(ce->name, name, namelen)) break; @@ -136,8 +136,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 < active_nr ; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr ; i++) { + struct cache_entry *ce = the_index.cache[i]; if (S_ISSPARSEDIR(ce->ce_mode)) { if (!ce_skip_worktree(ce)) @@ -151,7 +151,7 @@ static int checkout_all(const char *prefix, int prefix_length) */ if (ignore_skip_worktree) { ensure_full_index(&the_index); - ce = active_cache[i]; + ce = the_index.cache[i]; } } @@ -249,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - if (read_cache() < 0) { + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } @@ -270,7 +270,8 @@ 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; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, + LOCK_DIE_ON_ERROR); } get_parallel_checkout_configs(&pc_workers, &pc_threshold); diff --git a/builtin/checkout.c b/builtin/checkout.c index 2eefda81d8..734d730980 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,4 +1,4 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "advice.h" #include "blob.h" @@ -9,6 +9,7 @@ #include "config.h" #include "diff.h" #include "dir.h" +#include "hex.h" #include "hook.h" #include "ll-merge.h" #include "lockfile.h" @@ -29,6 +30,7 @@ #include "xdiff-interface.h" #include "entry.h" #include "parallel-checkout.h" +#include "add-interactive.h" static const char * const checkout_usage[] = { N_("git checkout [<options>] <branch>"), @@ -74,7 +76,7 @@ struct checkout_opts { const char *ignore_unmerged_opt; int ignore_unmerged; int pathspec_file_nul; - const char *pathspec_from_file; + char *pathspec_from_file; const char *new_branch; const char *new_branch_force; @@ -125,7 +127,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm } static int update_some(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, void *context UNUSED) { int len; struct cache_entry *ce; @@ -148,9 +150,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 = cache_name_pos(ce->name, ce->ce_namelen); + pos = index_name_pos(&the_index, ce->name, ce->ce_namelen); if (pos >= 0) { - struct cache_entry *old = active_cache[pos]; + struct cache_entry *old = the_index.cache[pos]; if (ce->ce_mode == old->ce_mode && !ce_intent_to_add(old) && oideq(&ce->oid, &old->oid)) { @@ -160,7 +162,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base, } } - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); + add_index_entry(&the_index, ce, + ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; } @@ -178,8 +181,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 < active_nr && - !strcmp(active_cache[pos]->name, ce->name)) + while (++pos < the_index.cache_nr && + !strcmp(the_index.cache[pos]->name, ce->name)) ; /* skip */ return pos; } @@ -187,9 +190,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 < active_nr && - !strcmp(active_cache[pos]->name, ce->name)) { - if (ce_stage(active_cache[pos]) == stage) + while (pos < the_index.cache_nr && + !strcmp(the_index.cache[pos]->name, ce->name)) { + if (ce_stage(the_index.cache[pos]) == stage) return 0; pos++; } @@ -206,8 +209,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos) unsigned seen = 0; const char *name = ce->name; - while (pos < active_nr) { - ce = active_cache[pos]; + while (pos < the_index.cache_nr) { + ce = the_index.cache[pos]; if (strcmp(name, ce->name)) break; seen |= (1 << ce_stage(ce)); @@ -223,15 +226,15 @@ 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 < active_nr && - !strcmp(active_cache[pos]->name, ce->name)) { - if (ce_stage(active_cache[pos]) == stage) - return checkout_entry(active_cache[pos], state, + 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, NULL, nr_checkouts); pos++; } if (!overlay_mode) { - unlink_entry(ce); + unlink_entry(ce, NULL); return 0; } if (stage == 2) @@ -243,7 +246,7 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos, static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts, struct mem_pool *ce_mem_pool) { - struct cache_entry *ce = active_cache[pos]; + struct cache_entry *ce = the_index.cache[pos]; const char *path = ce->name; mmfile_t ancestor, ours, theirs; enum ll_merge_result merge_status; @@ -256,7 +259,7 @@ static int checkout_merged(int pos, const struct checkout *state, int renormalize = 0; memset(threeway, 0, sizeof(threeway)); - while (pos < active_nr) { + while (pos < the_index.cache_nr) { int stage; stage = ce_stage(ce); if (!stage || strcmp(path, ce->name)) @@ -265,7 +268,7 @@ static int checkout_merged(int pos, const struct checkout *state, if (stage == 2) mode = create_ce_mode(ce->ce_mode); pos++; - ce = active_cache[pos]; + ce = the_index.cache[pos]; } if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2])) return error(_("path '%s' does not have necessary versions"), path); @@ -391,8 +394,8 @@ static int checkout_worktree(const struct checkout_opts *opts, if (pc_workers > 1) init_parallel_checkout(); - for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; + for (pos = 0; pos < the_index.cache_nr; pos++) { + struct cache_entry *ce = the_index.cache[pos]; if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, @@ -417,7 +420,7 @@ static int checkout_worktree(const struct checkout_opts *opts, mem_pool_discard(&ce_mem_pool, should_validate_cache_entries()); remove_marked_cache_entries(&the_index, 1); remove_scheduled_dirs(); - errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress); + errs |= finish_delayed_checkout(&state, opts->show_progress); if (opts->count_checkout_paths) { if (nr_unmerged) @@ -487,18 +490,31 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("'%s' must be used when '%s' is not specified"), "--worktree", "--source"); - if (opts->checkout_index && !opts->checkout_worktree && - opts->writeout_stage) - die(_("'%s' or '%s' cannot be used with %s"), - "--ours", "--theirs", "--staged"); - - if (opts->checkout_index && !opts->checkout_worktree && - opts->merge) - die(_("'%s' or '%s' cannot be used with %s"), - "--merge", "--conflict", "--staged"); + /* + * Reject --staged option to the restore command when combined with + * merge-related options. Use the accept_ref flag to distinguish it + * from the checkout command, which does not accept --staged anyway. + * + * `restore --ours|--theirs --worktree --staged` could mean resolving + * conflicted paths to one side in both the worktree and the index, + * but does not currently. + * + * `restore --merge|--conflict=<style>` already recreates conflicts + * in both the worktree and the index, so adding --staged would be + * meaningless. + */ + if (!opts->accept_ref && opts->checkout_index) { + if (opts->writeout_stage) + die(_("'%s' or '%s' cannot be used with %s"), + "--ours", "--theirs", "--staged"); + + if (opts->merge) + die(_("'%s' or '%s' cannot be used with %s"), + "--merge", "--conflict", "--staged"); + } if (opts->patch_mode) { - const char *patch_mode; + enum add_p_mode patch_mode; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -516,19 +532,20 @@ static int checkout_paths(const struct checkout_opts *opts, rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid); if (opts->checkout_index && opts->checkout_worktree) - patch_mode = "--patch=checkout"; + patch_mode = ADD_P_CHECKOUT; else if (opts->checkout_index && !opts->checkout_worktree) - patch_mode = "--patch=reset"; + patch_mode = ADD_P_RESET; else if (!opts->checkout_index && opts->checkout_worktree) - patch_mode = "--patch=worktree"; + patch_mode = ADD_P_WORKTREE; else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return run_add_interactive(rev, patch_mode, &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, rev, + &opts->pathspec); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); - if (read_cache_preload(&opts->pathspec) < 0) + if (repo_read_index_preload(the_repository, &opts->pathspec, 0) < 0) return error(_("index file corrupt")); if (opts->source_tree) @@ -540,13 +557,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 < active_nr; pos++) + for (pos = 0; pos < the_index.cache_nr; pos++) if (opts->overlay_mode) - mark_ce_for_checkout_overlay(active_cache[pos], + mark_ce_for_checkout_overlay(the_index.cache[pos], ps_matched, opts); else - mark_ce_for_checkout_no_overlay(active_cache[pos], + mark_ce_for_checkout_no_overlay(the_index.cache[pos], ps_matched, opts); @@ -561,8 +578,8 @@ static int checkout_paths(const struct checkout_opts *opts, unmerge_marked_index(&the_index); /* Any unmerged paths? */ - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; + for (pos = 0; pos < the_index.cache_nr; pos++) { + const struct cache_entry *ce = the_index.cache[pos]; if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) continue; @@ -626,6 +643,7 @@ static void show_local_changes(struct object *head, repo_init_revisions(the_repository, &rev, NULL); rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; + rev.diffopt.flags.recursive = 1; diff_setup_done(&rev.diffopt); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); @@ -710,6 +728,26 @@ static void setup_branch_path(struct branch_info *branch) branch->path = strbuf_detach(&buf, NULL); } +static void init_topts(struct unpack_trees_options *topts, int merge, + int show_progress, int overwrite_ignore, + struct commit *old_commit) +{ + memset(topts, 0, sizeof(*topts)); + topts->head_idx = -1; + topts->src_index = &the_index; + topts->dst_index = &the_index; + + setup_unpack_trees_porcelain(topts, "checkout"); + + topts->initial_checkout = is_index_unborn(&the_index); + topts->update = 1; + topts->merge = 1; + topts->quiet = merge && old_commit; + topts->verbose_update = show_progress; + topts->fn = twoway_merge; + topts->preserve_ignored = !overwrite_ignore; +} + static int merge_working_tree(const struct checkout_opts *opts, struct branch_info *old_branch_info, struct branch_info *new_branch_info, @@ -719,11 +757,11 @@ static int merge_working_tree(const struct checkout_opts *opts, struct lock_file lock_file = LOCK_INIT; struct tree *new_tree; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - if (read_cache_preload(NULL) < 0) + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); + if (repo_read_index_preload(the_repository, NULL, 0) < 0) return error(_("index file corrupt")); - resolve_undo_clear(); + resolve_undo_clear_index(&the_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"); @@ -740,32 +778,20 @@ static int merge_working_tree(const struct checkout_opts *opts, struct unpack_trees_options topts; const struct object_id *old_commit_oid; - memset(&topts, 0, sizeof(topts)); - topts.head_idx = -1; - topts.src_index = &the_index; - topts.dst_index = &the_index; + refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); - setup_unpack_trees_porcelain(&topts, "checkout"); - - refresh_cache(REFRESH_QUIET); - - if (unmerged_cache()) { + if (unmerged_index(&the_index)) { error(_("you need to resolve your current index first")); return 1; } /* 2-way merge to the new branch */ - topts.initial_checkout = is_cache_unborn(); - topts.update = 1; - topts.merge = 1; - topts.quiet = opts->merge && old_branch_info->commit; - topts.verbose_update = opts->show_progress; - topts.fn = twoway_merge; + init_topts(&topts, opts->merge, opts->show_progress, + opts->overwrite_ignore, old_branch_info->commit); init_checkout_metadata(&topts.meta, new_branch_info->refname, new_branch_info->commit ? &new_branch_info->commit->object.oid : &new_branch_info->oid, NULL); - topts.preserve_ignored = !opts->overwrite_ignore; old_commit_oid = old_branch_info->commit ? &old_branch_info->commit->object.oid : @@ -858,7 +884,7 @@ static int merge_working_tree(const struct checkout_opts *opts, } } - if (!cache_tree_fully_valid(active_cache_tree)) + if (!cache_tree_fully_valid(the_index.cache_tree)) cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) @@ -981,7 +1007,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, static int add_pending_uninteresting_ref(const char *refname, const struct object_id *oid, - int flags, void *cb_data) + int flags UNUSED, void *cb_data) { add_pending_oid(cb_data, refname, oid, UNINTERESTING); return 0; @@ -1260,7 +1286,7 @@ static int parse_branchname_arg(int argc, const char **argv, * between A and B, A...B names that merge base. * * (b) If <something> is _not_ a commit, either "--" is present - * or <something> is not a path, no -t or -b was given, and + * or <something> is not a path, no -t or -b was given, * and there is a tracking branch whose name is <something> * in one and only one remote (or if the branch exists on the * remote named in checkout.defaultRemote), then this is a @@ -1461,6 +1487,8 @@ static void die_if_some_operation_in_progress(void) "or \"git worktree add\".")); if (state.bisect_in_progress) warning(_("you are switching branch while bisecting")); + + wt_status_state_free_buffers(&state); } static int checkout_branch(struct checkout_opts *opts, @@ -1862,6 +1890,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) options, checkout_usage, &new_branch_info); branch_info_release(&new_branch_info); clear_pathspec(&opts.pathspec); + free(opts.pathspec_from_file); FREE_AND_NULL(options); return ret; } diff --git a/builtin/clean.c b/builtin/clean.c index 5466636e66..10aaa8c603 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -6,7 +6,7 @@ * Based on git-clean.sh by Pavel Roskin */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "cache.h" #include "config.h" @@ -26,7 +26,7 @@ static struct string_list del_list = STRING_LIST_INIT_DUP; static unsigned int colopts; static const char *const builtin_clean_usage[] = { - N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."), + N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] [<pathspec>...]"), NULL }; @@ -560,7 +560,7 @@ static int parse_choice(struct menu_stuff *menu_stuff, /* * Implement a git-add-interactive compatible UI, which is borrowed - * from git-add--interactive.perl. + * from add-interactive.c. * * Return value: * @@ -1012,7 +1012,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option"); @@ -1031,7 +1031,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct stat st; const char *rel; - if (!cache_name_is_other(ent->name, ent->len)) + if (!index_name_is_other(&the_index, ent->name, ent->len)) continue; if (lstat(ent->name, &st)) @@ -1092,5 +1092,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) strbuf_release(&buf); string_list_clear(&del_list, 0); string_list_clear(&exclude_list, 0); + clear_pathspec(&pathspec); return (errors != 0); } diff --git a/builtin/clone.c b/builtin/clone.c index 89a91b0017..462c286274 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -8,9 +8,10 @@ * Clone a repository into a different directory that does not yet exist. */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "parse-options.h" #include "fetch-pack.h" @@ -34,6 +35,7 @@ #include "list-objects-filter-options.h" #include "hook.h" #include "bundle.h" +#include "bundle-uri.h" /* * Overall FIXMEs: @@ -72,11 +74,12 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; static int option_dissociate; static int max_jobs = -1; static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; -static struct list_objects_filter_options filter_options; +static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static int option_filter_submodules = -1; /* unspecified */ static int config_filter_submodules = -1; /* unspecified */ static struct string_list server_options = STRING_LIST_INIT_NODUP; static int option_remote_submodules; +static const char *bundle_uri; static int recurse_submodules_cb(const struct option *opt, const char *arg, int unset) @@ -160,6 +163,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will use their remote-tracking branch")), OPT_BOOL(0, "sparse", &option_sparse_checkout, N_("initialize sparse-checkout file to include only files at root")), + OPT_STRING(0, "bundle-uri", &bundle_uri, + N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")), OPT_END() }; @@ -316,13 +321,11 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, int src_len, dest_len; struct dir_iterator *iter; int iter_status; - unsigned int flags; struct strbuf realpath = STRBUF_INIT; mkdir_if_missing(dest->buf, 0777); - flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS; - iter = dir_iterator_begin(src->buf, flags); + iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC); if (!iter) die_errno(_("failed to start iterator over '%s'"), src->buf); @@ -338,6 +341,10 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, strbuf_setlen(dest, dest_len); strbuf_addstr(dest, iter->relative_path); + if (S_ISLNK(iter->st.st_mode)) + die(_("symlink '%s' exists, refusing to clone with --local"), + iter->relative_path); + if (S_ISDIR(iter->st.st_mode)) { mkdir_if_missing(dest->buf, 0777); continue; @@ -494,6 +501,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs, /* if --branch=tag, pull the requested tag explicitly */ get_fetch_map(remote_head, tag_refspec, &tail, 0); } + free_refs(remote_head); } else { int i; for (i = 0; i < refspec->nr; i++) @@ -606,7 +614,7 @@ static void update_remote_refs(const struct ref *refs, } static void update_head(const struct ref *our, const struct ref *remote, - const char *msg) + const char *unborn, const char *msg) { const char *head; if (our && skip_prefix(our->name, "refs/heads/", &head)) { @@ -632,14 +640,23 @@ static void update_head(const struct ref *our, const struct ref *remote, */ update_ref(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) + die(_("unable to update HEAD")); + if (!option_bare) + install_branch_config(0, head, remote_name, unborn); } } static int git_sparse_checkout_init(const char *repo) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int result = 0; - strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL); + strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL); /* * We must apply the setting in the current process @@ -647,12 +664,12 @@ static int git_sparse_checkout_init(const char *repo) */ core_apply_sparse_checkout = 1; - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + if (run_command(&cmd)) { error(_("failed to initialize sparse-checkout")); result = 1; } - strvec_clear(&argv); return result; } @@ -672,7 +689,7 @@ static int checkout(int submodule_progress, int filter_submodules) head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL); if (!head) { warning(_("remote HEAD refers to nonexistent ref, " - "unable to checkout.\n")); + "unable to checkout")); return 0; } if (!strcmp(head, "HEAD")) { @@ -687,7 +704,7 @@ static int checkout(int submodule_progress, int filter_submodules) /* We need to be in the new work tree for the checkout */ setup_work_tree(); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof opts); opts.update = 1; @@ -717,37 +734,38 @@ static int checkout(int submodule_progress, int filter_submodules) oid_to_hex(&oid), "1", NULL); if (!err && (option_recurse_submodules.nr > 0)) { - struct strvec args = STRVEC_INIT; - strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL); + struct child_process cmd = CHILD_PROCESS_INIT; + strvec_pushl(&cmd.args, "submodule", "update", "--require-init", + "--recursive", NULL); if (option_shallow_submodules == 1) - strvec_push(&args, "--depth=1"); + strvec_push(&cmd.args, "--depth=1"); if (max_jobs != -1) - strvec_pushf(&args, "--jobs=%d", max_jobs); + strvec_pushf(&cmd.args, "--jobs=%d", max_jobs); if (submodule_progress) - strvec_push(&args, "--progress"); + strvec_push(&cmd.args, "--progress"); if (option_verbosity < 0) - strvec_push(&args, "--quiet"); + strvec_push(&cmd.args, "--quiet"); if (option_remote_submodules) { - strvec_push(&args, "--remote"); - strvec_push(&args, "--no-fetch"); + strvec_push(&cmd.args, "--remote"); + strvec_push(&cmd.args, "--no-fetch"); } if (filter_submodules && filter_options.choice) - strvec_pushf(&args, "--filter=%s", + strvec_pushf(&cmd.args, "--filter=%s", expand_list_objects_filter_spec(&filter_options)); if (option_single_branch >= 0) - strvec_push(&args, option_single_branch ? + strvec_push(&cmd.args, option_single_branch ? "--single-branch" : "--no-single-branch"); - err = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); + cmd.git_cmd = 1; + err = run_command(&cmd); } return err; @@ -848,11 +866,15 @@ static void write_refspec_config(const char *src_ref_prefix, static void dissociate_from_references(void) { - static const char* argv[] = { "repack", "-a", "-d", NULL }; char *alternates = git_pathdup("objects/info/alternates"); if (!access(alternates, F_OK)) { - if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN)) + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + cmd.no_stdin = 1; + strvec_pushl(&cmd.args, "repack", "-a", "-d", NULL); + if (run_command(&cmd)) die(_("cannot repack to clean up")); if (unlink(alternates) && errno != ENOENT) die_errno(_("cannot unlink temporary alternates file")); @@ -871,11 +893,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int is_bundle = 0, is_local; int reject_shallow = 0; const char *repo_name, *repo, *work_tree, *git_dir; + char *repo_to_free = NULL; char *path = NULL, *dir, *display_repo = NULL; int dest_exists, real_dest_exists = 0; const struct ref *refs, *remote_head; struct ref *remote_head_points_at = NULL; const struct ref *our_head_points_at; + char *unborn_head = NULL; struct ref *mapped_refs = NULL; const struct ref *ref; struct strbuf key = STRBUF_INIT; @@ -914,20 +938,20 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_bare = 1; if (option_bare) { - if (option_origin) - die(_("options '%s' and '%s %s' cannot be used together"), - "--bare", "--origin", option_origin); if (real_git_dir) die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir"); option_no_checkout = 1; } + if (bundle_uri && deepen) + die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude")); + repo_name = argv[0]; path = get_repo_path(repo_name, &is_bundle); if (path) { FREE_AND_NULL(path); - repo = absolute_pathdup(repo_name); + repo = repo_to_free = absolute_pathdup(repo_name); } else if (strchr(repo_name, ':')) { repo = repo_name; display_repo = transport_anonymize_url(repo); @@ -1148,10 +1172,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, branch_top.buf); - transport = transport_get(remote, remote->url[0]); - transport_set_verbosity(transport, option_verbosity, option_progress); - transport->family = family; - path = get_repo_path(remote->url[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; if (is_local) { @@ -1173,6 +1193,10 @@ 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_set_verbosity(transport, option_verbosity, option_progress); + transport->family = family; transport->cloning = 1; if (is_bundle) { @@ -1221,6 +1245,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; + /* + * Before fetching from the remote, download and install bundle + * data from the --bundle-uri option. + */ + if (bundle_uri) { + int has_heuristic = 0; + + /* At this point, we need the_repository to match the cloned repo. */ + if (repo_init(the_repository, git_dir, work_tree)) + warning(_("failed to initialize the repo, skipping bundle URI")); + else if (fetch_bundle_uri(the_repository, bundle_uri, &has_heuristic)) + warning(_("failed to fetch objects from bundle URI '%s'"), + bundle_uri); + else if (has_heuristic) + git_config_set_gently("fetch.bundleuri", bundle_uri); + } strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); refspec_ref_prefixes(&remote->fetch, @@ -1237,6 +1277,27 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (refs) mapped_refs = wanted_peer_refs(refs, &remote->fetch); + if (!bundle_uri) { + /* + * Populate transport->got_remote_bundle_uri and + * transport->bundle_uri. We might get nothing. + */ + transport_get_remote_bundle_uri(transport); + + if (transport->bundles && + hashmap_get_size(&transport->bundles->bundles)) { + /* At this point, we need the_repository to match the cloned repo. */ + if (repo_init(the_repository, git_dir, work_tree)) + warning(_("failed to initialize the repo, skipping bundle URI")); + else if (fetch_bundle_list(the_repository, + transport->bundles)) + warning(_("failed to fetch advertised bundles")); + } else { + clear_bundle_list(transport->bundles); + FREE_AND_NULL(transport->bundles); + } + } + if (mapped_refs) { int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); @@ -1266,51 +1327,49 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (transport_fetch_refs(transport, mapped_refs)) die(_("remote transport reported error")); } - - remote_head = find_ref_by_name(refs, "HEAD"); - remote_head_points_at = - guess_remote_head(remote_head, mapped_refs, 0); - - if (option_branch) { - our_head_points_at = - find_remote_branch(mapped_refs, option_branch); - - if (!our_head_points_at) - die(_("Remote branch %s not found in upstream %s"), - option_branch, remote_name); - } - else - our_head_points_at = remote_head_points_at; } - else { - const char *branch; - const char *ref; - char *ref_free = NULL; - if (option_branch) - die(_("Remote branch %s not found in upstream %s"), - option_branch, remote_name); + remote_head = find_ref_by_name(refs, "HEAD"); + remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0); - warning(_("You appear to have cloned an empty repository.")); + if (option_branch) { + our_head_points_at = find_remote_branch(mapped_refs, option_branch); + if (!our_head_points_at) + die(_("Remote branch %s not found in upstream %s"), + option_branch, remote_name); + } else if (remote_head_points_at) { + our_head_points_at = remote_head_points_at; + } else if (remote_head) { our_head_points_at = NULL; - remote_head_points_at = NULL; - remote_head = NULL; - option_no_checkout = 1; + } else { + const char *branch; + + if (!mapped_refs) { + warning(_("You appear to have cloned an empty repository.")); + option_no_checkout = 1; + } if (transport_ls_refs_options.unborn_head_target && skip_prefix(transport_ls_refs_options.unborn_head_target, "refs/heads/", &branch)) { - ref = transport_ls_refs_options.unborn_head_target; - create_symref("HEAD", ref, reflog_msg.buf); + unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target); } else { branch = git_default_branch_name(0); - ref_free = xstrfmt("refs/heads/%s", branch); - ref = ref_free; + unborn_head = xstrfmt("refs/heads/%s", branch); } - if (!option_bare) - install_branch_config(0, branch, remote_name, ref); - free(ref_free); + /* + * We may have selected a local default branch name "foo", + * and even though the remote's HEAD does not point there, + * it may still have a "foo" branch. If so, set it up so + * that we can follow the usual checkout code later. + * + * Note that for an empty repo we'll already have set + * option_no_checkout above, which would work against us here. + * But for an empty repo, find_remote_branch() can never find + * a match. + */ + our_head_points_at = find_remote_branch(mapped_refs, branch); } write_refspec_config(src_ref_prefix, our_head_points_at, @@ -1330,7 +1389,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) branch_top.buf, reflog_msg.buf, transport, !is_local); - update_head(our_head_points_at, remote_head, reflog_msg.buf); + update_head(our_head_points_at, remote_head, unborn_head, reflog_msg.buf); /* * We want to show progress for recursive submodule clones iff @@ -1357,9 +1416,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&key); free_refs(mapped_refs); free_refs(remote_head_points_at); + free(unborn_head); free(dir); free(path); - UNLEAK(repo); + free(repo_to_free); junk_mode = JUNK_LEAVE_ALL; transport_ls_refs_options_release(&transport_ls_refs_options); diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 51c4040ea6..d3be7f3b31 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -1,22 +1,24 @@ #include "builtin.h" #include "config.h" #include "dir.h" +#include "hex.h" #include "lockfile.h" #include "parse-options.h" #include "repository.h" #include "commit-graph.h" #include "object-store.h" #include "progress.h" +#include "replace-object.h" #include "tag.h" #define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \ - N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]") + N_("git commit-graph verify [--object-dir <dir>] [--shallow] [--[no-]progress]") #define BUILTIN_COMMIT_GRAPH_WRITE_USAGE \ - N_("git commit-graph write [--object-dir <objdir>] [--append] " \ - "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " \ - "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " \ - "<split options>") + N_("git commit-graph write [--object-dir <dir>] [--append]\n" \ + " [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]\n" \ + " [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]\n" \ + " <split options>") static const char * builtin_commit_graph_verify_usage[] = { BUILTIN_COMMIT_GRAPH_VERIFY_USAGE, @@ -58,7 +60,7 @@ static struct option *add_common_options(struct option *to) return parse_options_concat(common_opts, to); } -static int graph_verify(int argc, const char **argv) +static int graph_verify(int argc, const char **argv, const char *prefix) { struct commit_graph *graph = NULL; struct object_directory *odb = NULL; @@ -67,6 +69,7 @@ static int graph_verify(int argc, const char **argv) int fd; struct stat st; int flags = 0; + int ret; static struct option builtin_commit_graph_verify_options[] = { OPT_BOOL(0, "shallow", &opts.shallow, @@ -80,7 +83,7 @@ static int graph_verify(int argc, const char **argv) trace2_cmd_mode("verify"); opts.progress = isatty(2); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_commit_graph_verify_usage, 0); if (argc) @@ -111,8 +114,9 @@ static int graph_verify(int argc, const char **argv) if (!graph) return !!open_ok; - UNLEAK(graph); - return verify_commit_graph(the_repository, graph, flags); + ret = verify_commit_graph(the_repository, graph, flags); + free_commit_graph(graph); + return ret; } extern int read_replace_refs; @@ -179,7 +183,7 @@ static int write_option_max_new_filters(const struct option *opt, } static int git_commit_graph_write_config(const char *var, const char *value, - void *cb) + void *cb UNUSED) { if (!strcmp(var, "commitgraph.maxnewfilters")) write_opts.max_new_filters = git_config_int(var, value); @@ -190,7 +194,7 @@ static int git_commit_graph_write_config(const char *var, const char *value, return 0; } -static int graph_write(int argc, const char **argv) +static int graph_write(int argc, const char **argv, const char *prefix) { struct string_list pack_indexes = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; @@ -241,7 +245,7 @@ static int graph_write(int argc, const char **argv) git_config(git_commit_graph_write_config, &opts); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_commit_graph_write_usage, 0); if (argc) @@ -267,8 +271,8 @@ static int graph_write(int argc, const char **argv) if (opts.reachable) { if (write_commit_graph_reachable(odb, flags, &write_opts)) - return 1; - return 0; + result = 1; + goto cleanup; } if (opts.stdin_packs) { @@ -307,26 +311,22 @@ cleanup: int cmd_commit_graph(int argc, const char **argv, const char *prefix) { - struct option *builtin_commit_graph_options = common_opts; + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_commit_graph_options[] = { + OPT_SUBCOMMAND("verify", &fn, graph_verify), + OPT_SUBCOMMAND("write", &fn, graph_write), + OPT_END(), + }; + struct option *options = parse_options_concat(builtin_commit_graph_options, common_opts); git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, - builtin_commit_graph_options, - builtin_commit_graph_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc) - goto usage; read_replace_refs = 0; save_commit_buffer = 0; - if (!strcmp(argv[0], "verify")) - return graph_verify(argc, argv); - else if (argc && !strcmp(argv[0], "write")) - return graph_write(argc, argv); + argc = parse_options(argc, argv, prefix, options, + builtin_commit_graph_usage, 0); + FREE_AND_NULL(options); - error(_("unrecognized subcommand: %s"), argv[0]); -usage: - usage_with_options(builtin_commit_graph_usage, - builtin_commit_graph_options); + return fn(argc, argv, prefix); } diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 63ea322933..e805da5bb1 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -5,6 +5,7 @@ */ #include "cache.h" #include "config.h" +#include "hex.h" #include "object-store.h" #include "repository.h" #include "commit.h" @@ -15,8 +16,9 @@ #include "parse-options.h" static const char * const commit_tree_usage[] = { - N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] " - "[(-F <file>)...] <tree>"), + N_("git commit-tree <tree> [(-p <parent>)...]"), + N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]\n" + " [(-F <file>)...] <tree>"), NULL }; @@ -36,14 +38,6 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p) commit_list_insert(parent, parents_p); } -static int commit_tree_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, NULL); - if (status) - return status; - return git_default_config(var, value, cb); -} - static int parse_parent_arg_callback(const struct option *opt, const char *arg, int unset) { @@ -120,7 +114,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(commit_tree_config, NULL); + git_config(git_default_config, NULL); if (argc < 2 || !strcmp(argv[1], "-h")) usage_with_options(commit_tree_usage, options); diff --git a/builtin/commit.c b/builtin/commit.c index fcf9c85947..f71ed41bf5 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -5,7 +5,7 @@ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "lockfile.h" @@ -40,12 +40,19 @@ #include "pretty.h" static const char * const builtin_commit_usage[] = { - N_("git commit [<options>] [--] <pathspec>..."), + N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\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" + " [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" + " [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n" + " [--] [<pathspec>...]"), NULL }; static const char * const builtin_status_usage[] = { - N_("git status [<options>] [--] <pathspec>..."), + N_("git status [<options>] [--] [<pathspec>...]"), NULL }; @@ -139,7 +146,7 @@ static int opt_pass_trailer(const struct option *opt, const char *arg, int unset { BUG_ON_OPT_NEG(unset); - strvec_pushl(&trailer_args, "--trailer", arg, NULL); + strvec_pushl(opt->value, "--trailer", arg, NULL); return 0; } @@ -265,8 +272,8 @@ static int list_paths(struct string_list *list, const char *with_tree, /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr; i++) { + const struct cache_entry *ce = the_index.cache[i]; struct string_list_item *item; if (ce->ce_flags & CE_UPDATE) @@ -295,10 +302,10 @@ static void add_remove_files(struct string_list *list) continue; if (!lstat(p->string, &st)) { - if (add_to_cache(p->string, &st, 0)) + if (add_to_index(&the_index, p->string, &st, 0)) die(_("updating files failed")); } else - remove_file_from_cache(p->string); + remove_file_from_index(&the_index, p->string); } } @@ -309,7 +316,7 @@ static void create_base_index(const struct commit *current_head) struct tree_desc t; if (!current_head) { - discard_cache(); + discard_index(&the_index); return; } @@ -336,7 +343,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_cache(refresh_flags | REFRESH_IN_PORCELAIN)) + if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL)) die_resolve_conflict("commit"); } @@ -375,12 +382,13 @@ static const char *prepare_index(const char **argv, const char *prefix, (!amend || (fixup_message && strcmp(fixup_prefix, "amend")))))) die(_("No paths with --include/--only does not make sense.")); - if (read_cache_preload(&pathspec) < 0) + if (repo_read_index_preload(the_repository, &pathspec, 0) < 0) die(_("index file corrupt")); if (interactive) { char *old_index_env = NULL, *old_repo_index_file; - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &index_lock, + LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); @@ -403,9 +411,10 @@ static const char *prepare_index(const char **argv, const char *prefix, unsetenv(INDEX_ENVIRONMENT); FREE_AND_NULL(old_index_env); - discard_cache(); - read_cache_from(get_lock_file_path(&index_lock)); - if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { + discard_index(&the_index); + read_index_from(&the_index, get_lock_file_path(&index_lock), + get_git_dir()); + if (cache_tree_update(&the_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)) @@ -431,10 +440,11 @@ static const char *prepare_index(const char **argv, const char *prefix, * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &index_lock, + LOCK_DIE_ON_ERROR); add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); - update_main_cache_tree(WRITE_TREE_SILENT); + cache_tree_update(&the_index, WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; @@ -452,11 +462,12 @@ static const char *prepare_index(const char **argv, const char *prefix, * We still need to refresh the index here. */ if (!only && !pathspec.nr) { - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &index_lock, + LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); - if (active_cache_changed - || !cache_tree_fully_valid(active_cache_tree)) - update_main_cache_tree(WRITE_TREE_SILENT); + 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, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write new_index file")); @@ -498,14 +509,14 @@ static const char *prepare_index(const char **argv, const char *prefix, if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec)) exit(1); - discard_cache(); - if (read_cache() < 0) + discard_index(&the_index); + if (repo_read_index(the_repository) < 0) die(_("cannot read the index")); - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR); add_remove_files(&partial); - refresh_cache(REFRESH_QUIET); - update_main_cache_tree(WRITE_TREE_SILENT); + 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)) die(_("unable to write new_index file")); @@ -516,14 +527,14 @@ static const char *prepare_index(const char **argv, const char *prefix, create_base_index(current_head); add_remove_files(&partial); - refresh_cache(REFRESH_QUIET); + refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); if (write_locked_index(&the_index, &false_lock, 0)) die(_("unable to write temporary index file")); - discard_cache(); + discard_index(&the_index); ret = get_lock_file_path(&false_lock); - read_cache_from(ret); + read_index_from(&the_index, ret, get_git_dir()); out: string_list_clear(&partial, 0); clear_pathspec(&pathspec); @@ -980,8 +991,11 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct object_id oid; const char *parent = "HEAD"; - if (!active_nr && read_cache() < 0) - die(_("Cannot read index")); + if (!the_index.cache_nr) { + discard_index(&the_index); + if (repo_read_index(the_repository) < 0) + die(_("Cannot read index")); + } if (amend) parent = "HEAD^1"; @@ -991,10 +1005,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix, /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) - if (ce_intent_to_add(active_cache[i])) + for (i = 0; i < the_index.cache_nr; i++) + if (ce_intent_to_add(the_index.cache[i])) ita_nr++; - committable = active_nr - ita_nr > 0; + committable = the_index.cache_nr - ita_nr > 0; } else { /* * Unless the user did explicitly request a submodule @@ -1061,11 +1075,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_cache(); + discard_index(&the_index); } - read_cache_from(index_file); + read_index_from(&the_index, index_file, get_git_dir()); - if (update_main_cache_tree(0)) { + if (cache_tree_update(&the_index, 0)) { error(_("Error building trees")); return 0; } @@ -1549,7 +1563,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) &s.pathspec, NULL, NULL); if (use_optional_locks()) - fd = hold_locked_index(&index_lock, 0); + fd = repo_hold_locked_index(the_repository, &index_lock, 0); else fd = -1; @@ -1586,7 +1600,6 @@ int cmd_status(int argc, const char **argv, const char *prefix) static int git_commit_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; - int status; if (!strcmp(k, "commit.template")) return git_config_pathname(&template_file, k, v); @@ -1606,9 +1619,6 @@ static int git_commit_config(const char *k, const char *v, void *cb) return 0; } - status = git_gpg_config(k, v, NULL); - if (status) - return status; return git_status_config(k, v, s); } @@ -1633,7 +1643,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer), + OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer), OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), @@ -1816,7 +1826,7 @@ 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, &active_cache_tree->oid, + if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid, parents, &oid, author_ident.buf, NULL, sign_commit, extra)) { rollback_index_files(); @@ -1864,8 +1874,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) apply_autostash(git_path_merge_autostash(the_repository)); cleanup: - UNLEAK(author_ident); - UNLEAK(err); - UNLEAK(sb); + strbuf_release(&author_ident); + strbuf_release(&err); + strbuf_release(&sb); return ret; } diff --git a/builtin/config.c b/builtin/config.c index e7b88a9c08..49d832d409 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -1,7 +1,8 @@ #include "builtin.h" -#include "cache.h" +#include "alloc.h" #include "config.h" #include "color.h" +#include "ident.h" #include "parse-options.h" #include "urlmatch.h" #include "quote.h" @@ -207,7 +208,8 @@ static void show_config_scope(struct strbuf *buf) strbuf_addch(buf, term); } -static int show_all_config(const char *key_, const char *value_, void *cb) +static int show_all_config(const char *key_, const char *value_, + void *cb UNUSED) { if (show_origin || show_scope) { struct strbuf buf = STRBUF_INIT; @@ -458,7 +460,8 @@ static const char *get_color_slot; static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; -static int git_get_color_config(const char *var, const char *value, void *cb) +static int git_get_color_config(const char *var, const char *value, + void *cb UNUSED) { if (!strcmp(var, get_color_slot)) { if (!value) @@ -490,7 +493,7 @@ static int get_colorbool_found; static int get_diff_color_found; static int get_color_ui_found; static int git_get_colorbool_config(const char *var, const char *value, - void *cb) + void *data UNUSED) { if (!strcmp(var, get_colorbool_slot)) get_colorbool_found = git_config_colorbool(var, value); @@ -637,8 +640,9 @@ static char *default_user_config(void) int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = !startup_info->have_repository; - char *value; + char *value = NULL; int flags = 0; + int ret = 0; given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); @@ -854,44 +858,38 @@ int cmd_config(int argc, const char **argv, const char *prefix) free(config_file); } else if (actions == ACTION_SET) { - int ret; check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - UNLEAK(value); ret = git_config_set_in_file_gently(given_config_source.file, argv[0], 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]); - return ret; } else if (actions == ACTION_SET_ALL) { check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], - flags); + ret = git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, argv[2], + flags); } else if (actions == ACTION_ADD) { check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, - CONFIG_REGEX_NONE, - flags); + ret = git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, + CONFIG_REGEX_NONE, + flags); } else if (actions == ACTION_REPLACE_ALL) { check_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], - flags | CONFIG_FLAGS_MULTI_REPLACE); + ret = git_config_set_multivar_in_file_gently(given_config_source.file, + argv[0], value, argv[2], + flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); @@ -932,26 +930,28 @@ int cmd_config(int argc, const char **argv, const char *prefix) flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { - int ret; check_write(); check_argc(argc, 2, 2); ret = git_config_rename_section_in_file(given_config_source.file, argv[0], argv[1]); if (ret < 0) return ret; - if (ret == 0) + else if (!ret) die(_("no such section: %s"), argv[0]); + else + ret = 0; } else if (actions == ACTION_REMOVE_SECTION) { - int ret; check_write(); check_argc(argc, 1, 1); ret = git_config_rename_section_in_file(given_config_source.file, argv[0], NULL); if (ret < 0) return ret; - if (ret == 0) + else if (!ret) die(_("no such section: %s"), argv[0]); + else + ret = 0; } else if (actions == ACTION_GET_COLOR) { check_argc(argc, 1, 2); @@ -964,5 +964,6 @@ int cmd_config(int argc, const char **argv, const char *prefix) return get_colorbool(argv[0], argc == 2); } - return 0; + free(value); + return ret; } diff --git a/builtin/count-objects.c b/builtin/count-objects.c index 07b9419596..bb21bc43e4 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -57,7 +57,8 @@ static void loose_garbage(const char *path) report_garbage(PACKDIR_FILE_GARBAGE, path); } -static int count_loose(const struct object_id *oid, const char *path, void *data) +static int count_loose(const struct object_id *oid, const char *path, + void *data UNUSED) { struct stat st; @@ -72,7 +73,8 @@ static int count_loose(const struct object_id *oid, const char *path, void *data return 0; } -static int count_cruft(const char *basename, const char *path, void *data) +static int count_cruft(const char *basename UNUSED, const char *path, + void *data UNUSED) { loose_garbage(path); return 0; diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c index 4c6c89ab0d..6e509d02c3 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "alloc.h" #include "parse-options.h" #ifndef NO_UNIX_SOCKETS @@ -127,6 +128,9 @@ static void serve_one_client(FILE *in, FILE *out) if (e) { fprintf(out, "username=%s\n", e->item.username); fprintf(out, "password=%s\n", e->item.password); + if (e->item.password_expiry_utc != TIME_MAX) + fprintf(out, "password_expiry_utc=%"PRItime"\n", + e->item.password_expiry_utc); } } else if (!strcmp(action.buf, "exit")) { @@ -267,7 +271,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) const char *socket_path; int ignore_sighup = 0; static const char *usage[] = { - "git-credential-cache--daemon [opts] <socket_path>", + "git credential-cache--daemon [--debug] <socket-path>", NULL }; int debug = 0; @@ -305,7 +309,7 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) { const char * const usage[] = { - "git credential-cache--daemon [options] <action>", + "git credential-cache--daemon [--debug] <socket-path>", "", "credential-cache--daemon is disabled in this build of Git", NULL diff --git a/builtin/describe.c b/builtin/describe.c index a76f1a1a7a..5b5930f5c8 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -1,6 +1,7 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "commit.h" #include "tag.h" @@ -23,8 +24,9 @@ define_commit_slab(commit_names, struct commit_name *); static const char * const describe_usage[] = { - N_("git describe [<options>] [<commit-ish>...]"), - N_("git describe [<options>] --dirty"), + N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"), + N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"), + N_("git describe <blob>"), NULL }; @@ -63,7 +65,7 @@ static const char *prio_names[] = { N_("head"), N_("lightweight"), N_("annotated"), }; -static int commit_name_neq(const void *unused_cmp_data, +static int commit_name_neq(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *peeled) @@ -140,7 +142,8 @@ static void add_to_known_names(const char *path, } } -static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data) +static int get_name(const char *path, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { int is_tag = 0; struct object_id peeled; @@ -651,10 +654,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) int fd, result; setup_work_tree(); - read_cache(); + repo_read_index(the_repository); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); - fd = hold_locked_index(&index_lock, 0); + fd = repo_hold_locked_index(the_repository, + &index_lock, 0); if (0 <= fd) repo_update_index_if_able(the_repository, &index_lock); diff --git a/builtin/diagnose.c b/builtin/diagnose.c new file mode 100644 index 0000000000..d52015c67a --- /dev/null +++ b/builtin/diagnose.c @@ -0,0 +1,62 @@ +#include "builtin.h" +#include "parse-options.h" +#include "diagnose.h" + +static const char * const diagnose_usage[] = { + N_("git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n" + " [--mode=<mode>]"), + NULL +}; + +int cmd_diagnose(int argc, const char **argv, const char *prefix) +{ + struct strbuf zip_path = STRBUF_INIT; + time_t now = time(NULL); + struct tm tm; + enum diagnose_mode mode = DIAGNOSE_STATS; + char *option_output = NULL; + char *option_suffix = "%Y-%m-%d-%H%M"; + char *prefixed_filename; + + const struct option diagnose_options[] = { + OPT_STRING('o', "output-directory", &option_output, N_("path"), + N_("specify a destination for the diagnostics archive")), + OPT_STRING('s', "suffix", &option_suffix, N_("format"), + N_("specify a strftime format suffix for the filename")), + OPT_CALLBACK_F(0, "mode", &mode, "(stats|all)", + N_("specify the content of the diagnostic archive"), + PARSE_OPT_NONEG, option_parse_diagnose), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, diagnose_options, + diagnose_usage, 0); + + /* Prepare the path to put the result */ + prefixed_filename = prefix_filename(prefix, + option_output ? option_output : ""); + strbuf_addstr(&zip_path, prefixed_filename); + strbuf_complete(&zip_path, '/'); + + strbuf_addstr(&zip_path, "git-diagnostics-"); + strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0); + strbuf_addstr(&zip_path, ".zip"); + + switch (safe_create_leading_directories(zip_path.buf)) { + case SCLD_OK: + case SCLD_EXISTS: + break; + default: + die_errno(_("could not create leading directories for '%s'"), + zip_path.buf); + } + + /* Prepare diagnostics */ + if (create_diagnostics_archive(&zip_path, mode)) + die_errno(_("unable to create diagnostics archive %s"), + zip_path.buf); + + free(prefixed_filename); + strbuf_release(&zip_path); + return 0; +} diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 2bfaf9ba7a..dc991f753b 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -3,7 +3,6 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "diff.h" @@ -15,6 +14,7 @@ static const char diff_files_usage[] = "git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]" +"\n" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_files(int argc, const char **argv, const char *prefix) @@ -75,14 +75,14 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) diff_merges_set_dense_combined_if_unset(&rev); - if (read_cache_preload(&rev.diffopt.pathspec) < 0) { - perror("read_cache_preload"); + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { + perror("repo_read_index_preload"); result = -1; goto cleanup; } -cleanup: result = run_diff_files(&rev, options); result = diff_result_code(&rev.diffopt, result); +cleanup: release_revisions(&rev); return result; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 7d158af6b6..35dc9b23ee 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -1,4 +1,3 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "config.h" #include "diff.h" @@ -9,8 +8,9 @@ #include "submodule.h" static const char diff_cache_usage[] = -"git diff-index [-m] [--cached] " +"git diff-index [-m] [--cached] [--merge-base] " "[<common-diff-options>] <tree-ish> [<path>...]" +"\n" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_index(int argc, const char **argv, const char *prefix) @@ -61,12 +61,12 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) usage(diff_cache_usage); if (!(option & DIFF_INDEX_CACHED)) { setup_work_tree(); - if (read_cache_preload(&rev.diffopt.pathspec) < 0) { - perror("read_cache_preload"); + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { + perror("repo_read_index_preload"); return -1; } - } else if (read_cache() < 0) { - perror("read_cache"); + } else if (repo_read_index(the_repository) < 0) { + perror("repo_read_index"); return -1; } result = run_diff_index(&rev, option); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 116097a404..a393efa4f0 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -1,8 +1,9 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "diff.h" #include "commit.h" +#include "hex.h" #include "log-tree.h" #include "builtin.h" #include "submodule.h" @@ -83,8 +84,10 @@ static int diff_tree_stdin(char *line) } static const char diff_tree_usage[] = -"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " -"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n" +"git diff-tree [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]\n" +" [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]\n" +" [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n" +"\n" " -r diff recursively\n" " -c show combined diff for merge commits\n" " --cc show combined diff for merge commits removing uninteresting hunks\n" @@ -118,7 +121,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ repo_init_revisions(the_repository, opt, prefix); - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); opt->abbrev = 0; opt->diff = 1; diff --git a/builtin/diff.c b/builtin/diff.c index 54bb3de964..26f1e532c6 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -3,7 +3,7 @@ * * Copyright (c) 2006 Junio C Hamano */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "ewah/ewok.h" @@ -30,7 +30,8 @@ static const char builtin_diff_usage[] = " or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n" " or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n" " or: git diff [<options>] <blob> <blob>\n" -" or: git diff [<options>] --no-index [--] <path> <path>\n" +" or: git diff [<options>] --no-index [--] <path> <path>" +"\n" COMMON_DIFF_OPTIONS_HELP; static const char *blob_path(struct object_array_entry *entry) @@ -156,12 +157,13 @@ static int builtin_diff_index(struct rev_info *revs, usage(builtin_diff_usage); if (!(option & DIFF_INDEX_CACHED)) { setup_work_tree(); - if (read_cache_preload(&revs->diffopt.pathspec) < 0) { - perror("read_cache_preload"); + if (repo_read_index_preload(the_repository, + &revs->diffopt.pathspec, 0) < 0) { + perror("repo_read_index_preload"); return -1; } - } else if (read_cache() < 0) { - perror("read_cache"); + } else if (repo_read_index(the_repository) < 0) { + perror("repo_read_cache"); return -1; } return run_diff_index(revs, option); @@ -209,7 +211,7 @@ static int builtin_diff_tree(struct rev_info *revs, static int builtin_diff_combined(struct rev_info *revs, int argc, const char **argv, struct object_array_entry *ent, - int ents) + int ents, int first_non_parent) { struct oid_array parents = OID_ARRAY_INIT; int i; @@ -217,11 +219,18 @@ static int builtin_diff_combined(struct rev_info *revs, if (argc > 1) usage(builtin_diff_usage); + if (first_non_parent < 0) + die(_("no merge given, only parents.")); + if (first_non_parent >= ents) + BUG("first_non_parent out of range: %d", first_non_parent); + diff_merges_set_dense_combined_if_unset(revs); - for (i = 1; i < ents; i++) - oid_array_append(&parents, &ent[i].item->oid); - diff_tree_combined(&ent[0].item->oid, &parents, revs); + for (i = 0; i < ents; i++) { + if (i != first_non_parent) + oid_array_append(&parents, &ent[i].item->oid); + } + diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs); oid_array_clear(&parents); return 0; } @@ -231,12 +240,13 @@ static void refresh_index_quietly(void) struct lock_file lock_file = LOCK_INIT; int fd; - fd = hold_locked_index(&lock_file, 0); + fd = repo_hold_locked_index(the_repository, &lock_file, 0); if (fd < 0) return; - discard_cache(); - read_cache(); - refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); + discard_index(&the_index); + repo_read_index(the_repository); + refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, + NULL); repo_update_index_if_able(the_repository, &lock_file); } @@ -271,8 +281,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv diff_merges_set_dense_combined_if_unset(revs); setup_work_tree(); - if (read_cache_preload(&revs->diffopt.pathspec) < 0) { - perror("read_cache_preload"); + if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, + 0) < 0) { + perror("repo_read_index_preload"); return -1; } return run_diff_files(revs, options); @@ -385,6 +396,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) int i; struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; + int first_non_parent = -1; int blobs = 0, paths = 0; struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; @@ -543,6 +555,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix) continue; obj->flags |= flags; add_object_array(obj, name, &ent); + if (first_non_parent < 0 && + (i >= rev.cmdline.nr || /* HEAD by hand. */ + rev.cmdline.rev[i].whence != REV_CMD_PARENTS_ONLY)) + first_non_parent = ent.nr - 1; } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); @@ -590,12 +606,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix) &ent.objects[0], &ent.objects[1]); } else result = builtin_diff_combined(&rev, argc, argv, - ent.objects, ent.nr); + ent.objects, ent.nr, + first_non_parent); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); release_revisions(&rev); - UNLEAK(ent); + object_array_clear(&ent); UNLEAK(blob); return result; } diff --git a/builtin/difftool.c b/builtin/difftool.c index b3c509b8de..01681d0fb8 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -11,12 +11,13 @@ * * Copyright (C) 2016 Johannes Schindelin */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "builtin.h" #include "run-command.h" #include "exec-cmd.h" +#include "hex.h" #include "parse-options.h" #include "strvec.h" #include "strbuf.h" @@ -44,8 +45,11 @@ static int difftool_config(const char *var, const char *value, void *cb) static int print_tool_help(void) { - const char *argv[] = { "mergetool", "--tool-help=diff", NULL }; - return run_command_v_opt(argv, RUN_GIT_CMD); + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "mergetool", "--tool-help=diff", NULL); + return run_command(&cmd); } static int parse_index_info(char *p, int *mode1, int *mode2, @@ -125,10 +129,10 @@ struct working_tree_entry { char path[FLEX_ARRAY]; }; -static int working_tree_entry_cmp(const void *unused_cmp_data, +static int working_tree_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct working_tree_entry *a, *b; @@ -148,10 +152,10 @@ struct pair_entry { const char path[FLEX_ARRAY]; }; -static int pair_cmp(const void *unused_cmp_data, +static int pair_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *unused_keydata) + const void *keydata UNUSED) { const struct pair_entry *a, *b; @@ -184,7 +188,7 @@ struct path_entry { char path[FLEX_ARRAY]; }; -static int path_entry_cmp(const void *unused_cmp_data, +static int path_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *key) @@ -358,10 +362,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL); struct hashmap_iter iter; struct pair_entry *entry; - struct index_state wtindex; + struct index_state wtindex = INDEX_STATE_INIT(the_repository); struct checkout lstate, rstate; - int flags = RUN_GIT_CMD, err = 0; - const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; + int err = 0; + struct child_process cmd = CHILD_PROCESS_INIT; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; @@ -384,8 +388,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, mkdir(ldir.buf, 0700); mkdir(rdir.buf, 0700); - memset(&wtindex, 0, sizeof(wtindex)); - memset(&lstate, 0, sizeof(lstate)); lstate.base_dir = lbase_dir = xstrdup(ldir.buf); lstate.base_dir_len = ldir.len; @@ -563,16 +565,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, } strbuf_setlen(&ldir, ldir_len); - helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); - helper_argv[2] = rdir.buf; if (extcmd) { - helper_argv[0] = extcmd; - flags = 0; - } else + strvec_push(&cmd.args, extcmd); + } else { + strvec_push(&cmd.args, "difftool--helper"); + cmd.git_cmd = 1; setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); - ret = run_command_v_opt(helper_argv, flags); + } + strvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL); + ret = run_command(&cmd); /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&wtindex); @@ -716,7 +719,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) symlinks = has_symlinks; argc = parse_options(argc, argv, prefix, builtin_difftool_options, - builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN | + builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); if (tool_help) diff --git a/builtin/env--helper.c b/builtin/env--helper.c deleted file mode 100644 index 27349098b0..0000000000 --- a/builtin/env--helper.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "parse-options.h" - -static char const * const env__helper_usage[] = { - N_("git env--helper --type=[bool|ulong] <options> <env-var>"), - NULL -}; - -enum cmdmode { - ENV_HELPER_TYPE_BOOL = 1, - ENV_HELPER_TYPE_ULONG -}; - -static int option_parse_type(const struct option *opt, const char *arg, - int unset) -{ - enum cmdmode *cmdmode = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (!strcmp(arg, "bool")) - *cmdmode = ENV_HELPER_TYPE_BOOL; - else if (!strcmp(arg, "ulong")) - *cmdmode = ENV_HELPER_TYPE_ULONG; - else - die(_("unrecognized --type argument, %s"), arg); - - return 0; -} - -int cmd_env__helper(int argc, const char **argv, const char *prefix) -{ - int exit_code = 0; - const char *env_variable = NULL; - const char *env_default = NULL; - int ret; - int ret_int, default_int; - unsigned long ret_ulong, default_ulong; - enum cmdmode cmdmode = 0; - struct option opts[] = { - OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"), - N_("value is given this type"), PARSE_OPT_NONEG, - option_parse_type), - OPT_STRING(0, "default", &env_default, N_("value"), - N_("default for git_env_*(...) to fall back on")), - OPT_BOOL(0, "exit-code", &exit_code, - N_("be quiet only use git_env_*() value as exit code")), - OPT_END(), - }; - - argc = parse_options(argc, argv, prefix, opts, env__helper_usage, - PARSE_OPT_KEEP_UNKNOWN); - if (env_default && !*env_default) - usage_with_options(env__helper_usage, opts); - if (!cmdmode) - usage_with_options(env__helper_usage, opts); - if (argc != 1) - usage_with_options(env__helper_usage, opts); - env_variable = argv[0]; - - switch (cmdmode) { - case ENV_HELPER_TYPE_BOOL: - if (env_default) { - default_int = git_parse_maybe_bool(env_default); - if (default_int == -1) { - error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"), - env_default); - usage_with_options(env__helper_usage, opts); - } - } else { - default_int = 0; - } - ret_int = git_env_bool(env_variable, default_int); - if (!exit_code) - puts(ret_int ? "true" : "false"); - ret = ret_int; - break; - case ENV_HELPER_TYPE_ULONG: - if (env_default) { - if (!git_parse_ulong(env_default, &default_ulong)) { - error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"), - env_default); - usage_with_options(env__helper_usage, opts); - } - } else { - default_ulong = 0; - } - ret_ulong = git_env_ulong(env_variable, default_ulong); - if (!exit_code) - printf("%lu\n", ret_ulong); - ret = ret_ulong; - break; - default: - BUG("unknown <type> value"); - break; - } - - return !ret; -} diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e1748fb98b..78493c6d2b 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -6,6 +6,7 @@ #include "builtin.h" #include "cache.h" #include "config.h" +#include "hex.h" #include "refs.h" #include "refspec.h" #include "object-store.h" @@ -119,7 +120,7 @@ struct anonymized_entry_key { size_t orig_len; }; -static int anonymized_entry_cmp(const void *unused_cmp_data, +static int anonymized_entry_cmp(const void *cmp_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -409,7 +410,7 @@ static const char *anonymize_oid(const char *oid_hex) } static void show_filemodify(struct diff_queue_struct *q, - struct diff_options *options, void *data) + struct diff_options *options UNUSED, void *data) { int i; struct string_list *changed = data; @@ -1221,7 +1222,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) revs.sources = &revision_sources; revs.rewrite_parents = 1; argc = parse_options(argc, argv, prefix, options, fast_export_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) usage_with_options (fast_export_usage, options); diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 28d3193c38..f7548ff4a3 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "lockfile.h" @@ -46,7 +47,7 @@ struct object_entry { depth : DEPTH_BITS; }; -static int object_entry_hashcmp(const void *map_data, +static int object_entry_hashcmp(const void *map_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -436,7 +437,7 @@ static void set_checkpoint_signal(void) #else -static void checkpoint_signal(int signo) +static void checkpoint_signal(int signo UNUSED) { checkpoint_requested = 1; } @@ -3465,7 +3466,7 @@ static void git_pack_config(void) pack_idx_opts.version = indexversion_value; if (pack_idx_opts.version > 2) git_die_config("pack.indexversion", - "bad pack.indexversion=%"PRIu32, pack_idx_opts.version); + "bad pack.indexVersion=%"PRIu32, pack_idx_opts.version); } if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value)) max_packsize = packsizelimit_value; diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index f045bbbe94..702c9a3397 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -1,4 +1,6 @@ #include "builtin.h" +#include "alloc.h" +#include "hex.h" #include "pkt-line.h" #include "fetch-pack.h" #include "remote.h" @@ -62,6 +64,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("fetch-pack"); memset(&args, 0, sizeof(args)); + list_objects_filter_init(&args.filter_options); args.uploadpack = "git-upload-pack"; for (i = 1; i < argc && *argv[i] == '-'; i++) { diff --git a/builtin/fetch.c b/builtin/fetch.c index ac29c2b1ae..7221e57f35 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -3,6 +3,7 @@ */ #include "cache.h" #include "config.h" +#include "hex.h" #include "repository.h" #include "refs.h" #include "refspec.h" @@ -29,6 +30,7 @@ #include "commit-graph.h" #include "shallow.h" #include "worktree.h" +#include "bundle-uri.h" #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000) @@ -80,7 +82,7 @@ static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static int shown_url = 0; static struct refspec refmap = REFSPEC_INIT_FETCH; -static struct list_objects_filter_options filter_options; +static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static struct string_list server_options = STRING_LIST_INIT_DUP; static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; static int fetch_write_commit_graph = -1; @@ -122,6 +124,8 @@ static int git_fetch_config(const char *k, const char *v, void *cb) fetch_parallel_config = git_config_int(k, v); if (fetch_parallel_config < 0) die(_("fetch.parallel cannot be negative")); + if (!fetch_parallel_config) + fetch_parallel_config = online_cpus(); return 0; } @@ -301,7 +305,7 @@ struct refname_hash_entry { char refname[FLEX_ARRAY]; }; -static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data, +static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, const void *keydata) @@ -329,7 +333,7 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map, static int add_one_refname(const char *refname, const struct object_id *oid, - int flag, void *cbdata) + int flag UNUSED, void *cbdata) { struct hashmap *refname_map = cbdata; @@ -490,7 +494,9 @@ static void filter_prefetch_refspec(struct refspec *rs) continue; if (!rs->items[i].dst || (rs->items[i].src && - !strncmp(rs->items[i].src, "refs/tags/", 10))) { + !strncmp(rs->items[i].src, + ref_namespace[NAMESPACE_TAGS].ref, + strlen(ref_namespace[NAMESPACE_TAGS].ref)))) { int j; free(rs->items[i].src); @@ -506,7 +512,7 @@ static void filter_prefetch_refspec(struct refspec *rs) } old_dst = rs->items[i].dst; - strbuf_addstr(&new_dst, "refs/prefetch/"); + strbuf_addstr(&new_dst, ref_namespace[NAMESPACE_PREFETCH].ref); /* * If old_dst starts with "refs/", then place @@ -881,11 +887,9 @@ static void format_display(struct strbuf *display, char code, static int update_local_ref(struct ref *ref, struct ref_transaction *transaction, const char *remote, const struct ref *remote_ref, - struct strbuf *display, int summary_width, - struct worktree **worktrees) + struct strbuf *display, int summary_width) { struct commit *current = NULL, *updated; - const struct worktree *wt; const char *pretty_ref = prettify_refname(ref->name); int fast_forward = 0; @@ -900,16 +904,14 @@ static int update_local_ref(struct ref *ref, } if (!update_head_ok && - (wt = find_shared_symref(worktrees, "HEAD", ref->name)) && - !wt->is_bare && !is_null_oid(&ref->old_oid)) { + !is_null_oid(&ref->old_oid) && + branch_checked_out(ref->name)) { /* * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ format_display(display, '!', _("[rejected]"), - wt->is_current ? - _("can't fetch in current branch") : - _("checked out in another worktree"), + _("can't fetch into checked-out branch"), remote, pretty_ref, summary_width); return 1; } @@ -1110,10 +1112,10 @@ N_("it took %.2f seconds to check forced updates; you can use\n" static int store_updated_refs(const char *raw_url, const char *remote_name, int connectivity_checked, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head, struct worktree **worktrees) + struct fetch_head *fetch_head) { int url_len, i, rc = 0; - struct strbuf note = STRBUF_INIT, err = STRBUF_INIT; + struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; char *url; @@ -1131,6 +1133,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, if (!connectivity_checked) { struct check_connected_options opt = CHECK_CONNECTED_INIT; + opt.exclude_hidden_refs_section = "fetch"; rm = ref_map; if (check_connected(iterate_ref_map, &rm, &opt)) { rc = error(_("%s did not send all necessary objects\n"), url); @@ -1240,8 +1243,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, strbuf_reset(¬e); if (ref) { rc |= update_local_ref(ref, transaction, what, - rm, ¬e, summary_width, - worktrees); + rm, ¬e, summary_width); free(ref); } else if (write_fetch_head || dry_run) { /* @@ -1281,7 +1283,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, abort: strbuf_release(¬e); - strbuf_release(&err); free(url); return rc; } @@ -1326,14 +1327,14 @@ static int check_exist_and_connected(struct ref *ref_map) } opt.quiet = 1; + opt.exclude_hidden_refs_section = "fetch"; return check_connected(iterate_ref_map, &rm, &opt); } static int fetch_and_consume_refs(struct transport *transport, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head, - struct worktree **worktrees) + struct fetch_head *fetch_head) { int connectivity_checked = 1; int ret; @@ -1356,7 +1357,7 @@ static int fetch_and_consume_refs(struct transport *transport, trace2_region_enter("fetch", "consume_refs", the_repository); ret = store_updated_refs(transport->url, transport->remote->name, connectivity_checked, transaction, ref_map, - fetch_head, worktrees); + fetch_head); trace2_region_leave("fetch", "consume_refs", the_repository); out: @@ -1434,19 +1435,16 @@ cleanup: return result; } -static void check_not_current_branch(struct ref *ref_map, - struct worktree **worktrees) +static void check_not_current_branch(struct ref *ref_map) { - const struct worktree *wt; + const char *path; for (; ref_map; ref_map = ref_map->next) if (ref_map->peer_ref && starts_with(ref_map->peer_ref->name, "refs/heads/") && - (wt = find_shared_symref(worktrees, "HEAD", - ref_map->peer_ref->name)) && - !wt->is_bare) + (path = branch_checked_out(ref_map->peer_ref->name))) die(_("refusing to fetch into branch '%s' " "checked out at '%s'"), - ref_map->peer_ref->name, wt->path); + ref_map->peer_ref->name, path); } static int truncate_fetch_head(void) @@ -1472,8 +1470,9 @@ static void set_option(struct transport *transport, const char *name, const char } -static int add_oid(const char *refname, const struct object_id *oid, int flags, - void *cb_data) +static int add_oid(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct oid_array *oids = cb_data; @@ -1549,8 +1548,7 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) static int backfill_tags(struct transport *transport, struct ref_transaction *transaction, struct ref *ref_map, - struct fetch_head *fetch_head, - struct worktree **worktrees) + struct fetch_head *fetch_head) { int retcode, cannot_reuse; @@ -1571,7 +1569,7 @@ static int backfill_tags(struct transport *transport, transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); - retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head, worktrees); + retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head); if (gsecondary) { transport_disconnect(gsecondary); @@ -1592,7 +1590,6 @@ static int do_fetch(struct transport *transport, struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; int must_list_refs = 1; - struct worktree **worktrees = get_worktrees(); struct fetch_head fetch_head = { 0 }; struct strbuf err = STRBUF_INIT; @@ -1626,9 +1623,21 @@ static int do_fetch(struct transport *transport, break; } } - } else if (transport->remote && transport->remote->fetch.nr) - refspec_ref_prefixes(&transport->remote->fetch, - &transport_ls_refs_options.ref_prefixes); + } else { + struct branch *branch = branch_get(NULL); + + if (transport->remote->fetch.nr) + refspec_ref_prefixes(&transport->remote->fetch, + &transport_ls_refs_options.ref_prefixes); + if (branch_has_merge_config(branch) && + !strcmp(branch->remote_name, transport->remote->name)) { + int i; + for (i = 0; i < branch->merge_nr; i++) { + strvec_push(&transport_ls_refs_options.ref_prefixes, + branch->merge[i]->src); + } + } + } if (tags == TAGS_SET || tags == TAGS_DEFAULT) { must_list_refs = 1; @@ -1650,7 +1659,7 @@ static int do_fetch(struct transport *transport, ref_map = get_ref_map(transport->remote, remote_refs, rs, tags, &autotags); if (!update_head_ok) - check_not_current_branch(ref_map, worktrees); + check_not_current_branch(ref_map); retcode = open_fetch_head(&fetch_head); if (retcode) @@ -1683,7 +1692,7 @@ static int do_fetch(struct transport *transport, retcode = 1; } - if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head, worktrees)) { + if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head)) { retcode = 1; goto cleanup; } @@ -1706,7 +1715,7 @@ static int do_fetch(struct transport *transport, * the transaction and don't commit anything. */ if (backfill_tags(transport, transaction, tags_ref_map, - &fetch_head, worktrees)) + &fetch_head)) retcode = 1; } @@ -1791,7 +1800,6 @@ cleanup: close_fetch_head(&fetch_head); strbuf_release(&err); free_refs(ref_map); - free_worktrees(worktrees); return retcode; } @@ -1875,6 +1883,8 @@ static void add_options_to_argv(struct strvec *argv) strvec_push(argv, "--ipv4"); else if (family == TRANSPORT_FAMILY_IPV6) strvec_push(argv, "--ipv6"); + if (!write_fetch_head) + strvec_push(argv, "--no-write-fetch-head"); } /* Fetch multiple remotes in parallel */ @@ -1885,7 +1895,8 @@ struct parallel_fetch_state { int next, result; }; -static int fetch_next_remote(struct child_process *cp, struct strbuf *out, +static int fetch_next_remote(struct child_process *cp, + struct strbuf *out UNUSED, void *cb, void **task_cb) { struct parallel_fetch_state *state = cb; @@ -1907,7 +1918,8 @@ static int fetch_next_remote(struct child_process *cp, struct strbuf *out, return 1; } -static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb) +static int fetch_failed_to_start(struct strbuf *out UNUSED, + void *cb, void *task_cb) { struct parallel_fetch_state *state = cb; const char *remote = task_cb; @@ -1949,28 +1961,36 @@ static int fetch_multiple(struct string_list *list, int max_children) if (max_children != 1 && list->nr != 1) { struct parallel_fetch_state state = { argv.v, list, 0, 0 }; + const struct run_process_parallel_opts opts = { + .tr2_category = "fetch", + .tr2_label = "parallel/fetch", + + .processes = max_children, + + .get_next_task = &fetch_next_remote, + .start_failure = &fetch_failed_to_start, + .task_finished = &fetch_finished, + .data = &state, + }; strvec_push(&argv, "--end-of-options"); - result = run_processes_parallel_tr2(max_children, - &fetch_next_remote, - &fetch_failed_to_start, - &fetch_finished, - &state, - "fetch", "parallel/fetch"); - - if (!result) - result = state.result; + + run_processes_parallel(&opts); + result = state.result; } else for (i = 0; i < list->nr; i++) { const char *name = list->items[i].string; - strvec_push(&argv, name); + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushv(&cmd.args, argv.v); + strvec_push(&cmd.args, name); if (verbosity >= 0) printf(_("Fetching %s\n"), name); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + if (run_command(&cmd)) { error(_("could not fetch %s"), name); result = 1; } - strvec_pop(&argv); } strvec_clear(&argv); @@ -2097,6 +2117,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int cmd_fetch(int argc, const char **argv, const char *prefix) { int i; + const char *bundle_uri; struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote = NULL; int result = 0; @@ -2182,6 +2203,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (dry_run) write_fetch_head = 0; + if (!max_jobs) + max_jobs = online_cpus(); + + if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) && + fetch_bundle_uri(the_repository, bundle_uri, NULL)) + warning(_("failed to fetch bundles from '%s'"), bundle_uri); + if (all) { if (argc == 1) die(_("fetch --all does not take a repository argument")); @@ -2216,6 +2244,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) argv++; } } + string_list_remove_duplicates(&list, 0); if (negotiate_only) { struct oidset acked_commits = OIDSET_INIT; diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c index fd86e5a861..6aeac37148 100644 --- a/builtin/for-each-repo.c +++ b/builtin/for-each-repo.c @@ -6,7 +6,7 @@ #include "string-list.h" static const char * const for_each_repo_usage[] = { - N_("git for-each-repo --config=<config> <command-args>"), + N_("git for-each-repo --config=<config> [--] <arguments>"), NULL }; @@ -14,13 +14,16 @@ static int run_command_on_repo(const char *path, int argc, const char ** argv) { int i; struct child_process child = CHILD_PROCESS_INIT; + char *abspath = interpolate_path(path, 0); child.git_cmd = 1; - strvec_pushl(&child.args, "-C", path, NULL); + strvec_pushl(&child.args, "-C", abspath, NULL); for (i = 0; i < argc; i++) strvec_push(&child.args, argv[i]); + free(abspath); + return run_command(&child); } diff --git a/builtin/fsck.c b/builtin/fsck.c index 9e54892311..c4a633c032 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,6 +1,6 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "cache.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "commit.h" @@ -19,6 +19,8 @@ #include "decorate.h" #include "packfile.h" #include "object-store.h" +#include "replace-object.h" +#include "resolve-undo.h" #include "run-command.h" #include "worktree.h" @@ -227,20 +229,22 @@ static void mark_unreachable_referents(const struct object_id *oid) options.walk = mark_used; fsck_walk(obj, NULL, &options); + if (obj->type == OBJ_TREE) + free_tree_buffer((struct tree *)obj); } static int mark_loose_unreachable_referents(const struct object_id *oid, - const char *path, - void *data) + const char *path UNUSED, + void *data UNUSED) { mark_unreachable_referents(oid); return 0; } static int mark_packed_unreachable_referents(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, + void *data UNUSED) { mark_unreachable_referents(oid); return 0; @@ -436,9 +440,6 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) out: if (obj->type == OBJ_TREE) free_tree_buffer((struct tree *)obj); - if (obj->type == OBJ_COMMIT) - free_commit_buffer(the_repository->parsed_objects, - (struct commit *)obj); return err; } @@ -487,8 +488,9 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, } static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) + const char *email UNUSED, + timestamp_t timestamp, int tz UNUSED, + const char *message UNUSED, void *cb_data) { const char *refname = cb_data; @@ -501,8 +503,9 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid return 0; } -static int fsck_handle_reflog(const char *logname, const struct object_id *oid, - int flag, void *cb_data) +static int fsck_handle_reflog(const char *logname, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cb_data) { struct strbuf refname = STRBUF_INIT; @@ -513,7 +516,7 @@ static int fsck_handle_reflog(const char *logname, const struct object_id *oid, } static int fsck_handle_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object *obj; @@ -659,14 +662,15 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data) return 0; /* keep checking other objects, even if we saw an error */ } -static int fsck_cruft(const char *basename, const char *path, void *data) +static int fsck_cruft(const char *basename, const char *path, + void *data UNUSED) { if (!starts_with(basename, "tmp_obj_")) fprintf_ln(stderr, _("bad sha1 file: %s"), path); return 0; } -static int fsck_subdir(unsigned int nr, const char *path, void *data) +static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) { struct for_each_loose_cb *cb_data = data; struct progress *progress = cb_data->progress; @@ -730,19 +734,19 @@ static int fsck_head_link(const char *head_ref_name, return 0; } -static int fsck_cache_tree(struct cache_tree *it) +static int fsck_cache_tree(struct cache_tree *it, const char *index_path) { int i; int err = 0; if (verbose) - fprintf_ln(stderr, _("Checking cache tree")); + fprintf_ln(stderr, _("Checking cache tree of %s"), index_path); if (0 <= it->entry_count) { struct object *obj = parse_object(the_repository, &it->oid); if (!obj) { - error(_("%s: invalid sha1 pointer in cache-tree"), - oid_to_hex(&it->oid)); + error(_("%s: invalid sha1 pointer in cache-tree of %s"), + oid_to_hex(&it->oid), index_path); errors_found |= ERROR_REFS; return 1; } @@ -753,10 +757,81 @@ static int fsck_cache_tree(struct cache_tree *it) err |= objerror(obj, _("non-tree in cache-tree")); } for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree); + err |= fsck_cache_tree(it->down[i]->cache_tree, index_path); return err; } +static int fsck_resolve_undo(struct index_state *istate, + const char *index_path) +{ + struct string_list_item *item; + struct string_list *resolve_undo = istate->resolve_undo; + + if (!resolve_undo) + return 0; + + for_each_string_list_item(item, resolve_undo) { + const char *path = item->string; + struct resolve_undo_info *ru = item->util; + int i; + + if (!ru) + continue; + for (i = 0; i < 3; i++) { + struct object *obj; + + if (!ru->mode[i] || !S_ISREG(ru->mode[i])) + continue; + + obj = parse_object(the_repository, &ru->oid[i]); + if (!obj) { + error(_("%s: invalid sha1 pointer in resolve-undo of %s"), + oid_to_hex(&ru->oid[i]), + index_path); + errors_found |= ERROR_REFS; + continue; + } + obj->flags |= USED; + fsck_put_object_name(&fsck_walk_options, &ru->oid[i], + ":(%d):%s", i, path); + mark_object_reachable(obj); + } + } + return 0; +} + +static void fsck_index(struct index_state *istate, const char *index_path, + int is_main_index) +{ + unsigned int i; + + /* TODO: audit for interaction with sparse-index. */ + ensure_full_index(istate); + for (i = 0; i < istate->cache_nr; i++) { + unsigned int mode; + struct blob *blob; + struct object *obj; + + mode = istate->cache[i]->ce_mode; + if (S_ISGITLINK(mode)) + continue; + blob = lookup_blob(the_repository, + &istate->cache[i]->oid); + if (!blob) + continue; + obj = &blob->object; + obj->flags |= USED; + fsck_put_object_name(&fsck_walk_options, &obj->oid, + "%s:%s", + is_main_index ? "" : index_path, + istate->cache[i]->name); + mark_object_reachable(obj); + } + if (istate->cache_tree) + fsck_cache_tree(istate->cache_tree, index_path); + fsck_resolve_undo(istate, index_path); +} + static void mark_object_for_connectivity(const struct object_id *oid) { struct object *obj = lookup_unknown_object(the_repository, oid); @@ -764,24 +839,27 @@ static void mark_object_for_connectivity(const struct object_id *oid) } static int mark_loose_for_connectivity(const struct object_id *oid, - const char *path, - void *data) + const char *path UNUSED, + void *data UNUSED) { mark_object_for_connectivity(oid); return 0; } static int mark_packed_for_connectivity(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, + void *data UNUSED) { mark_object_for_connectivity(oid); return 0; } static char const * const fsck_usage[] = { - N_("git fsck [<options>] [<object>...]"), + N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" + " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" + " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" + " [--[no-]name-objects] [<object>...]"), NULL }; @@ -813,6 +891,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) errors_found = 0; read_replace_refs = 0; + save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); @@ -913,31 +992,30 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (keep_cache_objects) { + struct worktree **worktrees, **p; + verify_index_checksum = 1; verify_ce_order = 1; - read_cache(); - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) { - unsigned int mode; - struct blob *blob; - struct object *obj; - mode = active_cache[i]->ce_mode; - if (S_ISGITLINK(mode)) - continue; - blob = lookup_blob(the_repository, - &active_cache[i]->oid); - if (!blob) - continue; - obj = &blob->object; - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &obj->oid, - ":%s", active_cache[i]->name); - mark_object_reachable(obj); + worktrees = get_worktrees(); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + struct index_state istate = + INDEX_STATE_INIT(the_repository); + char *path; + + /* + * Make a copy since the buffer is reusable + * and may get overwritten by other calls + * while we're examining the index. + */ + path = xstrdup(worktree_git_path(wt, "index")); + read_index_from(&istate, path, get_worktree_git_dir(wt)); + fsck_index(&istate, path, wt->is_current); + discard_index(&istate); + free(path); } - if (active_cache_tree) - fsck_cache_tree(active_cache_tree); + free_worktrees(worktrees); } check_connectivity(); diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 2c109cf8b3..cae804a190 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -1,8 +1,10 @@ #include "builtin.h" +#include "alloc.h" #include "config.h" #include "parse-options.h" #include "fsmonitor.h" #include "fsmonitor-ipc.h" +#include "fsmonitor-path-utils.h" #include "compat/fsmonitor/fsm-health.h" #include "compat/fsmonitor/fsm-listen.h" #include "fsmonitor--daemon.h" @@ -13,8 +15,8 @@ static const char * const builtin_fsmonitor__daemon_usage[] = { N_("git fsmonitor--daemon start [<options>]"), N_("git fsmonitor--daemon run [<options>]"), - N_("git fsmonitor--daemon stop"), - N_("git fsmonitor--daemon status"), + "git fsmonitor--daemon stop", + "git fsmonitor--daemon status", NULL }; @@ -709,6 +711,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state, "fsmonitor: unsupported V1 protocol '%s'"), command); do_trivial = 1; + do_cookie = 1; } else { /* We have "builtin:*" */ @@ -718,6 +721,7 @@ static int do_handle_client(struct fsmonitor_daemon_state *state, "fsmonitor: invalid V2 protocol token '%s'", command); do_trivial = 1; + do_cookie = 1; } else { /* @@ -1208,7 +1212,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state) * events. */ if (pthread_create(&state->listener_thread, NULL, - fsm_listen__thread_proc, state) < 0) { + fsm_listen__thread_proc, state)) { ipc_server_stop_async(state->ipc_server_data); err = error(_("could not start fsmonitor listener thread")); goto cleanup; @@ -1219,7 +1223,7 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state) * Start the health thread to watch over our process. */ if (pthread_create(&state->health_thread, NULL, - fsm_health__thread_proc, state) < 0) { + fsm_health__thread_proc, state)) { ipc_server_stop_async(state->ipc_server_data); err = error(_("could not start fsmonitor health thread")); goto cleanup; @@ -1282,6 +1286,11 @@ static int fsmonitor_run_daemon(void) strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree())); state.nr_paths_watching = 1; + strbuf_init(&state.alias.alias, 0); + strbuf_init(&state.alias.points_to, 0); + if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias))) + goto done; + /* * We create and delete cookie files somewhere inside the .git * directory to help us keep sync with the file system. If @@ -1343,7 +1352,8 @@ static int fsmonitor_run_daemon(void) * directory.) */ strbuf_init(&state.path_ipc, 0); - strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path())); + strbuf_addstr(&state.path_ipc, + absolute_path(fsmonitor_ipc__get_path(the_repository))); /* * Confirm that we can create platform-specific resources for the @@ -1390,6 +1400,8 @@ done: strbuf_release(&state.path_gitdir_watch); strbuf_release(&state.path_cookie_prefix); strbuf_release(&state.path_ipc); + strbuf_release(&state.alias.alias); + strbuf_release(&state.alias.points_to); return err; } diff --git a/builtin/gc.c b/builtin/gc.c index 4ea70089c9..c58fe8c936 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -11,6 +11,7 @@ */ #include "builtin.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "tempfile.h" @@ -42,7 +43,7 @@ static const char * const builtin_gc_usage[] = { static int pack_refs = 1; static int prune_reflogs = 1; -static int cruft_packs = 0; +static int cruft_packs = -1; static int aggressive_depth = 50; static int aggressive_window = 250; static int gc_auto_threshold = 6700; @@ -167,10 +168,11 @@ static void gc_config(void) struct maintenance_run_opts; static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts) { - struct strvec pack_refs_cmd = STRVEC_INIT; - strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); + struct child_process cmd = CHILD_PROCESS_INIT; - return run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD); + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL); + return run_command(&cmd); } static int too_many_loose_objects(void) @@ -323,7 +325,7 @@ static uint64_t estimate_repack_memory(struct packed_git *pack) return os_cache + heap; } -static int keep_one_pack(struct string_list_item *item, void *data) +static int keep_one_pack(struct string_list_item *item, void *data UNUSED) { strvec_pushf(&repack, "--keep-pack=%s", basename(item->string)); return 0; @@ -536,8 +538,14 @@ static void gc_before_repack(void) if (pack_refs && maintenance_task_pack_refs(NULL)) die(FAILED_RUN, "pack-refs"); - if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD)) - die(FAILED_RUN, reflog.v[0]); + if (prune_reflogs) { + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + strvec_pushv(&cmd.args, reflog.v); + if (run_command(&cmd)) + die(FAILED_RUN, reflog.v[0]); + } } int cmd_gc(int argc, const char **argv, const char *prefix) @@ -551,6 +559,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) int daemonized = 0; int keep_largest_pack = -1; timestamp_t dummy; + struct child_process rerere_cmd = CHILD_PROCESS_INIT; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), @@ -581,7 +590,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) /* default expiry time, overwritten in gc_config */ gc_config(); if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) - die(_("failed to parse gc.logexpiry value %s"), gc_log_expire); + die(_("failed to parse gc.logExpiry value %s"), gc_log_expire); if (pack_refs < 0) pack_refs = !is_bare_repository(); @@ -594,6 +603,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (prune_expire && parse_expiry_date(prune_expire, &dummy)) die(_("failed to parse prune expiry value %s"), prune_expire); + prepare_repo_settings(the_repository); + if (cruft_packs < 0) + cruft_packs = the_repository->settings.gc_cruft_packs; + if (aggressive) { strvec_push(&repack, "-f"); if (aggressive_depth > 0) @@ -672,11 +685,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix) gc_before_repack(); if (!repository_format_precious_objects) { - if (run_command_v_opt(repack.v, - RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE)) + struct child_process repack_cmd = CHILD_PROCESS_INIT; + + repack_cmd.git_cmd = 1; + repack_cmd.close_object_store = 1; + strvec_pushv(&repack_cmd.args, repack.v); + if (run_command(&repack_cmd)) die(FAILED_RUN, repack.v[0]); if (prune_expire) { + struct child_process prune_cmd = CHILD_PROCESS_INIT; + /* run `git prune` even if using cruft packs */ strvec_push(&prune, prune_expire); if (quiet) @@ -684,18 +703,26 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (has_promisor_remote()) strvec_push(&prune, "--exclude-promisor-objects"); - if (run_command_v_opt(prune.v, RUN_GIT_CMD)) + prune_cmd.git_cmd = 1; + strvec_pushv(&prune_cmd.args, prune.v); + if (run_command(&prune_cmd)) die(FAILED_RUN, prune.v[0]); } } if (prune_worktrees_expire) { + struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT; + strvec_push(&prune_worktrees, prune_worktrees_expire); - if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD)) + prune_worktrees_cmd.git_cmd = 1; + strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v); + if (run_command(&prune_worktrees_cmd)) die(FAILED_RUN, prune_worktrees.v[0]); } - if (run_command_v_opt(rerere.v, RUN_GIT_CMD)) + rerere_cmd.git_cmd = 1; + strvec_pushv(&rerere_cmd.args, rerere.v); + if (run_command(&rerere_cmd)) die(FAILED_RUN, rerere.v[0]); report_garbage = report_pack_garbage; @@ -705,7 +732,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) clean_pack_garbage(); } - prepare_repo_settings(the_repository); if (the_repository->settings.gc_write_commit_graph == 1) write_commit_graph_reachable(the_repository->objects->odb, !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, @@ -776,8 +802,9 @@ struct cg_auto_data { int limit; }; -static int dfs_on_ref(const char *refname, - const struct object_id *oid, int flags, +static int dfs_on_ref(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct cg_auto_data *data = (struct cg_auto_data *)cb_data; @@ -904,12 +931,6 @@ static int fetch_remote(struct remote *remote, void *cbdata) static int maintenance_task_prefetch(struct maintenance_run_opts *opts) { - git_config_set_multivar_gently("log.excludedecoration", - "refs/prefetch/", - "refs/prefetch/", - CONFIG_FLAGS_FIXED_VALUE | - CONFIG_FLAGS_MULTI_REPLACE); - if (for_each_remote(fetch_remote, opts)) { error(_("failed to prefetch remotes")); return 1; @@ -956,9 +977,9 @@ struct write_loose_object_data { static int loose_object_auto_limit = 100; -static int loose_object_count(const struct object_id *oid, - const char *path, - void *data) +static int loose_object_count(const struct object_id *oid UNUSED, + const char *path UNUSED, + void *data) { int *count = (int*)data; if (++(*count) >= loose_object_auto_limit) @@ -983,15 +1004,15 @@ static int loose_object_auto_condition(void) NULL, NULL, &count); } -static int bail_on_loose(const struct object_id *oid, - const char *path, - void *data) +static int bail_on_loose(const struct object_id *oid UNUSED, + const char *path UNUSED, + void *data UNUSED) { return 1; } static int write_loose_object_to_stdin(const struct object_id *oid, - const char *path, + const char *path UNUSED, void *data) { struct write_loose_object_data *d = (struct write_loose_object_data *)data; @@ -1459,13 +1480,30 @@ static char *get_maintpath(void) return strbuf_detach(&sb, NULL); } -static int maintenance_register(void) +static char const * const builtin_maintenance_register_usage[] = { + "git maintenance register [--config-file <path>]", + NULL +}; + +static int maintenance_register(int argc, const char **argv, const char *prefix) { - int rc; + char *config_file = NULL; + struct option options[] = { + OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")), + OPT_END(), + }; + int found = 0; + const char *key = "maintenance.repo"; char *config_value; - struct child_process config_set = CHILD_PROCESS_INIT; - struct child_process config_get = CHILD_PROCESS_INIT; char *maintpath = get_maintpath(); + struct string_list_item *item; + const struct string_list *list; + + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_register_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_register_usage, + options); /* Disable foreground maintenance */ git_config_set("maintenance.auto", "false"); @@ -1476,46 +1514,112 @@ static int maintenance_register(void) else git_config_set("maintenance.strategy", "incremental"); - config_get.git_cmd = 1; - strvec_pushl(&config_get.args, "config", "--global", "--get", - "--fixed-value", "maintenance.repo", maintpath, NULL); - config_get.out = -1; - - if (start_command(&config_get)) { - rc = error(_("failed to run 'git config'")); - goto done; - } - - /* We already have this value in our config! */ - if (!finish_command(&config_get)) { - rc = 0; - goto done; + list = git_config_get_value_multi(key); + if (list) { + for_each_string_list_item(item, list) { + if (!strcmp(maintpath, item->string)) { + found = 1; + break; + } + } } - config_set.git_cmd = 1; - strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo", - maintpath, NULL); + if (!found) { + int rc; + char *user_config = NULL, *xdg_config = NULL; - rc = run_command(&config_set); + if (!config_file) { + git_global_config(&user_config, &xdg_config); + config_file = user_config; + if (!user_config) + die(_("$HOME not set")); + } + rc = git_config_set_multivar_in_file_gently( + config_file, "maintenance.repo", maintpath, + CONFIG_REGEX_NONE, 0); + free(user_config); + free(xdg_config); + + if (rc) + die(_("unable to add '%s' value of '%s'"), + key, maintpath); + } -done: free(maintpath); - return rc; + return 0; } -static int maintenance_unregister(void) +static char const * const builtin_maintenance_unregister_usage[] = { + "git maintenance unregister [--config-file <path>] [--force]", + NULL +}; + +static int maintenance_unregister(int argc, const char **argv, const char *prefix) { - int rc; - struct child_process config_unset = CHILD_PROCESS_INIT; + int force = 0; + char *config_file = NULL; + struct option options[] = { + OPT_STRING(0, "config-file", &config_file, N_("file"), N_("use given config file")), + OPT__FORCE(&force, + N_("return success even if repository was not registered"), + PARSE_OPT_NOCOMPLETE), + OPT_END(), + }; + const char *key = "maintenance.repo"; char *maintpath = get_maintpath(); + int found = 0; + struct string_list_item *item; + const struct string_list *list; + struct config_set cs = { { 0 } }; + + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_unregister_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_unregister_usage, + options); + + if (config_file) { + git_configset_init(&cs); + git_configset_add_file(&cs, config_file); + list = git_configset_get_value_multi(&cs, key); + } else { + list = git_config_get_value_multi(key); + } + if (list) { + for_each_string_list_item(item, list) { + if (!strcmp(maintpath, item->string)) { + found = 1; + break; + } + } + } - config_unset.git_cmd = 1; - strvec_pushl(&config_unset.args, "config", "--global", "--unset", - "--fixed-value", "maintenance.repo", maintpath, NULL); + if (found) { + int rc; + char *user_config = NULL, *xdg_config = NULL; + if (!config_file) { + git_global_config(&user_config, &xdg_config); + config_file = user_config; + if (!user_config) + die(_("$HOME not set")); + } + rc = git_config_set_multivar_in_file_gently( + config_file, key, NULL, maintpath, + CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE); + free(user_config); + free(xdg_config); + + if (rc && + (!force || rc == CONFIG_NOTHING_SET)) + die(_("unable to unset '%s' value of '%s'"), + key, maintpath); + } else if (!force) { + die(_("repository '%s' is not registered"), maintpath); + } - rc = run_command(&config_unset); + git_configset_clear(&cs); free(maintpath); - return rc; + return 0; } static const char *get_frequency(enum schedule_priority schedule) @@ -1852,20 +1956,16 @@ static char *schtasks_task_name(const char *frequency) static int schtasks_remove_task(enum schedule_priority schedule) { const char *cmd = "schtasks"; - int result; - struct strvec args = STRVEC_INIT; + struct child_process child = CHILD_PROCESS_INIT; const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); get_schedule_cmd(&cmd, NULL); - strvec_split(&args, cmd); - strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL); - - result = run_command_v_opt(args.v, 0); - - strvec_clear(&args); + strvec_split(&child.args, cmd); + strvec_pushl(&child.args, "/delete", "/tn", name, "/f", NULL); free(name); - return result; + + return run_command(&child); } static int schtasks_remove_tasks(void) @@ -2059,6 +2159,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) struct child_process crontab_edit = CHILD_PROCESS_INIT; FILE *cron_list, *cron_in; struct strbuf line = STRBUF_INIT; + struct tempfile *tmpedit = NULL; get_schedule_cmd(&cmd, NULL); strvec_split(&crontab_list.args, cmd); @@ -2073,6 +2174,17 @@ static int crontab_update_schedule(int run_maintenance, int fd) /* Ignore exit code, as an empty crontab will return error. */ finish_command(&crontab_list); + tmpedit = mks_tempfile_t(".git_cron_edit_tmpXXXXXX"); + if (!tmpedit) { + result = error(_("failed to create crontab temporary file")); + goto out; + } + cron_in = fdopen_tempfile(tmpedit, "w"); + if (!cron_in) { + result = error(_("failed to open temporary file")); + goto out; + } + /* * Read from the .lock file, filtering out the old * schedule while appending the new schedule. @@ -2080,19 +2192,6 @@ static int crontab_update_schedule(int run_maintenance, int fd) cron_list = fdopen(fd, "r"); rewind(cron_list); - strvec_split(&crontab_edit.args, cmd); - crontab_edit.in = -1; - crontab_edit.git_cmd = 0; - - if (start_command(&crontab_edit)) - return error(_("failed to run 'crontab'; your system might not support 'cron'")); - - cron_in = fdopen(crontab_edit.in, "w"); - if (!cron_in) { - result = error(_("failed to open stdin of 'crontab'")); - goto done_editing; - } - while (!strbuf_getline_lf(&line, cron_list)) { if (!in_old_region && !strcmp(line.buf, BEGIN_LINE)) in_old_region = 1; @@ -2126,14 +2225,22 @@ static int crontab_update_schedule(int run_maintenance, int fd) } fflush(cron_in); - fclose(cron_in); - close(crontab_edit.in); -done_editing: + strvec_split(&crontab_edit.args, cmd); + strvec_push(&crontab_edit.args, get_tempfile_path(tmpedit)); + crontab_edit.git_cmd = 0; + + if (start_command(&crontab_edit)) { + result = error(_("failed to run 'crontab'; your system might not support 'cron'")); + goto out; + } + if (finish_command(&crontab_edit)) result = error(_("'crontab' died")); else fclose(cron_list); +out: + delete_tempfile(&tmpedit); return result; } @@ -2490,6 +2597,7 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) PARSE_OPT_NONEG, maintenance_opt_scheduler), OPT_END() }; + const char *register_args[] = { "register", NULL }; argc = parse_options(argc, argv, prefix, options, builtin_maintenance_start_usage, 0); @@ -2499,34 +2607,46 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) opts.scheduler = resolve_scheduler(opts.scheduler); validate_scheduler(opts.scheduler); - if (maintenance_register()) + if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL)) warning(_("failed to add repo to global config")); return update_background_schedule(&opts, 1); } -static int maintenance_stop(void) +static const char *const builtin_maintenance_stop_usage[] = { + "git maintenance stop", + NULL +}; + +static int maintenance_stop(int argc, const char **argv, const char *prefix) { + struct option options[] = { + OPT_END() + }; + argc = parse_options(argc, argv, prefix, options, + builtin_maintenance_stop_usage, 0); + if (argc) + usage_with_options(builtin_maintenance_stop_usage, options); return update_background_schedule(NULL, 0); } -static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]"); +static const char * const builtin_maintenance_usage[] = { + N_("git maintenance <subcommand> [<options>]"), + NULL, +}; int cmd_maintenance(int argc, const char **argv, const char *prefix) { - if (argc < 2 || - (argc == 2 && !strcmp(argv[1], "-h"))) - usage(builtin_maintenance_usage); - - if (!strcmp(argv[1], "run")) - return maintenance_run(argc - 1, argv + 1, prefix); - if (!strcmp(argv[1], "start")) - return maintenance_start(argc - 1, argv + 1, prefix); - if (!strcmp(argv[1], "stop")) - return maintenance_stop(); - if (!strcmp(argv[1], "register")) - return maintenance_register(); - if (!strcmp(argv[1], "unregister")) - return maintenance_unregister(); - - die(_("invalid subcommand: %s"), argv[1]); + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_maintenance_options[] = { + OPT_SUBCOMMAND("run", &fn, maintenance_run), + OPT_SUBCOMMAND("start", &fn, maintenance_start), + OPT_SUBCOMMAND("stop", &fn, maintenance_stop), + OPT_SUBCOMMAND("register", &fn, maintenance_register), + OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister), + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, builtin_maintenance_options, + builtin_maintenance_usage, 0); + return fn(argc, argv, prefix); } diff --git a/builtin/grep.c b/builtin/grep.c index bcb07ea7f7..c590fcb19d 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -3,8 +3,9 @@ * * Copyright (c) 2006 Junio C Hamano */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "alloc.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "blob.h" @@ -458,6 +459,33 @@ static int grep_submodule(struct grep_opt *opt, * subrepo's odbs to the in-memory alternates list. */ obj_read_lock(); + + /* + * NEEDSWORK: when reading a submodule, the sparsity settings in the + * superproject are incorrectly forgotten or misused. For example: + * + * 1. "command_requires_full_index" + * When this setting is turned on for `grep`, only the superproject + * knows it. All the submodules are read with their own configs + * and get prepare_repo_settings()'d. Therefore, these submodules + * "forget" the sparse-index feature switch. As a result, the index + * of these submodules are expanded unexpectedly. + * + * 2. "core_apply_sparse_checkout" + * When running `grep` in the superproject, this setting is + * populated using the superproject's configs. However, once + * initialized, this config is globally accessible and is read by + * prepare_repo_settings() for the submodules. For instance, if a + * submodule is using a sparse-checkout, however, the superproject + * is not, the result is that the config from the superproject will + * dictate the behavior for the submodule, making it "forget" its + * sparse-checkout state. + * + * 3. "core_sparse_checkout_cone" + * ditto. + * + * Note that this list is not exhaustive. + */ repo_read_gitmodules(subrepo, 0); /* @@ -520,8 +548,6 @@ static int grep_cache(struct grep_opt *opt, if (repo_read_index(repo) < 0) die(_("index file corrupt")); - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(repo->index); for (nr = 0; nr < repo->index->cache_nr; nr++) { const struct cache_entry *ce = repo->index->cache[nr]; @@ -530,8 +556,20 @@ static int grep_cache(struct grep_opt *opt, strbuf_setlen(&name, name_base_len); strbuf_addstr(&name, ce->name); + if (S_ISSPARSEDIR(ce->ce_mode)) { + enum object_type type; + struct tree_desc tree; + void *data; + unsigned long size; + + data = read_object_file(&ce->oid, &type, &size); + init_tree_desc(&tree, data, size); - if (S_ISREG(ce->ce_mode) && + hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0); + strbuf_setlen(&name, name_base_len); + strbuf_addstr(&name, ce->name); + free(data); + } else if (S_ISREG(ce->ce_mode) && match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL, S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) { @@ -961,6 +999,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)"), PARSE_OPT_NOCOMPLETE), + OPT_INTEGER('m', "max-count", &opt.max_count, + N_("maximum number of results per file")), OPT_END() }; grep_prefix = prefix; @@ -982,6 +1022,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION); + if (the_repository->gitdir) { + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + } + if (use_index && !startup_info->have_repository) { int fallback = 0; git_config_get_bool("grep.fallbacktonoindex", &fallback); @@ -1101,6 +1146,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (recurse_submodules && untracked) die(_("--untracked not supported with --recurse-submodules")); + /* + * Optimize out the case where the amount of matches is limited to zero. + * We do this to keep results consistent with GNU grep(1). + */ + if (opt.max_count == 0) + return 1; + if (show_in_pager) { if (num_threads > 1) warning(_("invalid option combination, ignoring --threads")); diff --git a/builtin/hash-object.c b/builtin/hash-object.c index fbae878c2b..1848768b97 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -6,6 +6,7 @@ */ #include "builtin.h" #include "config.h" +#include "hex.h" #include "object-store.h" #include "blob.h" #include "quote.h" @@ -27,6 +28,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig else ret = write_object_file_literally(buf.buf, buf.len, type, oid, flags); + close(fd); strbuf_release(&buf); return ret; } @@ -80,8 +82,9 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags, int cmd_hash_object(int argc, const char **argv, const char *prefix) { static const char * const hash_object_usage[] = { - N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."), - "git hash-object --stdin-paths", + N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters]\n" + " [--stdin [--literally]] [--] <file>..."), + N_("git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]"), NULL }; const char *type = blob_type; diff --git a/builtin/help.c b/builtin/help.c index 222f994f86..53f2812dfb 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -43,6 +43,8 @@ static enum help_action { HELP_ACTION_ALL = 1, HELP_ACTION_GUIDES, HELP_ACTION_CONFIG, + HELP_ACTION_USER_INTERFACES, + HELP_ACTION_DEVELOPER_INTERFACES, HELP_ACTION_CONFIG_FOR_COMPLETION, HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, } cmd_mode; @@ -69,6 +71,12 @@ static struct option builtin_help_options[] = { OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"), HELP_ACTION_GUIDES), + OPT_CMDMODE(0, "user-interfaces", &cmd_mode, + N_("print list of user-facing repository, command and file interfaces"), + HELP_ACTION_USER_INTERFACES), + OPT_CMDMODE(0, "developer-interfaces", &cmd_mode, + N_("print list of file formats, protocols and other developer interfaces"), + HELP_ACTION_DEVELOPER_INTERFACES), OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"), HELP_ACTION_CONFIG), OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "", @@ -80,10 +88,12 @@ static struct option builtin_help_options[] = { }; static const char * const builtin_help_usage[] = { - "git help [-a|--all] [--[no-]verbose]] [--[no-]external-commands] [--[no-]aliases]", - N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>]"), + "git help [-a|--all] [--[no-]verbose] [--[no-]external-commands] [--[no-]aliases]", + N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>|<doc>]"), "git help [-g|--guides]", "git help [-c|--config]", + "git help [--user-interfaces]", + "git help [--developer-interfaces]", NULL }; @@ -430,6 +440,8 @@ static const char *cmd_to_page(const char *git_cmd) return git_cmd; else if (is_git_command(git_cmd)) return xstrfmt("git-%s", git_cmd); + else if (!strcmp("scalar", git_cmd)) + return xstrdup(git_cmd); else return xstrfmt("git%s", git_cmd); } @@ -654,6 +666,14 @@ int cmd_help(int argc, const char **argv, const char *prefix) opt_mode_usage(argc, "--config-for-completion", help_format); list_config_help(SHOW_CONFIG_VARS); return 0; + case HELP_ACTION_USER_INTERFACES: + opt_mode_usage(argc, "--user-interfaces", help_format); + list_user_interfaces_help(); + return 0; + case HELP_ACTION_DEVELOPER_INTERFACES: + opt_mode_usage(argc, "--developer-interfaces", help_format); + list_developer_interfaces_help(); + return 0; case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION: opt_mode_usage(argc, "--config-sections-for-completion", help_format); diff --git a/builtin/hook.c b/builtin/hook.c index 54e5c6ec93..f95b7965c5 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -7,7 +7,7 @@ #include "strvec.h" #define BUILTIN_HOOK_RUN_USAGE \ - N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]") + N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]") static const char * const builtin_hook_usage[] = { BUILTIN_HOOK_RUN_USAGE, @@ -28,6 +28,8 @@ static int run(int argc, const char **argv, const char *prefix) struct option run_options[] = { OPT_BOOL(0, "ignore-missing", &ignore_missing, N_("silently ignore missing requested <hook-name>")), + OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"), + N_("file to read into hooks' stdin")), OPT_END(), }; int ret; @@ -67,18 +69,14 @@ usage: int cmd_hook(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option builtin_hook_options[] = { + OPT_SUBCOMMAND("run", &fn, run), OPT_END(), }; argc = parse_options(argc, argv, NULL, builtin_hook_options, - builtin_hook_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc) - goto usage; + builtin_hook_usage, 0); - if (!strcmp(argv[0], "run")) - return run(argc, argv, prefix); - -usage: - usage_with_options(builtin_hook_usage, builtin_hook_options); + return fn(argc, argv, prefix); } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 3e385b4800..b451755f40 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1,6 +1,8 @@ #include "builtin.h" +#include "alloc.h" #include "config.h" #include "delta.h" +#include "hex.h" #include "pack.h" #include "csum-file.h" #include "blob.h" @@ -14,6 +16,7 @@ #include "thread-utils.h" #include "packfile.h" #include "object-store.h" +#include "replace-object.h" #include "promisor-remote.h" static const char index_pack_usage[] = @@ -1575,7 +1578,7 @@ static int git_index_pack_config(const char *k, const char *v, void *cb) if (!strcmp(k, "pack.indexversion")) { opts->version = git_config_int(k, v); if (opts->version > 2) - die(_("bad pack.indexversion=%"PRIu32), opts->version); + die(_("bad pack.indexVersion=%"PRIu32), opts->version); return 0; } if (!strcmp(k, "pack.threads")) { diff --git a/builtin/init-db.c b/builtin/init-db.c index 546f9c595e..dcaaf102ea 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -515,7 +515,10 @@ static int shared_callback(const struct option *opt, const char *arg, int unset) } static const char *const init_db_usage[] = { - N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"), + N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" + " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" + " [-b <branch-name> | --initial-branch=<branch-name>]\n" + " [--shared[=<permissions>]] [<directory>]"), NULL }; diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 84748eafc0..e58627c72a 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -13,7 +13,9 @@ #include "config.h" static const char * const git_interpret_trailers_usage[] = { - N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"), + N_("git interpret-trailers [--in-place] [--trim-empty]\n" + " [(--trailer <token>[(=|:)<value>])...]\n" + " [--parse] [<file>...]"), NULL }; diff --git a/builtin/log.c b/builtin/log.c index 88a5e98875..4693385e8e 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -4,9 +4,10 @@ * (C) Copyright 2006 Linus Torvalds * 2006 Junio Hamano */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" +#include "git-compat-util.h" +#include "alloc.h" #include "config.h" +#include "hex.h" #include "refs.h" #include "object-store.h" #include "color.h" @@ -52,9 +53,12 @@ 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[] = { N_("git log [<options>] [<revision-range>] [[--] <path>...]"), @@ -101,6 +105,20 @@ static int parse_decoration_style(const char *value) return -1; } +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; +static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; + +static int clear_decorations_callback(const struct option *opt, + const char *arg, int unset) +{ + string_list_clear(&decorate_refs_include, 0); + string_list_clear(&decorate_refs_exclude, 0); + use_default_decoration_filter = 0; + return 0; +} + static int decorate_callback(const struct option *opt, const char *arg, int unset) { if (unset) @@ -162,18 +180,61 @@ static void cmd_log_init_defaults(struct rev_info *rev) parse_date_format(default_date_mode, &rev->date_mode); } +static void set_default_decoration_filter(struct decoration_filter *decoration_filter) +{ + int i; + char *value = NULL; + struct string_list *include = decoration_filter->include_ref_pattern; + const struct string_list *config_exclude = + git_config_get_value_multi("log.excludeDecoration"); + + if (config_exclude) { + struct string_list_item *item; + for_each_string_list_item(item, config_exclude) + string_list_append(decoration_filter->exclude_ref_config_pattern, + item->string); + } + + /* + * By default, decorate_all is disabled. Enable it if + * log.initialDecorationSet=all. Don't ever disable it by config, + * since the command-line takes precedent. + */ + if (use_default_decoration_filter && + !git_config_get_string("log.initialdecorationset", &value) && + !strcmp("all", value)) + use_default_decoration_filter = 0; + free(value); + + if (!use_default_decoration_filter || + decoration_filter->exclude_ref_pattern->nr || + decoration_filter->include_ref_pattern->nr || + decoration_filter->exclude_ref_config_pattern->nr) + return; + + /* + * No command-line or config options were given, so + * populate with sensible defaults. + */ + for (i = 0; i < ARRAY_SIZE(ref_namespace); i++) { + if (!ref_namespace[i].decoration) + continue; + + string_list_append(include, ref_namespace[i].ref); + } +} + static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, struct rev_info *rev, struct setup_revision_opt *opt) { struct userformat_want w; int quiet = 0, source = 0, mailmap; static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP}; - static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; - static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; - static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; - struct decoration_filter decoration_filter = {&decorate_refs_include, - &decorate_refs_exclude, - &decorate_refs_exclude_config}; + struct decoration_filter decoration_filter = { + .exclude_ref_pattern = &decorate_refs_exclude, + .include_ref_pattern = &decorate_refs_include, + .exclude_ref_config_pattern = &decorate_refs_exclude_config, + }; static struct revision_sources revision_sources; const struct option builtin_log_options[] = { @@ -181,6 +242,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, OPT_BOOL(0, "source", &source, N_("show source")), OPT_BOOL(0, "use-mailmap", &mailmap, N_("use mail map file")), OPT_ALIAS(0, "mailmap", "use-mailmap"), + OPT_CALLBACK_F(0, "clear-decorations", NULL, NULL, + N_("clear all previously-defined decoration filters"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG, + clear_decorations_callback), OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include, N_("pattern"), N_("only decorate refs that match <pattern>")), OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude, @@ -199,7 +264,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, mailmap = use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); if (quiet) @@ -265,16 +330,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, } if (decoration_style || rev->simplify_by_decoration) { - const struct string_list *config_exclude = - repo_config_get_value_multi(the_repository, - "log.excludeDecoration"); - - if (config_exclude) { - struct string_list_item *item; - for_each_string_list_item(item, config_exclude) - string_list_append(&decorate_refs_exclude_config, - item->string); - } + set_default_decoration_filter(&decoration_filter); if (decoration_style) rev->show_decorations = 1; @@ -383,7 +439,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list) setitimer(ITIMER_REAL, &early_output_timer, NULL); } -static void early_output(int signal) +static void early_output(int signal UNUSED) { show_early_output = log_show_early; } @@ -548,8 +604,6 @@ static int git_log_config(const char *var, const char *value, void *cb) return 0; } - if (git_gpg_config(var, value, cb) < 0) - return -1; return git_diff_ui_config(var, value, cb); } @@ -645,9 +699,10 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) return 0; } -static int show_tree_object(const struct object_id *oid, - struct strbuf *base, - const char *pathname, unsigned mode, void *context) +static int show_tree_object(const struct object_id *oid UNUSED, + struct strbuf *base UNUSED, + const char *pathname, unsigned mode, + void *context) { FILE *file = context; fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : ""); @@ -668,10 +723,10 @@ static void show_setup_revisions_tweak(struct rev_info *rev, int cmd_show(int argc, const char **argv, const char *prefix) { struct rev_info rev; - struct object_array_entry *objects; + unsigned int i; struct setup_revision_opt opt; struct pathspec match_all; - int i, count, ret = 0; + int ret = 0; init_log_defaults(); git_config(git_log_config, NULL); @@ -698,12 +753,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) if (!rev.no_walk) return cmd_log_deinit(cmd_log_walk(&rev), &rev); - count = rev.pending.nr; - objects = rev.pending.objects; rev.diffopt.no_free = 1; - for (i = 0; i < count && !ret; i++) { - struct object *o = objects[i].item; - const char *name = objects[i].name; + for (i = 0; i < rev.pending.nr && !ret; i++) { + struct object *o = rev.pending.objects[i].item; + const char *name = rev.pending.objects[i].name; switch (o->type) { case OBJ_BLOB: ret = show_blob_object(&o->oid, &rev, name); @@ -726,7 +779,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) if (!o) ret = error(_("could not read object %s"), oid_to_hex(oid)); - objects[i].item = o; + rev.pending.objects[i].item = o; i--; break; } @@ -743,11 +796,24 @@ int cmd_show(int argc, const char **argv, const char *prefix) rev.shown_one = 1; break; case OBJ_COMMIT: - rev.pending.nr = rev.pending.alloc = 0; - rev.pending.objects = NULL; + { + struct object_array old; + struct object_array blank = OBJECT_ARRAY_INIT; + + memcpy(&old, &rev.pending, sizeof(old)); + memcpy(&rev.pending, &blank, sizeof(rev.pending)); + add_object_array(o, name, &rev.pending); ret = cmd_log_walk_no_free(&rev); + + /* + * No need for + * object_array_clear(&pending). It was + * cleared already in prepare_revision_walk() + */ + memcpy(&rev.pending, &old, sizeof(rev.pending)); break; + } default: ret = error(_("unknown type: %d"), o->type); } @@ -942,6 +1008,8 @@ static int git_format_config(const char *var, const char *value, void *cb) 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); return 0; @@ -995,6 +1063,10 @@ static int git_format_config(const char *var, const char *value, void *cb) from = NULL; return 0; } + if (!strcmp(var, "format.forceinbodyfrom")) { + force_in_body_from = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "format.notes")) { int b = git_parse_maybe_bool(value); if (b < 0) @@ -1009,6 +1081,23 @@ static int git_format_config(const char *var, const char *value, void *cb) cover_from_description_mode = parse_cover_from_description(value); return 0; } + if (!strcmp(var, "format.mboxrd")) { + stdout_mboxrd = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "format.noprefix")) { + format_no_prefix = 1; + return 0; + } + + /* + * ignore some porcelain config which would otherwise be parsed by + * git_diff_ui_config(), via git_log_config(); we can't just avoid + * diff_ui_config completely, because we do care about some ui options + * like color. + */ + if (!strcmp(var, "diff.noprefix")) + return 0; return git_log_config(var, value, cb); } @@ -1265,6 +1354,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, log.in2 = 4; log.file = rev->diffopt.file; log.groups = SHORTLOG_GROUP_AUTHOR; + shortlog_finish_setup(&log); for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); @@ -1694,7 +1784,7 @@ static void prepare_bases(struct base_tree_info *bases, struct object_id *patch_id; if (*commit_base_at(&commit_base, commit)) continue; - if (commit_patch_id(commit, &diffopt, &oid, 0, 1)) + if (commit_patch_id(commit, &diffopt, &oid, 0)) die(_("cannot get patch id")); ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id); patch_id = bases->patch_id + bases->nr_patch_id; @@ -1801,6 +1891,7 @@ 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; int creation_factor = -1; const struct option builtin_format_patch_options[] = { @@ -1886,6 +1977,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("show changes against <refspec> in cover letter or single patch")), OPT_INTEGER(0, "creation-factor", &creation_factor, N_("percentage by which creation is weighted")), + OPT_BOOL(0, "force-in-body-from", &force_in_body_from, + N_("show in-body From: even if identical to the e-mail header")), OPT_END() }; @@ -1914,6 +2007,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; + if (format_no_prefix) + diff_set_noprefix(&rev.diffopt); + if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; @@ -1926,9 +2022,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); + rev.force_in_body_from = force_in_body_from; + /* Make sure "0000-$sub.patch" gives non-negative length for $sub */ if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix)) fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix); @@ -1937,12 +2035,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); if (reroll_count) { - struct strbuf sprefix = STRBUF_INIT; - strbuf_addf(&sprefix, "%s v%s", rev.subject_prefix, reroll_count); rev.reroll_count = reroll_count; - rev.subject_prefix = strbuf_detach(&sprefix, NULL); + rev.subject_prefix = sprefix.buf; } for (i = 0; i < extra_hdr.nr; i++) { @@ -2018,6 +2114,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + rev.always_show_header = 1; rev.zero_commit = zero_commit; rev.patch_name_max = fmt_patch_name_max; @@ -2032,6 +2129,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.diffopt.close_file, "--output", !!output_directory, "--output-directory"); + if (use_stdout && stdout_mboxrd) + rev.commit_format = CMIT_FMT_MBOXRD; + if (use_stdout) { setup_pager(); } else if (!rev.diffopt.close_file) { @@ -2303,6 +2403,7 @@ done: strbuf_release(&rdiff1); strbuf_release(&rdiff2); strbuf_release(&rdiff_title); + strbuf_release(&sprefix); free(to_free); if (rev.ref_message_ids) string_list_clear(rev.ref_message_ids, 0); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c6484fdb45..26e309f533 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -260,7 +260,7 @@ static size_t expand_show_index(struct strbuf *sb, const char *start, end = strchr(start + 1, ')'); if (!end) - die(_("bad ls-files format: element '%s'" + die(_("bad ls-files format: element '%s' " "does not end in ')'"), start); len = end - start + 1; @@ -616,6 +616,7 @@ void overlay_tree_on_index(struct index_state *istate, if (!fn) fn = read_one_entry_quick; err = read_tree(the_repository, tree, &pathspec, fn, istate); + clear_pathspec(&pathspec); if (err) die("unable to read tree entries %s", tree_name); diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index df44e5cc0d..2dfbd8ed9b 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "hex.h" #include "transport.h" #include "ref-filter.h" #include "remote.h" @@ -7,8 +8,8 @@ static const char * const ls_remote_usage[] = { N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" - " [-q | --quiet] [--exit-code] [--get-url]\n" - " [--symref] [<repository> [<refs>...]]"), + " [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n" + " [--symref] [<repository> [<patterns>...]]"), NULL }; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index e279be8bb6..64d8e54318 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -5,6 +5,7 @@ */ #include "cache.h" #include "config.h" +#include "hex.h" #include "object-store.h" #include "blob.h" #include "tree.h" @@ -14,37 +15,11 @@ #include "parse-options.h" #include "pathspec.h" -static int line_termination = '\n'; -#define LS_RECURSIVE 1 -#define LS_TREE_ONLY (1 << 1) -#define LS_SHOW_TREES (1 << 2) -static int abbrev; -static int ls_options; -static struct pathspec pathspec; -static int chomp_prefix; -static const char *ls_tree_prefix; -static const char *format; -struct show_tree_data { - unsigned mode; - enum object_type type; - const struct object_id *oid; - const char *pathname; - struct strbuf *base; -}; - -static const char * const ls_tree_usage[] = { +static const char * const ls_tree_usage[] = { N_("git ls-tree [<options>] <tree-ish> [<path>...]"), NULL }; -static enum ls_tree_cmdmode { - MODE_DEFAULT = 0, - MODE_LONG, - MODE_NAME_ONLY, - MODE_NAME_STATUS, - MODE_OBJECT_ONLY, -} cmdmode; - static void expand_objectsize(struct strbuf *line, const struct object_id *oid, const enum object_type type, unsigned int padded) { @@ -64,10 +39,34 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid, } } +struct ls_tree_options { + unsigned null_termination:1; + int abbrev; + enum ls_tree_path_options { + LS_RECURSIVE = 1 << 0, + LS_TREE_ONLY = 1 << 1, + LS_SHOW_TREES = 1 << 2, + } ls_options; + struct pathspec pathspec; + int chomp_prefix; + const char *ls_tree_prefix; + const char *format; +}; + +struct show_tree_data { + struct ls_tree_options *options; + unsigned mode; + enum object_type type; + const struct object_id *oid; + const char *pathname; + struct strbuf *base; +}; + static size_t expand_show_tree(struct strbuf *sb, const char *start, void *context) { struct show_tree_data *data = context; + struct ls_tree_options *options = data->options; const char *end; const char *p; unsigned int errlen; @@ -92,18 +91,18 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start, } else if (skip_prefix(start, "(objectsize)", &p)) { expand_objectsize(sb, data->oid, data->type, 0); } else if (skip_prefix(start, "(objectname)", &p)) { - strbuf_add_unique_abbrev(sb, data->oid, abbrev); + strbuf_add_unique_abbrev(sb, data->oid, options->abbrev); } else if (skip_prefix(start, "(path)", &p)) { const char *name = data->base->buf; - const char *prefix = chomp_prefix ? ls_tree_prefix : NULL; - struct strbuf quoted = STRBUF_INIT; + const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; struct strbuf sbuf = STRBUF_INIT; + size_t baselen = data->base->len; + strbuf_addstr(data->base, data->pathname); name = relative_path(data->base->buf, prefix, &sbuf); - quote_c_style(name, "ed, NULL, 0); - strbuf_addbuf(sb, "ed); + quote_c_style(name, sb, NULL, 0); + strbuf_setlen(data->base, baselen); strbuf_release(&sbuf); - strbuf_release("ed); } else { errlen = (unsigned long)len; die(_("bad ls-tree format: %%%.*s"), errlen, start); @@ -111,18 +110,19 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start, return len; } -static int show_recursive(const char *base, size_t baselen, const char *pathname) +static int show_recursive(struct ls_tree_options *options, const char *base, + size_t baselen, const char *pathname) { int i; - if (ls_options & LS_RECURSIVE) + if (options->ls_options & LS_RECURSIVE) return 1; - if (!pathspec.nr) + if (!options->pathspec.nr) return 0; - for (i = 0; i < pathspec.nr; i++) { - const char *spec = pathspec.items[i].match; + for (i = 0; i < options->pathspec.nr; i++) { + const char *spec = options->pathspec.items[i].match; size_t len, speclen; if (strncmp(base, spec, baselen)) @@ -144,12 +144,12 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *context) { - size_t baselen; + struct ls_tree_options *options = context; int recurse = 0; struct strbuf sb = STRBUF_INIT; enum object_type type = object_type(mode); - - struct show_tree_data data = { + struct show_tree_data cb_data = { + .options = options, .mode = mode, .type = type, .oid = oid, @@ -157,57 +157,61 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, .base = base, }; - if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname)) + if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname)) recurse = READ_TREE_RECURSIVE; - if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES)) + if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES)) return recurse; - if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY)) + if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY)) return 0; - baselen = base->len; - strbuf_expand(&sb, format, expand_show_tree, &data); - strbuf_addch(&sb, line_termination); + strbuf_expand(&sb, options->format, expand_show_tree, &cb_data); + strbuf_addch(&sb, options->null_termination ? '\0' : '\n'); fwrite(sb.buf, sb.len, 1, stdout); strbuf_release(&sb); - strbuf_setlen(base, baselen); return recurse; } -static int show_tree_common(struct show_tree_data *data, int *recurse, - const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode) +static int show_tree_common(struct ls_tree_options *options, int *recurse, + struct strbuf *base, const char *pathname, + enum object_type type) { - enum object_type type = object_type(mode); int ret = -1; - *recurse = 0; - data->mode = mode; - data->type = type; - data->oid = oid; - data->pathname = pathname; - data->base = base; if (type == OBJ_BLOB) { - if (ls_options & LS_TREE_ONLY) + if (options->ls_options & LS_TREE_ONLY) ret = 0; } else if (type == OBJ_TREE && - show_recursive(base->buf, base->len, pathname)) { + show_recursive(options, base->buf, base->len, pathname)) { *recurse = READ_TREE_RECURSIVE; - if (!(ls_options & LS_SHOW_TREES)) + if (!(options->ls_options & LS_SHOW_TREES)) ret = *recurse; } return ret; } -static void show_tree_common_default_long(struct strbuf *base, +static void show_tree_common_default_long(struct ls_tree_options *options, + struct strbuf *base, const char *pathname, const size_t baselen) { + const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; + strbuf_addstr(base, pathname); - write_name_quoted_relative(base->buf, - chomp_prefix ? ls_tree_prefix : NULL, stdout, - line_termination); + + if (options->null_termination) { + struct strbuf sb = STRBUF_INIT; + const char *name = relative_path(base->buf, prefix, &sb); + + fputs(name, stdout); + fputc('\0', stdout); + + strbuf_release(&sb); + } else { + write_name_quoted_relative(base->buf, prefix, stdout, '\n'); + } + strbuf_setlen(base, baselen); } @@ -215,35 +219,38 @@ static int show_tree_default(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *context) { + struct ls_tree_options *options = context; int early; int recurse; - struct show_tree_data data = { 0 }; + enum object_type type = object_type(mode); - early = show_tree_common(&data, &recurse, oid, base, pathname, mode); + early = show_tree_common(options, &recurse, base, pathname, type); if (early >= 0) return early; - printf("%06o %s %s\t", data.mode, type_name(data.type), - find_unique_abbrev(data.oid, abbrev)); - show_tree_common_default_long(base, pathname, data.base->len); + printf("%06o %s %s\t", mode, type_name(object_type(mode)), + find_unique_abbrev(oid, options->abbrev)); + show_tree_common_default_long(options, base, pathname, base->len); return recurse; } static int show_tree_long(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context) { + struct ls_tree_options *options = context; int early; int recurse; - struct show_tree_data data = { 0 }; char size_text[24]; + enum object_type type = object_type(mode); - early = show_tree_common(&data, &recurse, oid, base, pathname, mode); + early = show_tree_common(options, &recurse, base, pathname, type); if (early >= 0) return early; - if (data.type == OBJ_BLOB) { + if (type == OBJ_BLOB) { unsigned long size; - if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD) + if (oid_object_info(the_repository, oid, &size) == OBJ_BAD) xsnprintf(size_text, sizeof(size_text), "BAD"); else xsnprintf(size_text, sizeof(size_text), @@ -252,47 +259,76 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, xsnprintf(size_text, sizeof(size_text), "-"); } - printf("%06o %s %s %7s\t", data.mode, type_name(data.type), - find_unique_abbrev(data.oid, abbrev), size_text); - show_tree_common_default_long(base, pathname, data.base->len); + printf("%06o %s %s %7s\t", mode, type_name(type), + find_unique_abbrev(oid, options->abbrev), size_text); + show_tree_common_default_long(options, base, pathname, base->len); return recurse; } static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context) { + struct ls_tree_options *options = context; int early; int recurse; const size_t baselen = base->len; - struct show_tree_data data = { 0 }; + enum object_type type = object_type(mode); + const char *prefix; - early = show_tree_common(&data, &recurse, oid, base, pathname, mode); + early = show_tree_common(options, &recurse, base, pathname, type); if (early >= 0) return early; + prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL; strbuf_addstr(base, pathname); - write_name_quoted_relative(base->buf, - chomp_prefix ? ls_tree_prefix : NULL, - stdout, line_termination); + if (options->null_termination) { + struct strbuf sb = STRBUF_INIT; + const char *name = relative_path(base->buf, prefix, &sb); + + fputs(name, stdout); + fputc('\0', stdout); + + strbuf_release(&sb); + } else { + write_name_quoted_relative(base->buf, prefix, stdout, '\n'); + } strbuf_setlen(base, baselen); return recurse; } static int show_tree_object(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, void *context) + const char *pathname, unsigned mode, + void *context) { + struct ls_tree_options *options = context; int early; int recurse; - struct show_tree_data data = { 0 }; + enum object_type type = object_type(mode); + const char *str; - early = show_tree_common(&data, &recurse, oid, base, pathname, mode); + early = show_tree_common(options, &recurse, base, pathname, type); if (early >= 0) return early; - printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination); + str = find_unique_abbrev(oid, options->abbrev); + if (options->null_termination) { + fputs(str, stdout); + fputc('\0', stdout); + } else { + puts(str); + } return recurse; } +enum ls_tree_cmdmode { + MODE_DEFAULT = 0, + MODE_LONG, + MODE_NAME_ONLY, + MODE_NAME_STATUS, + MODE_OBJECT_ONLY, +}; + struct ls_tree_cmdmode_to_fmt { enum ls_tree_cmdmode mode; const char *const fmt; @@ -332,15 +368,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct tree *tree; int i, full_tree = 0; read_tree_fn_t fn = NULL; + enum ls_tree_cmdmode cmdmode = MODE_DEFAULT; + int null_termination = 0; + struct ls_tree_options options = { 0 }; const struct option ls_tree_options[] = { - OPT_BIT('d', NULL, &ls_options, N_("only show trees"), + OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"), LS_TREE_ONLY), - OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"), + OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"), LS_RECURSIVE), - OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"), + OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"), LS_SHOW_TREES), - OPT_SET_INT('z', NULL, &line_termination, - N_("terminate entries with NUL byte"), 0), + OPT_BOOL('z', NULL, &null_termination, + N_("terminate entries with NUL byte")), OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"), MODE_LONG), OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"), @@ -349,29 +388,32 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) MODE_NAME_STATUS), OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"), MODE_OBJECT_ONLY), - OPT_SET_INT(0, "full-name", &chomp_prefix, + OPT_SET_INT(0, "full-name", &options.chomp_prefix, N_("use full path names"), 0), OPT_BOOL(0, "full-tree", &full_tree, N_("list entire tree; not just current directory " "(implies --full-name)")), - OPT_STRING_F(0, "format", &format, N_("format"), + OPT_STRING_F(0, "format", &options.format, N_("format"), N_("format to use for the output"), PARSE_OPT_NONEG), - OPT__ABBREV(&abbrev), + OPT__ABBREV(&options.abbrev), OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; + int ret; git_config(git_default_config, NULL); - ls_tree_prefix = prefix; + options.ls_tree_prefix = prefix; if (prefix) - chomp_prefix = strlen(prefix); + options.chomp_prefix = strlen(prefix); argc = parse_options(argc, argv, prefix, ls_tree_options, ls_tree_usage, 0); + options.null_termination = null_termination; + if (full_tree) { - ls_tree_prefix = prefix = NULL; - chomp_prefix = 0; + options.ls_tree_prefix = prefix = NULL; + options.chomp_prefix = 0; } /* * We wanted to detect conflicts between --name-only and @@ -383,10 +425,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) /* -d -r should imply -t, but -d by itself should not have to. */ if ( (LS_TREE_ONLY|LS_RECURSIVE) == - ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) - ls_options |= LS_SHOW_TREES; + ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options)) + options.ls_options |= LS_SHOW_TREES; - if (format && cmdmode) + if (options.format && cmdmode) usage_msg_opt( _("--format can't be combined with other format-altering options"), ls_tree_usage, ls_tree_options); @@ -401,13 +443,13 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * cannot be lifted until it is converted to use * match_pathspec() or tree_entry_interesting() */ - parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & - ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), + parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC & + ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), PATHSPEC_PREFER_CWD, prefix, argv + 1); - for (i = 0; i < pathspec.nr; i++) - pathspec.items[i].nowildcard_len = pathspec.items[i].len; - pathspec.has_wildcard = 0; + for (i = 0; i < options.pathspec.nr; i++) + options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len; + options.pathspec.has_wildcard = 0; tree = parse_tree_indirect(&oid); if (!tree) die("not a tree object"); @@ -417,11 +459,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) */ while (m2f) { if (!m2f->fmt) { - fn = format ? show_tree_fmt : show_tree_default; - } else if (format && !strcmp(format, m2f->fmt)) { + fn = options.format ? show_tree_fmt : show_tree_default; + } else if (options.format && !strcmp(options.format, m2f->fmt)) { cmdmode = m2f->mode; fn = m2f->fn; - } else if (!format && cmdmode == m2f->mode) { + } else if (!options.format && cmdmode == m2f->mode) { fn = m2f->fn; } else { m2f++; @@ -430,5 +472,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) break; } - return !!read_tree(the_repository, tree, &pathspec, fn, NULL); + ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options); + clear_pathspec(&options.pathspec); + return ret; } diff --git a/builtin/merge-base.c b/builtin/merge-base.c index a11f8c6e4b..be8f3b221c 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -2,6 +2,7 @@ #include "cache.h" #include "config.h" #include "commit.h" +#include "hex.h" #include "refs.h" #include "diff.h" #include "revision.h" @@ -31,8 +32,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) static const char * const merge_base_usage[] = { N_("git merge-base [-a | --all] <commit> <commit>..."), N_("git merge-base [-a | --all] --octopus <commit>..."), - N_("git merge-base --independent <commit>..."), N_("git merge-base --is-ancestor <commit> <commit>"), + N_("git merge-base --independent <commit>..."), N_("git merge-base --fork-point <ref> [<commit>]"), NULL }; diff --git a/builtin/merge-file.c b/builtin/merge-file.c index e695867ee5..c923bbf2ab 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -25,10 +25,10 @@ static int label_cb(const struct option *opt, const char *arg, int unset) int cmd_merge_file(int argc, const char **argv, const char *prefix) { - const char *names[3] = { NULL, NULL, NULL }; - mmfile_t mmfs[3]; - mmbuffer_t result = {NULL, 0}; - xmparam_t xmp = {{0}}; + const char *names[3] = { 0 }; + mmfile_t mmfs[3] = { 0 }; + mmbuffer_t result = { 0 }; + xmparam_t xmp = { 0 }; int ret = 0, i = 0, to_stdout = 0; int quiet = 0; struct option options[] = { @@ -71,21 +71,24 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) for (i = 0; i < 3; i++) { char *fname; - int ret; + mmfile_t *mmf = mmfs + i; if (!names[i]) names[i] = argv[i]; fname = prefix_filename(prefix, argv[i]); - ret = read_mmfile(mmfs + i, fname); + + if (read_mmfile(mmf, fname)) + ret = -1; + else if (mmf->size > MAX_XDIFF_SIZE || + buffer_is_binary(mmf->ptr, mmf->size)) + ret = error("Cannot merge binary files: %s", + argv[i]); + free(fname); if (ret) - return -1; + goto cleanup; - if (mmfs[i].size > MAX_XDIFF_SIZE || - buffer_is_binary(mmfs[i].ptr, mmfs[i].size)) - return error("Cannot merge binary files: %s", - argv[i]); } xmp.ancestor = names[1]; @@ -93,9 +96,6 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) xmp.file2 = names[2]; ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result); - for (i = 0; i < 3; i++) - free(mmfs[i].ptr); - if (ret >= 0) { const char *filename = argv[0]; char *fpath = prefix_filename(prefix, argv[0]); @@ -116,5 +116,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) if (ret > 127) ret = 127; +cleanup: + for (i = 0; i < 3; i++) + free(mmfs[i].ptr); + return ret; } diff --git a/builtin/merge-index.c b/builtin/merge-index.c index c0383fe9df..c875f5d37e 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -1,5 +1,6 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "hex.h" #include "run-command.h" static const char *pgm; @@ -12,12 +13,13 @@ static int merge_entry(int pos, const char *path) const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL }; char hexbuf[4][GIT_MAX_HEXSZ + 1]; char ownbuf[4][60]; + struct child_process cmd = CHILD_PROCESS_INIT; - if (pos >= active_nr) + if (pos >= the_index.cache_nr) die("git merge-index: %s not in the cache", path); found = 0; do { - const struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = the_index.cache[pos]; int stage = ce_stage(ce); if (strcmp(ce->name, path)) @@ -27,11 +29,12 @@ 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 < active_nr); + } while (++pos < the_index.cache_nr); if (!found) die("git merge-index: %s not in the cache", path); - if (run_command_v_opt(arguments, 0)) { + strvec_pushv(&cmd.args, arguments); + if (run_command(&cmd)) { if (one_shot) err++; else { @@ -45,7 +48,7 @@ static int merge_entry(int pos, const char *path) static void merge_one_path(const char *path) { - int pos = cache_name_pos(path, strlen(path)); + int pos = index_name_pos(&the_index, path, strlen(path)); /* * If it already exists in the cache as stage0, it's @@ -60,8 +63,8 @@ static void merge_all(void) int i; /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr; i++) { + const struct cache_entry *ce = the_index.cache[i]; if (!ce_stage(ce)) continue; i += merge_entry(i, ce->name)-1; @@ -80,7 +83,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix) if (argc < 3) usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])"); - read_cache(); + repo_read_index(the_repository); /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&the_index); diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c index 3583cff71c..284eb48609 100644 --- a/builtin/merge-ours.c +++ b/builtin/merge-ours.c @@ -7,7 +7,6 @@ * * Pretend we resolved the heads, but declare our tree trumps everybody else. */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "git-compat-util.h" #include "builtin.h" #include "diff.h" @@ -25,7 +24,7 @@ int cmd_merge_ours(int argc, const char **argv, const char *prefix) * commit. The index must match HEAD, or this merge cannot go * through. */ - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die_errno("read_cache failed"); if (index_differs_from(the_repository, "HEAD", NULL, 0)) return 2; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 5dc94d6f88..e782518164 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -1,14 +1,21 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "tree-walk.h" #include "xdiff-interface.h" +#include "help.h" +#include "hex.h" +#include "commit.h" +#include "commit-reach.h" +#include "merge-ort.h" #include "object-store.h" +#include "parse-options.h" #include "repository.h" #include "blob.h" #include "exec-cmd.h" #include "merge-blobs.h" +#include "quote.h" -static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>"; +static int line_termination = '\n'; struct merge_list { struct merge_list *next; @@ -28,7 +35,7 @@ static void add_merge_entry(struct merge_list *entry) merge_result_end = &entry->next; } -static void merge_trees(struct tree_desc t[3], const char *base); +static void trivial_merge_trees(struct tree_desc t[3], const char *base); static const char *explanation(struct merge_list *entry) { @@ -93,7 +100,7 @@ static void *origin(struct merge_list *entry, unsigned long *size) return NULL; } -static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf) +static int show_outf(void *priv UNUSED, mmbuffer_t *mb, int nbuf) { int i; for (i = 0; i < nbuf; i++) @@ -225,7 +232,7 @@ static void unresolved_directory(const struct traverse_info *info, buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2)); #undef ENTRY_OID - merge_trees(t, newbase); + trivial_merge_trees(t, newbase); free(buf0); free(buf1); @@ -342,7 +349,7 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s return mask; } -static void merge_trees(struct tree_desc t[3], const char *base) +static void trivial_merge_trees(struct tree_desc t[3], const char *base) { struct traverse_info info; @@ -366,19 +373,18 @@ static void *get_tree_descriptor(struct repository *r, return buf; } -int cmd_merge_tree(int argc, const char **argv, const char *prefix) +static int trivial_merge(const char *base, + const char *branch1, + const char *branch2) { struct repository *r = the_repository; struct tree_desc t[3]; void *buf1, *buf2, *buf3; - if (argc != 4) - usage(merge_tree_usage); - - buf1 = get_tree_descriptor(r, t+0, argv[1]); - buf2 = get_tree_descriptor(r, t+1, argv[2]); - buf3 = get_tree_descriptor(r, t+2, argv[3]); - merge_trees(t, ""); + buf1 = get_tree_descriptor(r, t+0, base); + buf2 = get_tree_descriptor(r, t+1, branch1); + buf3 = get_tree_descriptor(r, t+2, branch2); + trivial_merge_trees(t, ""); free(buf1); free(buf2); free(buf3); @@ -386,3 +392,238 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) show_result(); return 0; } + +enum mode { + MODE_UNKNOWN, + MODE_TRIVIAL, + MODE_REAL, +}; + +struct merge_tree_options { + int mode; + int allow_unrelated_histories; + int show_messages; + int name_only; + int use_stdin; +}; + +static int real_merge(struct merge_tree_options *o, + const char *merge_base, + const char *branch1, const char *branch2, + const char *prefix) +{ + struct commit *parent1, *parent2; + struct commit_list *merge_bases = NULL; + struct merge_options opt; + struct merge_result result = { 0 }; + int show_messages = o->show_messages; + + parent1 = get_merge_parent(branch1); + if (!parent1) + help_unknown_ref(branch1, "merge-tree", + _("not something we can merge")); + + parent2 = get_merge_parent(branch2); + if (!parent2) + help_unknown_ref(branch2, "merge-tree", + _("not something we can merge")); + + init_merge_options(&opt, the_repository); + + opt.show_rename_progress = 0; + + opt.branch1 = branch1; + opt.branch2 = branch2; + + if (merge_base) { + struct commit *base_commit; + struct tree *base_tree, *parent1_tree, *parent2_tree; + + base_commit = lookup_commit_reference_by_name(merge_base); + if (!base_commit) + die(_("could not lookup commit %s"), merge_base); + + opt.ancestor = merge_base; + base_tree = get_commit_tree(base_commit); + parent1_tree = get_commit_tree(parent1); + parent2_tree = get_commit_tree(parent2); + merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result); + } else { + /* + * Get the merge bases, in reverse order; see comment above + * merge_incore_recursive in merge-ort.h + */ + merge_bases = get_merge_bases(parent1, parent2); + if (!merge_bases && !o->allow_unrelated_histories) + die(_("refusing to merge unrelated histories")); + merge_bases = reverse_commit_list(merge_bases); + merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result); + } + + if (result.clean < 0) + die(_("failure to merge")); + + if (show_messages == -1) + show_messages = !result.clean; + + if (o->use_stdin) + printf("%d%c", result.clean, line_termination); + printf("%s%c", oid_to_hex(&result.tree->object.oid), line_termination); + if (!result.clean) { + struct string_list conflicted_files = STRING_LIST_INIT_NODUP; + const char *last = NULL; + int i; + + merge_get_conflicted_files(&result, &conflicted_files); + for (i = 0; i < conflicted_files.nr; i++) { + const char *name = conflicted_files.items[i].string; + struct stage_info *c = conflicted_files.items[i].util; + if (!o->name_only) + printf("%06o %s %d\t", + c->mode, oid_to_hex(&c->oid), c->stage); + else if (last && !strcmp(last, name)) + continue; + write_name_quoted_relative( + name, prefix, stdout, line_termination); + last = name; + } + string_list_clear(&conflicted_files, 1); + } + if (show_messages) { + putchar(line_termination); + merge_display_update_messages(&opt, line_termination == '\0', + &result); + } + if (o->use_stdin) + putchar(line_termination); + merge_finalize(&opt, &result); + return !result.clean; /* result.clean < 0 handled above */ +} + +int cmd_merge_tree(int argc, const char **argv, const char *prefix) +{ + struct merge_tree_options o = { .show_messages = -1 }; + int expected_remaining_argc; + int original_argc; + const char *merge_base = NULL; + + const char * const merge_tree_usage[] = { + N_("git merge-tree [--write-tree] [<options>] <branch1> <branch2>"), + N_("git merge-tree [--trivial-merge] <base-tree> <branch1> <branch2>"), + NULL + }; + struct option mt_options[] = { + OPT_CMDMODE(0, "write-tree", &o.mode, + N_("do a real merge instead of a trivial merge"), + MODE_REAL), + OPT_CMDMODE(0, "trivial-merge", &o.mode, + N_("do a trivial merge only"), MODE_TRIVIAL), + OPT_BOOL(0, "messages", &o.show_messages, + N_("also show informational/conflict messages")), + OPT_SET_INT('z', NULL, &line_termination, + N_("separate paths with the NUL character"), '\0'), + OPT_BOOL_F(0, "name-only", + &o.name_only, + N_("list filenames without modes/oids/stages"), + PARSE_OPT_NONEG), + OPT_BOOL_F(0, "allow-unrelated-histories", + &o.allow_unrelated_histories, + N_("allow merging unrelated histories"), + PARSE_OPT_NONEG), + OPT_BOOL_F(0, "stdin", + &o.use_stdin, + N_("perform multiple merges, one per line of input"), + PARSE_OPT_NONEG), + OPT_STRING(0, "merge-base", + &merge_base, + N_("commit"), + N_("specify a merge-base for the merge")), + OPT_END() + }; + + /* Parse arguments */ + original_argc = argc - 1; /* ignoring argv[0] */ + argc = parse_options(argc, argv, prefix, mt_options, + merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION); + + /* Handle --stdin */ + if (o.use_stdin) { + struct strbuf buf = STRBUF_INIT; + + if (o.mode == MODE_TRIVIAL) + die(_("--trivial-merge is incompatible with all other options")); + if (merge_base) + die(_("--merge-base is incompatible with --stdin")); + line_termination = '\0'; + while (strbuf_getline_lf(&buf, stdin) != EOF) { + struct strbuf **split; + int result; + const char *input_merge_base = NULL; + + split = strbuf_split(&buf, ' '); + if (!split[0] || !split[1]) + die(_("malformed input line: '%s'."), buf.buf); + strbuf_rtrim(split[0]); + strbuf_rtrim(split[1]); + + /* parse the merge-base */ + if (!strcmp(split[1]->buf, "--")) { + input_merge_base = split[0]->buf; + } + + if (input_merge_base && split[2] && split[3] && !split[4]) { + strbuf_rtrim(split[2]); + strbuf_rtrim(split[3]); + result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix); + } else if (!input_merge_base && !split[2]) { + result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix); + } else { + die(_("malformed input line: '%s'."), buf.buf); + } + + if (result < 0) + die(_("merging cannot continue; got unclean result of %d"), result); + strbuf_list_free(split); + } + strbuf_release(&buf); + return 0; + } + + /* Figure out which mode to use */ + switch (o.mode) { + default: + BUG("unexpected command mode %d", o.mode); + case MODE_UNKNOWN: + switch (argc) { + default: + usage_with_options(merge_tree_usage, mt_options); + case 2: + o.mode = MODE_REAL; + break; + case 3: + o.mode = MODE_TRIVIAL; + break; + } + expected_remaining_argc = argc; + break; + case MODE_REAL: + expected_remaining_argc = 2; + break; + case MODE_TRIVIAL: + expected_remaining_argc = 3; + /* Removal of `--trivial-merge` is expected */ + original_argc--; + break; + } + if (o.mode == MODE_TRIVIAL && argc < original_argc) + die(_("--trivial-merge is incompatible with all other options")); + + if (argc != expected_remaining_argc) + usage_with_options(merge_tree_usage, mt_options); + + /* Do the relevant type of merge */ + if (o.mode == MODE_REAL) + return real_merge(&o, merge_base, argv[0], argv[1], prefix); + else + return trivial_merge(argv[0], argv[1], argv[2]); +} diff --git a/builtin/merge.c b/builtin/merge.c index d9784d4891..19c31d4ff4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -6,9 +6,11 @@ * Based on git-merge.sh by Junio C Hamano. */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" +#include "alloc.h" #include "config.h" +#include "hex.h" #include "parse-options.h" #include "builtin.h" #include "lockfile.h" @@ -188,7 +190,7 @@ static struct strategy *get_strategy(const char *name) for (i = 0; i < main_cmds.cnt; i++) { int j, found = 0; struct cmdname *ent = main_cmds.names[i]; - for (j = 0; j < ARRAY_SIZE(all_strategy); j++) + for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++) if (!strncmp(ent->name, all_strategy[j].name, ent->len) && !all_strategy[j].name[ent->len]) found = 1; @@ -313,8 +315,16 @@ static int save_state(struct object_id *stash) int len; struct child_process cp = CHILD_PROCESS_INIT; struct strbuf buffer = STRBUF_INIT; + struct lock_file lock_file = LOCK_INIT; + int fd; int rc = -1; + fd = repo_hold_locked_index(the_repository, &lock_file, 0); + refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); + if (0 <= fd) + repo_update_index_if_able(the_repository, &lock_file); + rollback_lock_file(&lock_file); + strvec_pushl(&cp.args, "stash", "create", NULL); cp.out = -1; cp.git_cmd = 1; @@ -337,62 +347,54 @@ out: return rc; } -static void read_empty(const struct object_id *oid, int verbose) +static void read_empty(const struct object_id *oid) { - int i = 0; - const char *args[7]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "-m"; - args[i++] = "-u"; - args[i++] = empty_tree_oid_hex(); - args[i++] = oid_to_hex(oid); - args[i] = NULL; + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(), + oid_to_hex(oid), NULL); + cmd.git_cmd = 1; - if (run_command_v_opt(args, RUN_GIT_CMD)) + if (run_command(&cmd)) die(_("read-tree failed")); } -static void reset_hard(const struct object_id *oid, int verbose) +static void reset_hard(const struct object_id *oid) { - int i = 0; - const char *args[6]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "--reset"; - args[i++] = "-u"; - args[i++] = oid_to_hex(oid); - args[i] = NULL; + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushl(&cmd.args, "read-tree", "-v", "--reset", "-u", + oid_to_hex(oid), NULL); + cmd.git_cmd = 1; - if (run_command_v_opt(args, RUN_GIT_CMD)) + if (run_command(&cmd)) die(_("read-tree failed")); } static void restore_state(const struct object_id *head, const struct object_id *stash) { - struct strbuf sb = STRBUF_INIT; - const char *args[] = { "stash", "apply", NULL, NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; - if (is_null_oid(stash)) - return; + reset_hard(head); - reset_hard(head, 1); + if (is_null_oid(stash)) + goto refresh_cache; - args[2] = oid_to_hex(stash); + strvec_pushl(&cmd.args, "stash", "apply", "--index", "--quiet", NULL); + strvec_push(&cmd.args, oid_to_hex(stash)); /* * It is OK to ignore error here, for example when there was * nothing to restore. */ - run_command_v_opt(args, RUN_GIT_CMD); + cmd.git_cmd = 1; + run_command(&cmd); - strbuf_release(&sb); - refresh_cache(REFRESH_QUIET); +refresh_cache: + discard_index(&the_index); + if (repo_read_index(the_repository) < 0) + die(_("could not read index")); } /* This is called when no merge was necessary. */ @@ -493,7 +495,8 @@ static void finish(struct commit *head_commit, /* Run a post-merge hook */ run_hooks_l("post-merge", squash ? "1" : "0", NULL); - apply_autostash(git_path_merge_autostash(the_repository)); + if (new_head) + apply_autostash(git_path_merge_autostash(the_repository)); strbuf_release(&reflog_message); } @@ -502,7 +505,6 @@ static void merge_name(const char *remote, struct strbuf *msg) { struct commit *remote_head; struct object_id branch_head; - struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; struct merge_remote_desc *desc; const char *ptr; @@ -590,7 +592,6 @@ static void merge_name(const char *remote, struct strbuf *msg) oid_to_hex(&remote_head->object.oid), remote); cleanup: free(found_ref); - strbuf_release(&buf); strbuf_release(&bname); } @@ -662,9 +663,6 @@ static int git_merge_config(const char *k, const char *v, void *cb) status = fmt_merge_msg_config(k, v, cb); if (status) return status; - status = git_gpg_config(k, v, NULL); - if (status) - return status; return git_diff_ui_config(k, v, cb); } @@ -695,7 +693,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(&active_cache_tree); + cache_tree_free(&the_index.cache_tree); for (i = 0; i < nr_trees; i++) { parse_tree(trees[i]); init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); @@ -707,7 +705,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_cache_as_tree(oid, 0, NULL)) + if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL)) die(_("git write-tree failed to write a tree")); } @@ -717,7 +715,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, { const char *head_arg = "HEAD"; - if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0) + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, + SKIP_IF_UNCHANGED, 0, NULL, NULL, + NULL) < 0) return error(_("Unable to write index.")); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") || @@ -751,15 +751,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, for (j = common; j; j = j->next) commit_list_insert(j->item, &reversed); - hold_locked_index(&lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock, + LOCK_DIE_ON_ERROR); if (!strcmp(strategy, "ort")) clean = merge_ort_recursive(&o, head, remoteheads->item, reversed, &result); else clean = merge_recursive(&o, head, remoteheads->item, reversed, &result); - if (clean < 0) - exit(128); + if (clean < 0) { + rollback_lock_file(&lock); + return 2; + } if (write_locked_index(&the_index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write %s"), get_index_file()); @@ -772,7 +775,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, } static void count_diff_files(struct diff_queue_struct *q, - struct diff_options *opt, void *data) + struct diff_options *opt UNUSED, void *data) { int *count = data; @@ -783,8 +786,8 @@ static int count_unmerged_entries(void) { int i, ret = 0; - for (i = 0; i < active_nr; i++) - if (ce_stage(active_cache[i])) + for (i = 0; i < the_index.cache_nr; i++) + if (ce_stage(the_index.cache[i])) ret++; return ret; @@ -858,9 +861,9 @@ static void prepare_to_commit(struct commit_list *remoteheads) * the editor and after we invoke run_status above. */ if (invoked_hook) - discard_cache(); + discard_index(&the_index); } - read_cache_from(index_file); + read_index_from(&the_index, index_file, get_git_dir()); strbuf_addbuf(&msg, &merge_msg); if (squash) BUG("the control must not reach here under --squash"); @@ -909,7 +912,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) struct object_id result_tree, result_commit; struct commit_list *parents, **pptr = &parents; - if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0) + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, + SKIP_IF_UNCHANGED, 0, NULL, NULL, + NULL) < 0) return error(_("Unable to write index.")); write_tree_trivial(&result_tree); @@ -1375,7 +1380,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } - if (read_cache_unmerged()) + if (repo_read_index_unmerged(the_repository)) die_resolve_conflict("merge"); if (file_exists(git_path_merge_head(the_repository))) { @@ -1396,7 +1401,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } - resolve_undo_clear(); + resolve_undo_clear_index(&the_index); if (option_edit < 0) option_edit = default_edit_option(); @@ -1459,7 +1464,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) check_trust_level); remote_head_oid = &remoteheads->item->object.oid; - read_empty(remote_head_oid, 0); + read_empty(remote_head_oid); update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; @@ -1554,7 +1559,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) !common->next && oideq(&common->item->object.oid, &head_commit->object.oid)) { /* Again the most common case of merging one remote. */ - struct strbuf msg = STRBUF_INIT; + const char *msg = have_message ? + "Fast-forward (no commit created; -m option ignored)" : + "Fast-forward"; struct commit *commit; if (verbosity >= 0) { @@ -1564,10 +1571,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix) find_unique_abbrev(&remoteheads->item->object.oid, DEFAULT_ABBREV)); } - strbuf_addstr(&msg, "Fast-forward"); - if (have_message) - strbuf_addstr(&msg, - " (no commit created; -m option ignored)"); commit = remoteheads->item; if (!commit) { ret = 1; @@ -1586,9 +1589,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } - finish(head_commit, remoteheads, &commit->object.oid, msg.buf); + finish(head_commit, remoteheads, &commit->object.oid, msg); remove_merge_branch_state(the_repository); - strbuf_release(&msg); goto done; } else if (!remoteheads->next && common->next) ; @@ -1601,8 +1603,24 @@ 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_cache(REFRESH_QUIET); + refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); if (allow_trivial && fast_forward != FF_ONLY) { + /* + * Must first ensure that index matches HEAD before + * attempting a trivial merge. + */ + struct tree *head_tree = get_commit_tree(head_commit); + struct strbuf sb = STRBUF_INIT; + + if (repo_index_has_changes(the_repository, head_tree, + &sb)) { + error(_("Your local changes to the following files would be overwritten by merge:\n %s"), + sb.buf); + strbuf_release(&sb); + ret = 2; + goto done; + } + /* See if it is really trivial. */ git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); @@ -1659,15 +1677,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * tree in the index -- this means that the index must be in * sync with the head commit. The strategies are responsible * to ensure this. + * + * Stash away the local changes so that we can try more than one + * and/or recover from merge strategies bailing while leaving the + * index and working tree polluted. */ - if (use_strategies_nr == 1 || - /* - * Stash away the local changes so that we can try more than one. - */ - save_state(&stash)) + if (save_state(&stash)) oidclr(&stash); - for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) { + for (i = 0; i < use_strategies_nr; i++) { int ret, cnt; if (i) { printf(_("Rewinding the tree to pristine...\n")); @@ -1682,7 +1700,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) */ wt_strategy = use_strategies[i]->name; - ret = try_merge_strategy(use_strategies[i]->name, + ret = try_merge_strategy(wt_strategy, common, remoteheads, head_commit); /* @@ -1692,16 +1710,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) */ if (ret < 2) { if (!ret) { - if (option_commit) { - /* Automerge succeeded. */ - automerge_was_ok = 1; - break; - } + /* + * This strategy worked; no point in trying + * another. + */ merge_was_ok = 1; + best_strategy = wt_strategy; + break; } cnt = (use_strategies_nr > 1) ? evaluate_result() : 0; if (best_cnt <= 0 || cnt <= best_cnt) { - best_strategy = use_strategies[i]->name; + best_strategy = wt_strategy; best_cnt = cnt; } } @@ -1711,7 +1730,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * If we have a resulting tree, that means the strategy module * auto resolved the merge cleanly. */ - if (automerge_was_ok) { + if (merge_was_ok && option_commit) { + automerge_was_ok = 1; ret = finish_automerge(head_commit, head_subsumed, common, remoteheads, &result_tree, wt_strategy); @@ -1756,6 +1776,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) "stopped before committing as requested\n")); else ret = suggest_conflicts(); + if (autostash) + printf(_("When finished, apply stashed changes with `git stash pop`\n")); done: if (!automerge_was_ok) { @@ -1764,5 +1786,6 @@ done: } strbuf_release(&buf); free(branch_to_free); + discard_index(&the_index); return ret; } diff --git a/builtin/mktag.c b/builtin/mktag.c index 5d22909122..42c2457c70 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "hex.h" #include "parse-options.h" #include "tag.h" #include "replace-object.h" diff --git a/builtin/mktree.c b/builtin/mktree.c index 902edba6d2..848c7b4747 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -4,6 +4,8 @@ * Copyright (c) Junio C Hamano, 2006, 2009 */ #include "builtin.h" +#include "alloc.h" +#include "hex.h" #include "quote.h" #include "tree.h" #include "parse-options.h" @@ -74,6 +76,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) unsigned mode; enum object_type mode_type; /* object type derived from mode */ enum object_type obj_type; /* object type derived from sha */ + struct object_info oi = OBJECT_INFO_INIT; char *path, *to_free = NULL; struct object_id oid; @@ -116,8 +119,14 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) path, ptr, type_name(mode_type)); } - /* Check the type of object identified by sha1 */ - obj_type = oid_object_info(the_repository, &oid, NULL); + /* Check the type of object identified by oid without fetching objects */ + oi.typep = &obj_type; + if (oid_object_info_extended(the_repository, &oid, &oi, + OBJECT_INFO_LOOKUP_REPLACE | + OBJECT_INFO_QUICK | + OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) + obj_type = -1; + if (obj_type < 0) { if (allow_missing) { ; /* no problem - missing objects are presumed to be of the right type */ diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 5edbb7fe86..9a18a82b05 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -56,11 +56,12 @@ static struct opts_multi_pack_index { static int parse_object_dir(const struct option *opt, const char *arg, int unset) { - free(opts.object_dir); + char **value = opt->value; + free(*value); if (unset) - opts.object_dir = xstrdup(get_object_directory()); + *value = xstrdup(get_object_directory()); else - opts.object_dir = real_pathdup(arg, 1); + *value = real_pathdup(arg, 1); return 0; } @@ -78,7 +79,7 @@ static struct option *add_common_options(struct option *prev) } static int git_multi_pack_index_write_config(const char *var, const char *value, - void *cb) + void *cb UNUSED) { if (!strcmp(var, "pack.writebitmaphashcache")) { if (git_config_bool(var, value)) @@ -87,6 +88,13 @@ static int git_multi_pack_index_write_config(const char *var, const char *value, opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE; } + if (!strcmp(var, "pack.writebitmaplookuptable")) { + if (git_config_bool(var, value)) + opts.flags |= MIDX_WRITE_BITMAP_LOOKUP_TABLE; + else + opts.flags &= ~MIDX_WRITE_BITMAP_LOOKUP_TABLE; + } + /* * We should never make a fall-back call to 'git_default_config', since * this was already called in 'cmd_multi_pack_index()'. @@ -104,7 +112,8 @@ static void read_packs_from_stdin(struct string_list *to) strbuf_release(&buf); } -static int cmd_multi_pack_index_write(int argc, const char **argv) +static int cmd_multi_pack_index_write(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_write_options[] = { @@ -132,9 +141,9 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_write_usage, - PARSE_OPT_KEEP_UNKNOWN); + 0); if (argc) usage_with_options(builtin_multi_pack_index_write_usage, options); @@ -160,7 +169,8 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) opts.refs_snapshot, opts.flags); } -static int cmd_multi_pack_index_verify(int argc, const char **argv) +static int cmd_multi_pack_index_verify(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_verify_options[] = { @@ -174,9 +184,9 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_verify_usage, - PARSE_OPT_KEEP_UNKNOWN); + 0); if (argc) usage_with_options(builtin_multi_pack_index_verify_usage, options); @@ -186,7 +196,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv) return verify_midx_file(the_repository, opts.object_dir, opts.flags); } -static int cmd_multi_pack_index_expire(int argc, const char **argv) +static int cmd_multi_pack_index_expire(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_expire_options[] = { @@ -200,9 +211,9 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_expire_usage, - PARSE_OPT_KEEP_UNKNOWN); + 0); if (argc) usage_with_options(builtin_multi_pack_index_expire_usage, options); @@ -212,7 +223,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv) return expire_midx_packs(the_repository, opts.object_dir, opts.flags); } -static int cmd_multi_pack_index_repack(int argc, const char **argv) +static int cmd_multi_pack_index_repack(int argc, const char **argv, + const char *prefix) { struct option *options; static struct option builtin_multi_pack_index_repack_options[] = { @@ -229,10 +241,10 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv) if (isatty(2)) opts.flags |= MIDX_PROGRESS; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_repack_usage, - PARSE_OPT_KEEP_UNKNOWN); + 0); if (argc) usage_with_options(builtin_multi_pack_index_repack_usage, options); @@ -247,7 +259,15 @@ int cmd_multi_pack_index(int argc, const char **argv, const char *prefix) { int res; - struct option *builtin_multi_pack_index_options = common_opts; + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_multi_pack_index_options[] = { + OPT_SUBCOMMAND("repack", &fn, cmd_multi_pack_index_repack), + OPT_SUBCOMMAND("write", &fn, cmd_multi_pack_index_write), + OPT_SUBCOMMAND("verify", &fn, cmd_multi_pack_index_verify), + OPT_SUBCOMMAND("expire", &fn, cmd_multi_pack_index_expire), + OPT_END(), + }; + struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts); git_config(git_default_config, NULL); @@ -256,31 +276,12 @@ int cmd_multi_pack_index(int argc, const char **argv, the_repository->objects->odb) opts.object_dir = xstrdup(the_repository->objects->odb->path); - argc = parse_options(argc, argv, prefix, - builtin_multi_pack_index_options, - builtin_multi_pack_index_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (!argc) - goto usage; - - if (!strcmp(argv[0], "repack")) - res = cmd_multi_pack_index_repack(argc, argv); - else if (!strcmp(argv[0], "write")) - res = cmd_multi_pack_index_write(argc, argv); - else if (!strcmp(argv[0], "verify")) - res = cmd_multi_pack_index_verify(argc, argv); - else if (!strcmp(argv[0], "expire")) - res = cmd_multi_pack_index_expire(argc, argv); - else { - error(_("unrecognized subcommand: %s"), argv[0]); - goto usage; - } + argc = parse_options(argc, argv, prefix, options, + builtin_multi_pack_index_usage, 0); + FREE_AND_NULL(options); + + res = fn(argc, argv, prefix); free(opts.object_dir); return res; - -usage: - usage_with_options(builtin_multi_pack_index_usage, - builtin_multi_pack_index_options); } diff --git a/builtin/mv.c b/builtin/mv.c index 83a465ba83..8129050377 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -3,8 +3,9 @@ * * Copyright (C) 2006 Johannes Schindelin */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "alloc.h" #include "config.h" #include "pathspec.h" #include "lockfile.h" @@ -13,12 +14,20 @@ #include "string-list.h" #include "parse-options.h" #include "submodule.h" +#include "entry.h" static const char * const builtin_mv_usage[] = { N_("git mv [<options>] <source>... <destination>"), NULL }; +enum update_mode { + WORKING_DIRECTORY = (1 << 1), + INDEX = (1 << 2), + SPARSE = (1 << 3), + SKIP_WORKTREE_DIR = (1 << 4), +}; + #define DUP_BASENAME 1 #define KEEP_TRAILING_SLASH 2 @@ -63,7 +72,7 @@ static const char **internal_prefix_pathspec(const char *prefix, static const char *add_slash(const char *path) { size_t len = strlen(path); - if (path[len - 1] != '/') { + if (len && path[len - 1] != '/') { char *with_slash = xmalloc(st_add(len, 2)); memcpy(with_slash, path, len); with_slash[len++] = '/'; @@ -79,7 +88,7 @@ static void prepare_move_submodule(const char *src, int first, const char **submodule_gitfile) { struct strbuf submodule_dotgit = STRBUF_INIT; - if (!S_ISGITLINK(active_cache[first]->ce_mode)) + if (!S_ISGITLINK(the_index.cache[first]->ce_mode)) die(_("Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok(&the_index)) die(_("Please stage your changes to .gitmodules or stash them to proceed")); @@ -98,13 +107,13 @@ static int index_range_of_same_dir(const char *src, int length, const char *src_w_slash = add_slash(src); int first, last, len_w_slash = length + 1; - first = cache_name_pos(src_w_slash, len_w_slash); + first = index_name_pos(&the_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 < active_nr; last++) { - const char *path = active_cache[last]->name; + for (last = first; last < the_index.cache_nr; last++) { + const char *path = the_index.cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } @@ -115,6 +124,39 @@ static int index_range_of_same_dir(const char *src, int length, return last - first; } +/* + * Given the path of a directory that does not exist on-disk, check whether the + * directory contains any entries in the index with the SKIP_WORKTREE flag + * enabled. + * Return 1 if such index entries exist. + * Return 0 otherwise. + */ +static int empty_dir_has_sparse_contents(const char *name) +{ + int ret = 0; + const char *with_slash = add_slash(name); + int length = strlen(with_slash); + + int pos = index_name_pos(&the_index, with_slash, length); + const struct cache_entry *ce; + + if (pos < 0) { + pos = -pos - 1; + if (pos >= the_index.cache_nr) + goto free_return; + ce = the_index.cache[pos]; + if (strncmp(with_slash, ce->name, length)) + goto free_return; + if (ce_skip_worktree(ce)) + ret = 1; + } + +free_return: + if (with_slash != name) + free((char *)with_slash); + return ret; +} + int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; @@ -129,12 +171,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix) OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; - enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX, SPARSE } *modes; + const char *dst_w_slash; + const char **src_dir = NULL; + int src_dir_nr = 0, src_dir_alloc = 0; + struct strbuf a_src_dir = STRBUF_INIT; + enum update_mode *modes, dst_mode = 0; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; struct lock_file lock_file = LOCK_INIT; struct cache_entry *ce; struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; + struct string_list dirty_paths = STRING_LIST_INIT_NODUP; git_config(git_default_config, NULL); @@ -143,12 +190,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - if (read_cache() < 0) + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); source = internal_prefix_pathspec(prefix, argv, argc, 0); - modes = xcalloc(argc, sizeof(enum update_mode)); + CALLOC_ARRAY(modes, argc); + /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out, except in the case @@ -158,6 +206,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); + dst_w_slash = add_slash(dest_path[0]); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') @@ -165,18 +214,37 @@ int cmd_mv(int argc, const char **argv, const char *prefix) destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { - dest_path[0] = add_slash(dest_path[0]); - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); } else { - if (argc != 1) + if (!path_in_sparse_checkout(dst_w_slash, &the_index) && + empty_dir_has_sparse_contents(dst_w_slash)) { + destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME); + dst_mode = SKIP_WORKTREE_DIR; + } else if (argc != 1) { die(_("destination '%s' is not a directory"), dest_path[0]); - destination = dest_path; + } else { + destination = dest_path; + /* + * <destination> is a file outside of sparse-checkout + * cone. Insist on cone mode here for backward + * compatibility. We don't want dst_mode to be assigned + * for a file when the repo is using no-cone mode (which + * is deprecated at this point) sparse-checkout. As + * SPARSE here is only considering cone-mode situation. + */ + if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index)) + dst_mode = SPARSE; + } + } + if (dst_w_slash != dest_path[0]) { + free((char *)dst_w_slash); + dst_w_slash = NULL; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; - int length, src_is_dir; + int length; const char *bad = NULL; int skip_sparse = 0; @@ -185,54 +253,107 @@ int cmd_mv(int argc, const char **argv, const char *prefix) length = strlen(src); if (lstat(src, &st) < 0) { - /* only error if existence is expected. */ - if (modes[i] != SPARSE) + int pos; + const struct cache_entry *ce; + + pos = index_name_pos(&the_index, src, length); + if (pos < 0) { + const char *src_w_slash = add_slash(src); + if (!path_in_sparse_checkout(src_w_slash, &the_index) && + empty_dir_has_sparse_contents(src)) { + modes[i] |= SKIP_WORKTREE_DIR; + goto dir_check; + } + /* only error if existence is expected. */ + if (!(modes[i] & SPARSE)) + bad = _("bad source"); + goto act_on_entry; + } + ce = the_index.cache[pos]; + if (!ce_skip_worktree(ce)) { bad = _("bad source"); - } else if (!strncmp(src, dst, length) && - (dst[length] == 0 || dst[length] == '/')) { + goto act_on_entry; + } + if (!ignore_sparse) { + string_list_append(&only_match_skip_worktree, src); + goto act_on_entry; + } + /* Check if dst exists in index */ + if (index_name_pos(&the_index, dst, strlen(dst)) < 0) { + modes[i] |= SPARSE; + goto act_on_entry; + } + if (!force) { + bad = _("destination exists"); + goto act_on_entry; + } + modes[i] |= SPARSE; + goto act_on_entry; + } + if (!strncmp(src, dst, length) && + (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); - } else if ((src_is_dir = S_ISDIR(st.st_mode)) - && lstat(dst, &st) == 0) + goto act_on_entry; + } + if (S_ISDIR(st.st_mode) + && lstat(dst, &st) == 0) { bad = _("cannot move directory over file"); - else if (src_is_dir) { - int first = cache_name_pos(src, length), last; + goto act_on_entry; + } - if (first >= 0) +dir_check: + if (S_ISDIR(st.st_mode)) { + int j, dst_len, n; + int first = index_name_pos(&the_index, src, length), last; + + if (first >= 0) { prepare_move_submodule(src, first, submodule_gitfile + i); - else if (index_range_of_same_dir(src, length, - &first, &last) < 1) + goto act_on_entry; + } else if (index_range_of_same_dir(src, length, + &first, &last) < 1) { bad = _("source directory is empty"); - else { /* last - first >= 1 */ - int j, dst_len, n; - - modes[i] = WORKING_DIRECTORY; - n = argc + last - first; - REALLOC_ARRAY(source, n); - REALLOC_ARRAY(destination, n); - REALLOC_ARRAY(modes, n); - REALLOC_ARRAY(submodule_gitfile, n); - - dst = add_slash(dst); - dst_len = strlen(dst); - - for (j = 0; j < last - first; j++) { - const struct cache_entry *ce = active_cache[first + j]; - const char *path = ce->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, path + length + 1); - modes[argc + j] = ce_skip_worktree(ce) ? SPARSE : INDEX; - submodule_gitfile[argc + j] = NULL; - } - argc += last - first; + goto act_on_entry; } - } else if (!(ce = cache_file_exists(src, length, 0))) { + + /* last - first >= 1 */ + modes[i] |= WORKING_DIRECTORY; + + ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc); + src_dir[src_dir_nr++] = src; + + n = argc + last - first; + REALLOC_ARRAY(source, n); + REALLOC_ARRAY(destination, n); + REALLOC_ARRAY(modes, n); + REALLOC_ARRAY(submodule_gitfile, n); + + dst = add_slash(dst); + dst_len = strlen(dst); + + for (j = 0; j < last - first; j++) { + const struct cache_entry *ce = the_index.cache[first + j]; + const char *path = ce->name; + source[argc + j] = path; + destination[argc + j] = + prefix_path(dst, dst_len, path + length + 1); + memset(modes + argc + j, 0, sizeof(enum update_mode)); + modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX; + submodule_gitfile[argc + j] = NULL; + } + argc += last - first; + goto act_on_entry; + } + if (!(ce = index_file_exists(&the_index, src, length, 0))) { bad = _("not under version control"); - } else if (ce_stage(ce)) { + goto act_on_entry; + } + if (ce_stage(ce)) { bad = _("conflicted"); - } else if (lstat(dst, &st) == 0 && - (!ignore_case || strcasecmp(src, dst))) { + goto act_on_entry; + } + if (lstat(dst, &st) == 0 && + (!ignore_case || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /* @@ -246,34 +367,52 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } else bad = _("Cannot overwrite"); } - } else if (string_list_has_string(&src_for_dst, dst)) + goto act_on_entry; + } + if (string_list_has_string(&src_for_dst, dst)) { bad = _("multiple sources for the same target"); - else if (is_dir_sep(dst[strlen(dst) - 1])) + goto act_on_entry; + } + if (is_dir_sep(dst[strlen(dst) - 1])) { bad = _("destination directory does not exist"); - else { - /* - * We check if the paths are in the sparse-checkout - * definition as a very final check, since that - * allows us to point the user to the --sparse - * option as a way to have a successful run. - */ - if (!ignore_sparse && - !path_in_sparse_checkout(src, &the_index)) { - string_list_append(&only_match_skip_worktree, src); - skip_sparse = 1; - } - if (!ignore_sparse && - !path_in_sparse_checkout(dst, &the_index)) { - string_list_append(&only_match_skip_worktree, dst); - skip_sparse = 1; + goto act_on_entry; + } + + if (ignore_sparse && + (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && + index_entry_exists(&the_index, dst, strlen(dst))) { + bad = _("destination exists in the index"); + if (force) { + if (verbose) + warning(_("overwriting '%s'"), dst); + bad = NULL; + } else { + goto act_on_entry; } + } + /* + * We check if the paths are in the sparse-checkout + * definition as a very final check, since that + * allows us to point the user to the --sparse + * option as a way to have a successful run. + */ + if (!ignore_sparse && + !path_in_sparse_checkout(src, &the_index)) { + string_list_append(&only_match_skip_worktree, src); + skip_sparse = 1; + } + if (!ignore_sparse && + !path_in_sparse_checkout(dst, &the_index)) { + string_list_append(&only_match_skip_worktree, dst); + skip_sparse = 1; + } - if (skip_sparse) - goto remove_entry; + if (skip_sparse) + goto remove_entry; - string_list_insert(&src_for_dst, dst); - } + string_list_insert(&src_for_dst, dst); +act_on_entry: if (!bad) continue; if (!ignore_errors) @@ -282,14 +421,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix) remove_entry: if (--argc > 0) { int n = argc - i; - memmove(source + i, source + i + 1, - n * sizeof(char *)); - memmove(destination + i, destination + i + 1, - n * sizeof(char *)); - memmove(modes + i, modes + i + 1, - n * sizeof(enum update_mode)); - memmove(submodule_gitfile + i, submodule_gitfile + i + 1, - n * sizeof(char *)); + MOVE_ARRAY(source + i, source + i + 1, n); + MOVE_ARRAY(destination + i, destination + i + 1, n); + MOVE_ARRAY(modes + i, modes + i + 1, n); + MOVE_ARRAY(submodule_gitfile + i, + submodule_gitfile + i + 1, n); i--; } } @@ -304,11 +440,19 @@ remove_entry: const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; + int sparse_and_dirty = 0; + struct checkout state = CHECKOUT_INIT; + state.istate = &the_index; + + if (force) + state.force = 1; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (show_only) continue; - if (mode != INDEX && mode != SPARSE && rename(src, dst) < 0) { + if (!(mode & (INDEX | SPARSE | SKIP_WORKTREE_DIR)) && + !(dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && + rename(src, dst) < 0) { if (ignore_errors) continue; die_errno(_("renaming '%s' failed"), src); @@ -322,14 +466,91 @@ remove_entry: 1); } - if (mode == WORKING_DIRECTORY) + if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR)) continue; - pos = cache_name_pos(src, strlen(src)); + pos = index_name_pos(&the_index, src, strlen(src)); assert(pos >= 0); - rename_cache_entry_at(pos, dst); + if (!(mode & SPARSE) && !lstat(src, &st)) + sparse_and_dirty = ie_modified(&the_index, + the_index.cache[pos], + &st, + 0); + rename_index_entry_at(&the_index, pos, dst); + + if (ignore_sparse && + core_apply_sparse_checkout && + core_sparse_checkout_cone) { + /* + * NEEDSWORK: we are *not* paying attention to + * "out-to-out" move (<source> is out-of-cone and + * <destination> is out-of-cone) at this point. It + * should be added in a future patch. + */ + if ((mode & SPARSE) && + path_in_sparse_checkout(dst, &the_index)) { + /* from out-of-cone to in-cone */ + int dst_pos = index_name_pos(&the_index, dst, + strlen(dst)); + struct cache_entry *dst_ce = the_index.cache[dst_pos]; + + dst_ce->ce_flags &= ~CE_SKIP_WORKTREE; + + if (checkout_entry(dst_ce, &state, NULL, NULL)) + die(_("cannot checkout %s"), dst_ce->name); + } else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) && + !(mode & SPARSE) && + !path_in_sparse_checkout(dst, &the_index)) { + /* from in-cone to out-of-cone */ + int dst_pos = index_name_pos(&the_index, dst, + strlen(dst)); + struct cache_entry *dst_ce = the_index.cache[dst_pos]; + + /* + * if src is clean, it will suffice to remove it + */ + if (!sparse_and_dirty) { + dst_ce->ce_flags |= CE_SKIP_WORKTREE; + unlink_or_warn(src); + } else { + /* + * if src is dirty, move it to the + * destination and create leading + * dirs if necessary + */ + char *dst_dup = xstrdup(dst); + string_list_append(&dirty_paths, dst); + safe_create_leading_directories(dst_dup); + FREE_AND_NULL(dst_dup); + rename(src, dst); + } + } + } } + /* + * cleanup the empty src_dirs + */ + for (i = 0; i < src_dir_nr; i++) { + int dummy; + strbuf_addstr(&a_src_dir, src_dir[i]); + /* + * if entries under a_src_dir are all moved away, + * recursively remove a_src_dir to cleanup + */ + if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len, + &dummy, &dummy) < 1) { + remove_dir_recursively(&a_src_dir, 0); + } + strbuf_reset(&a_src_dir); + } + + strbuf_release(&a_src_dir); + free(src_dir); + + if (dirty_paths.nr) + advise_on_moving_dirty_path(&dirty_paths); + if (gitmodules_modified) stage_updated_gitmodules(&the_index); @@ -338,6 +559,7 @@ remove_entry: die(_("Unable to write new index file")); string_list_clear(&src_for_dst, 0); + string_list_clear(&dirty_paths, 0); UNLEAK(source); UNLEAK(dest_path); free(submodule_gitfile); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 02ea9d1633..723ba616a8 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -1,5 +1,6 @@ #include "builtin.h" -#include "cache.h" +#include "alloc.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "commit.h" @@ -108,19 +109,11 @@ static int is_better_name(struct rev_name *name, int name_distance = effective_distance(name->distance, name->generation); int new_distance = effective_distance(distance, generation); - /* - * When comparing names based on tags, prefer names - * based on the older tag, even if it is farther away. - */ + /* If both are tags, we prefer the nearer one. */ if (from_tag && name->from_tag) - return (name->taggerdate > taggerdate || - (name->taggerdate == taggerdate && - name_distance > new_distance)); + return name_distance > new_distance; - /* - * We know that at least one of them is a non-tag at this point. - * favor a tag over a non-tag. - */ + /* Favor a tag over a non-tag. */ if (name->from_tag != from_tag) return from_tag; @@ -273,17 +266,6 @@ static int subpath_matches(const char *path, const char *filter) return -1; } -static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous) -{ - if (shorten_unambiguous) - refname = shorten_unambiguous_ref(refname, 0); - else if (skip_prefix(refname, "refs/heads/", &refname)) - ; /* refname already advanced */ - else - skip_prefix(refname, "refs/", &refname); - return refname; -} - struct name_ref_data { int tags_only; int name_only; @@ -309,11 +291,19 @@ static void add_to_tip_table(const struct object_id *oid, const char *refname, int shorten_unambiguous, struct commit *commit, timestamp_t taggerdate, int from_tag, int deref) { - refname = name_ref_abbrev(refname, shorten_unambiguous); + char *short_refname = NULL; + + if (shorten_unambiguous) + short_refname = shorten_unambiguous_ref(refname, 0); + else if (skip_prefix(refname, "refs/heads/", &refname)) + ; /* refname already advanced */ + else + skip_prefix(refname, "refs/", &refname); ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc); oidcpy(&tip_table.table[tip_table.nr].oid, oid); - tip_table.table[tip_table.nr].refname = xstrdup(refname); + tip_table.table[tip_table.nr].refname = short_refname ? + short_refname : xstrdup(refname); tip_table.table[tip_table.nr].commit = commit; tip_table.table[tip_table.nr].taggerdate = taggerdate; tip_table.table[tip_table.nr].from_tag = from_tag; @@ -344,7 +334,8 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_) return a->taggerdate != b->taggerdate; } -static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) +static int name_ref(const char *path, const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct object *o = parse_object(the_repository, oid); struct name_ref_data *data = cb_data; @@ -577,7 +568,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) N_("ignore refs matching <pattern>")), OPT_GROUP(""), OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), - OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use annotate-stdin instead")), + OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use --annotate-stdin instead")), OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")), OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), OPT_BOOL(0, "always", &always, diff --git a/builtin/notes.c b/builtin/notes.c index a3d0d15a22..75ce7f3f57 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -10,6 +10,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" +#include "hex.h" #include "notes.h" #include "object-store.h" #include "repository.h" @@ -113,8 +114,9 @@ static void free_note_data(struct note_data *d) } static int list_each_note(const struct object_id *object_oid, - const struct object_id *note_oid, char *note_path, - void *cb_data) + const struct object_id *note_oid, + char *note_path UNUSED, + void *cb_data UNUSED) { printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid)); return 0; @@ -181,7 +183,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * strbuf_addch(&buf, '\n'); strbuf_add_commented_lines(&buf, "\n", strlen("\n")); strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template))); - strbuf_addch(&buf, '\n'); + strbuf_add_commented_lines(&buf, "\n", strlen("\n")); write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); @@ -994,17 +996,34 @@ static int get_ref(int argc, const char **argv, const char *prefix) int cmd_notes(int argc, const char **argv, const char *prefix) { - int result; const char *override_notes_ref = NULL; + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"), N_("use notes from <notes-ref>")), + OPT_SUBCOMMAND("list", &fn, list), + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("copy", &fn, copy), + OPT_SUBCOMMAND("append", &fn, append_edit), + OPT_SUBCOMMAND("edit", &fn, append_edit), + OPT_SUBCOMMAND("show", &fn, show), + OPT_SUBCOMMAND("merge", &fn, merge), + OPT_SUBCOMMAND("remove", &fn, remove_cmd), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("get-ref", &fn, get_ref), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_notes_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + PARSE_OPT_SUBCOMMAND_OPTIONAL); + if (!fn) { + if (argc) { + error(_("unknown subcommand: `%s'"), argv[0]); + usage_with_options(git_notes_usage, options); + } + fn = list; + } if (override_notes_ref) { struct strbuf sb = STRBUF_INIT; @@ -1014,28 +1033,5 @@ int cmd_notes(int argc, const char **argv, const char *prefix) strbuf_release(&sb); } - if (argc < 1 || !strcmp(argv[0], "list")) - result = list(argc, argv, prefix); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv, prefix); - else if (!strcmp(argv[0], "copy")) - result = copy(argc, argv, prefix); - else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit")) - result = append_edit(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv, prefix); - else if (!strcmp(argv[0], "merge")) - result = merge(argc, argv, prefix); - else if (!strcmp(argv[0], "remove")) - result = remove_cmd(argc, argv, prefix); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv, prefix); - else if (!strcmp(argv[0], "get-ref")) - result = get_ref(argc, argv, prefix); - else { - result = error(_("unknown subcommand: %s"), argv[0]); - usage_with_options(git_notes_usage, options); - } - - return result ? 1 : 0; + return !!fn(argc, argv, prefix); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index cc5f41086d..545b8bddc8 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1,5 +1,6 @@ #include "builtin.h" -#include "cache.h" +#include "alloc.h" +#include "hex.h" #include "repository.h" #include "config.h" #include "attr.h" @@ -31,6 +32,7 @@ #include "list.h" #include "packfile.h" #include "object-store.h" +#include "replace-object.h" #include "dir.h" #include "midx.h" #include "trace2.h" @@ -180,8 +182,8 @@ static inline void oe_set_delta_size(struct packing_data *pack, #define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val) static const char *pack_usage[] = { - N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"), - N_("git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"), + N_("git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"), + N_("git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"), NULL }; @@ -759,8 +761,8 @@ static enum write_one_status write_one(struct hashfile *f, return WRITE_ONE_WRITTEN; } -static int mark_tagged(const char *path, const struct object_id *oid, int flag, - void *cb_data) +static int mark_tagged(const char *path UNUSED, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { struct object_id peeled; struct object_entry *entry = packlist_find(&to_pack, oid); @@ -929,8 +931,10 @@ static struct object_entry **compute_write_order(void) */ for_each_tag_ref(mark_tagged, NULL); - if (use_delta_islands) + if (use_delta_islands) { max_layers = compute_pack_layers(&to_pack); + free_island_marks(); + } ALLOC_ARRAY(wo, to_pack.nr_objects); wo_end = 0; @@ -1318,7 +1322,7 @@ static int no_try_delta(const char *path) if (!check) check = attr_check_initl("delta", NULL); - git_check_attr(the_repository->index, path, check); + git_check_attr(the_repository->index, NULL, path, check); if (ATTR_FALSE(check->items[0].value)) return 1; return 0; @@ -1588,7 +1592,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, static int add_object_entry_from_bitmap(const struct object_id *oid, enum object_type type, - int flags, uint32_t name_hash, + int flags UNUSED, uint32_t name_hash, struct packed_git *pack, off_t offset) { display_progress(progress_state, ++nr_seen); @@ -1708,17 +1712,14 @@ static void pbase_tree_put(struct pbase_tree_cache *cache) free(cache); } -static int name_cmp_len(const char *name) +static size_t name_cmp_len(const char *name) { - int i; - for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++) - ; - return i; + return strcspn(name, "\n/"); } static void add_pbase_object(struct tree_desc *tree, const char *name, - int cmplen, + size_t cmplen, const char *fullname) { struct name_entry entry; @@ -1743,7 +1744,7 @@ static void add_pbase_object(struct tree_desc *tree, struct tree_desc sub; struct pbase_tree_cache *tree; const char *down = name+cmplen+1; - int downlen = name_cmp_len(down); + size_t downlen = name_cmp_len(down); tree = pbase_tree_get(&entry.oid); if (!tree) @@ -1795,7 +1796,7 @@ static int check_pbase_path(unsigned hash) static void add_preferred_base_object(const char *name) { struct pbase_tree *it; - int cmplen; + size_t cmplen; unsigned hash = pack_name_hash(name); if (!num_preferred_base || check_pbase_path(hash)) @@ -3035,7 +3036,8 @@ static void add_tag_chain(const struct object_id *oid) } } -static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data) +static int add_ref_tag(const char *tag UNUSED, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { struct object_id peeled; @@ -3148,6 +3150,14 @@ static int git_pack_config(const char *k, const char *v, void *cb) else write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE; } + + if (!strcmp(k, "pack.writebitmaplookuptable")) { + if (git_config_bool(k, v)) + write_bitmap_options |= BITMAP_OPT_LOOKUP_TABLE; + else + write_bitmap_options &= ~BITMAP_OPT_LOOKUP_TABLE; + } + if (!strcmp(k, "pack.usebitmaps")) { use_bitmap_index_default = git_config_bool(k, v); return 0; @@ -3170,7 +3180,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) if (!strcmp(k, "pack.indexversion")) { pack_idx_opts.version = git_config_int(k, v); if (pack_idx_opts.version > 2) - die(_("bad pack.indexversion=%"PRIu32), + die(_("bad pack.indexVersion=%"PRIu32), pack_idx_opts.version); return 0; } @@ -3252,13 +3262,14 @@ static int add_object_entry_from_pack(const struct object_id *oid, return 0; } -static void show_commit_pack_hint(struct commit *commit, void *_data) +static void show_commit_pack_hint(struct commit *commit UNUSED, + void *data UNUSED) { /* nothing to do; commits don't have a namehash */ } static void show_object_pack_hint(struct object *object, const char *name, - void *_data) + void *data UNUSED) { struct object_entry *oe = packlist_find(&to_pack, &object->oid); if (!oe) @@ -3456,7 +3467,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type return; } -static void show_cruft_object(struct object *obj, const char *name, void *data) +static void show_cruft_object(struct object *obj, const char *name, void *data UNUSED) { /* * if we did not record it earlier, it's at least as old as our @@ -3476,7 +3487,7 @@ static void show_cruft_commit(struct commit *commit, void *data) show_cruft_object((struct object*)commit, NULL, data); } -static int cruft_include_check_obj(struct object *obj, void *data) +static int cruft_include_check_obj(struct object *obj, void *data UNUSED) { return !has_object_kept_pack(&obj->oid, IN_CORE_KEEP_PACKS); } @@ -3655,7 +3666,7 @@ static void read_object_list_from_stdin(void) } } -static void show_commit(struct commit *commit, void *data) +static void show_commit(struct commit *commit, void *data UNUSED) { add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0); @@ -3666,7 +3677,8 @@ static void show_commit(struct commit *commit, void *data) propagate_island_marks(commit); } -static void show_object(struct object *obj, const char *name, void *data) +static void show_object(struct object *obj, const char *name, + void *data UNUSED) { add_preferred_base_object(name); add_object_entry(&obj->oid, obj->type, name, 0); @@ -3753,7 +3765,7 @@ static void show_edge(struct commit *commit) static int add_object_in_unpacked_pack(const struct object_id *oid, struct packed_git *pack, uint32_t pos, - void *_data) + void *data UNUSED) { if (cruft) { off_t offset; @@ -3787,7 +3799,7 @@ static void add_objects_in_unpacked_packs(void) } static int add_loose_object(const struct object_id *oid, const char *path, - void *data) + void *data UNUSED) { enum object_type type = oid_object_info(the_repository, oid, NULL); @@ -3938,20 +3950,21 @@ static int get_object_list_from_bitmap(struct rev_info *revs) } static void record_recent_object(struct object *obj, - const char *name, - void *data) + const char *name UNUSED, + void *data UNUSED) { oid_array_append(&recent_objects, &obj->oid); } -static void record_recent_commit(struct commit *commit, void *data) +static void record_recent_commit(struct commit *commit, void *data UNUSED) { oid_array_append(&recent_objects, &commit->object.oid); } static int mark_bitmap_preferred_tip(const char *refname, - const struct object_id *oid, int flags, - void *_data) + const struct object_id *oid, + int flags UNUSED, + void *data UNUSED) { struct object_id peeled; struct object *object; @@ -4139,21 +4152,6 @@ static int option_parse_cruft_expiration(const struct option *opt, return 0; } -struct po_filter_data { - unsigned have_revs:1; - struct rev_info revs; -}; - -static struct list_objects_filter_options *po_filter_revs_init(void *value) -{ - struct po_filter_data *data = value; - - repo_init_revisions(the_repository, &data->revs, NULL); - data->have_revs = 1; - - return &data->revs.filter; -} - int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; @@ -4164,7 +4162,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int rev_list_index = 0; int stdin_packs = 0; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - struct po_filter_data pfd = { .have_revs = 0 }; + struct list_objects_filter_options filter_options = + LIST_OBJECTS_FILTER_INIT; struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, @@ -4255,7 +4254,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) &write_bitmap_index, N_("write a bitmap index if possible"), WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN), - OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_CALLBACK_F(0, "missing", NULL, N_("action"), N_("handling for missing objects"), PARSE_OPT_NONEG, option_parse_missing_action), @@ -4375,7 +4374,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (pfd.have_revs && pfd.revs.filter.choice) { + if (filter_options.choice) { if (!pack_to_stdout) die(_("cannot use --filter without --stdout")); if (stdin_packs) @@ -4462,13 +4461,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_cruft_objects(); } else if (!use_internal_rev_list) { read_object_list_from_stdin(); - } else if (pfd.have_revs) { - get_object_list(&pfd.revs, rp.nr, rp.v); - release_revisions(&pfd.revs); } else { struct rev_info revs; repo_init_revisions(the_repository, &revs, NULL); + list_objects_filter_copy(&revs.filter, &filter_options); get_object_list(&revs, rp.nr, rp.v); release_revisions(&revs); } @@ -4503,6 +4500,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) reuse_packfile_objects); cleanup: + list_objects_filter_release(&filter_options); strvec_clear(&rp); return 0; diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index ed9b9013a5..82115c5808 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -7,6 +7,7 @@ */ #include "builtin.h" +#include "hex.h" #include "repository.h" #include "packfile.h" #include "object-store.h" @@ -14,7 +15,7 @@ #define BLKSIZE 512 static const char pack_redundant_usage[] = -"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)"; +"git pack-redundant [--verbose] [--alt-odb] (--all | <pack-filename>...)"; static int load_all_packs, verbose, alt_odb; diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index cfbd5c36c7..27c2ca06ac 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -5,7 +5,7 @@ #include "repository.h" static char const * const pack_refs_usage[] = { - N_("git pack-refs [<options>]"), + N_("git pack-refs [--all] [--no-prune]"), NULL }; diff --git a/builtin/patch-id.c b/builtin/patch-id.c index 881fcf3273..338b15cd7b 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -2,6 +2,8 @@ #include "builtin.h" #include "config.h" #include "diff.h" +#include "hex.h" +#include "parse-options.h" static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) { @@ -57,10 +59,12 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after) } static int get_one_patchid(struct object_id *next_oid, struct object_id *result, - struct strbuf *line_buf, int stable) + struct strbuf *line_buf, int stable, int verbatim) { int patchlen = 0, found_next = 0; int before = -1, after = -1; + int diff_is_binary = 0; + char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1]; git_hash_ctx ctx; the_hash_algo->init_fn(&ctx); @@ -71,11 +75,14 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, const char *p = line; int len; - if (!skip_prefix(line, "diff-tree ", &p) && - !skip_prefix(line, "commit ", &p) && + /* 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)) + 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; @@ -88,14 +95,44 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, /* Parsing diff header? */ if (before == -1) { - if (starts_with(line, "index ")) + if (starts_with(line, "GIT binary patch") || + starts_with(line, "Binary files")) { + diff_is_binary = 1; + before = 0; + the_hash_algo->update_fn(&ctx, pre_oid_str, + strlen(pre_oid_str)); + the_hash_algo->update_fn(&ctx, post_oid_str, + strlen(post_oid_str)); + if (stable) + flush_one_hunk(result, &ctx); + continue; + } else if (skip_prefix(line, "index ", &p)) { + char *oid1_end = strstr(line, ".."); + char *oid2_end = NULL; + if (oid1_end) + oid2_end = strstr(oid1_end, " "); + if (!oid2_end) + oid2_end = line + strlen(line) - 1; + if (oid1_end != NULL && oid2_end != NULL) { + *oid1_end = *oid2_end = '\0'; + strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1); + strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1); + } continue; - else if (starts_with(line, "--- ")) + } else if (starts_with(line, "--- ")) before = after = 1; else if (!isalpha(line[0])) break; } + if (diff_is_binary) { + if (starts_with(line, "diff ")) { + diff_is_binary = 0; + before = -1; + } + continue; + } + /* Looking for a valid hunk header? */ if (before == 0 && after == 0) { if (starts_with(line, "@@ -")) { @@ -120,8 +157,8 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, if (line[0] == '+' || line[0] == ' ') after--; - /* Compute the sha without whitespace */ - len = remove_space(line); + /* Add line to hash algo (possibly removing whitespace) */ + len = verbatim ? strlen(line) : remove_space(line); patchlen += len; the_hash_algo->update_fn(&ctx, line, len); } @@ -134,7 +171,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result, return patchlen; } -static void generate_id_list(int stable) +static void generate_id_list(int stable, int verbatim) { struct object_id oid, n, result; int patchlen; @@ -142,21 +179,32 @@ static void generate_id_list(int stable) oidclr(&oid); while (!feof(stdin)) { - patchlen = get_one_patchid(&n, &result, &line_buf, stable); + patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim); flush_current_id(patchlen, &oid, &result); oidcpy(&oid, &n); } strbuf_release(&line_buf); } -static const char patch_id_usage[] = "git patch-id [--stable | --unstable]"; +static const char *const patch_id_usage[] = { + N_("git patch-id [--stable | --unstable | --verbatim]"), NULL +}; + +struct patch_id_opts { + int stable; + int verbatim; +}; static int git_patch_id_config(const char *var, const char *value, void *cb) { - int *stable = cb; + struct patch_id_opts *opts = cb; if (!strcmp(var, "patchid.stable")) { - *stable = git_config_bool(var, value); + opts->stable = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "patchid.verbatim")) { + opts->verbatim = git_config_bool(var, value); return 0; } @@ -165,21 +213,29 @@ static int git_patch_id_config(const char *var, const char *value, void *cb) int cmd_patch_id(int argc, const char **argv, const char *prefix) { - int stable = -1; - - git_config(git_patch_id_config, &stable); - - /* If nothing is set, default to unstable. */ - if (stable < 0) - stable = 0; - - if (argc == 2 && !strcmp(argv[1], "--stable")) - stable = 1; - else if (argc == 2 && !strcmp(argv[1], "--unstable")) - stable = 0; - else if (argc != 1) - usage(patch_id_usage); - - generate_id_list(stable); + /* if nothing is set, default to unstable */ + struct patch_id_opts config = {0, 0}; + int opts = 0; + struct option builtin_patch_id_options[] = { + OPT_CMDMODE(0, "unstable", &opts, + N_("use the unstable patch-id algorithm"), 1), + OPT_CMDMODE(0, "stable", &opts, + N_("use the stable patch-id algorithm"), 2), + OPT_CMDMODE(0, "verbatim", &opts, + N_("don't strip whitespace from the patch"), 3), + OPT_END() + }; + + git_config(git_patch_id_config, &config); + + /* verbatim implies stable */ + if (config.verbatim) + config.stable = 1; + + 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); return 0; } diff --git a/builtin/prune.c b/builtin/prune.c index df376b2ed1..119a253a2a 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -1,12 +1,14 @@ #include "cache.h" #include "commit.h" #include "diff.h" +#include "hex.h" #include "revision.h" #include "builtin.h" #include "reachable.h" #include "parse-options.h" #include "progress.h" #include "prune-packed.h" +#include "replace-object.h" #include "object-store.h" #include "shallow.h" @@ -98,7 +100,8 @@ static int prune_object(const struct object_id *oid, const char *fullpath, return 0; } -static int prune_cruft(const char *basename, const char *path, void *data) +static int prune_cruft(const char *basename, const char *path, + void *data UNUSED) { if (starts_with(basename, "tmp_obj_")) prune_tmp_file(path); @@ -107,7 +110,8 @@ static int prune_cruft(const char *basename, const char *path, void *data) return 0; } -static int prune_subdir(unsigned int nr, const char *path, void *data) +static int prune_subdir(unsigned int nr UNUSED, const char *path, + void *data UNUSED) { if (!show_only) rmdir(path); @@ -127,7 +131,9 @@ static void remove_temporary_files(const char *path) dir = opendir(path); if (!dir) { - fprintf(stderr, "Unable to open directory %s\n", path); + if (errno != ENOENT) + fprintf(stderr, "Unable to open directory %s: %s\n", + path, strerror(errno)); return; } while ((de = readdir(dir)) != NULL) diff --git a/builtin/pull.c b/builtin/pull.c index 01155ba67b..56f679d94a 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -5,10 +5,11 @@ * * Fetch one or more remote refs and merge it/them into the current HEAD. */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" #include "builtin.h" +#include "hex.h" #include "parse-options.h" #include "exec-cmd.h" #include "run-command.h" @@ -359,8 +360,6 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified) */ static int git_pull_config(const char *var, const char *value, void *cb) { - int status; - if (!strcmp(var, "rebase.autostash")) { config_autostash = git_config_bool(var, value); return 0; @@ -372,10 +371,6 @@ static int git_pull_config(const char *var, const char *value, void *cb) check_trust_level = 0; } - status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); } @@ -515,76 +510,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo, */ static int run_fetch(const char *repo, const char **refspecs) { - struct strvec args = STRVEC_INIT; - int ret; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&args, "fetch", "--update-head-ok", NULL); + strvec_pushl(&cmd.args, "fetch", "--update-head-ok", NULL); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); if (opt_progress) - strvec_push(&args, opt_progress); + strvec_push(&cmd.args, opt_progress); /* Options passed to git-fetch */ if (opt_all) - strvec_push(&args, opt_all); + strvec_push(&cmd.args, opt_all); if (opt_append) - strvec_push(&args, opt_append); + strvec_push(&cmd.args, opt_append); if (opt_upload_pack) - strvec_push(&args, opt_upload_pack); - argv_push_force(&args); + strvec_push(&cmd.args, opt_upload_pack); + argv_push_force(&cmd.args); if (opt_tags) - strvec_push(&args, opt_tags); + strvec_push(&cmd.args, opt_tags); if (opt_prune) - strvec_push(&args, opt_prune); + strvec_push(&cmd.args, opt_prune); if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT) switch (recurse_submodules_cli) { case RECURSE_SUBMODULES_ON: - strvec_push(&args, "--recurse-submodules=on"); + strvec_push(&cmd.args, "--recurse-submodules=on"); break; case RECURSE_SUBMODULES_OFF: - strvec_push(&args, "--recurse-submodules=no"); + strvec_push(&cmd.args, "--recurse-submodules=no"); break; case RECURSE_SUBMODULES_ON_DEMAND: - strvec_push(&args, "--recurse-submodules=on-demand"); + strvec_push(&cmd.args, "--recurse-submodules=on-demand"); break; default: BUG("submodule recursion option not understood"); } if (max_children) - strvec_push(&args, max_children); + strvec_push(&cmd.args, max_children); if (opt_dry_run) - strvec_push(&args, "--dry-run"); + strvec_push(&cmd.args, "--dry-run"); if (opt_keep) - strvec_push(&args, opt_keep); + strvec_push(&cmd.args, opt_keep); if (opt_depth) - strvec_push(&args, opt_depth); + strvec_push(&cmd.args, opt_depth); if (opt_unshallow) - strvec_push(&args, opt_unshallow); + strvec_push(&cmd.args, opt_unshallow); if (opt_update_shallow) - strvec_push(&args, opt_update_shallow); + strvec_push(&cmd.args, opt_update_shallow); if (opt_refmap) - strvec_push(&args, opt_refmap); + strvec_push(&cmd.args, opt_refmap); if (opt_ipv4) - strvec_push(&args, opt_ipv4); + strvec_push(&cmd.args, opt_ipv4); if (opt_ipv6) - strvec_push(&args, opt_ipv6); + strvec_push(&cmd.args, opt_ipv6); if (opt_show_forced_updates > 0) - strvec_push(&args, "--show-forced-updates"); + strvec_push(&cmd.args, "--show-forced-updates"); else if (opt_show_forced_updates == 0) - strvec_push(&args, "--no-show-forced-updates"); + strvec_push(&cmd.args, "--no-show-forced-updates"); if (set_upstream) - strvec_push(&args, set_upstream); - strvec_pushv(&args, opt_fetch.v); + strvec_push(&cmd.args, set_upstream); + strvec_pushv(&cmd.args, opt_fetch.v); if (repo) { - strvec_push(&args, repo); - strvec_pushv(&args, refspecs); + strvec_push(&cmd.args, repo); + strvec_pushv(&cmd.args, refspecs); } else if (*refspecs) BUG("refspecs without repo?"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE); - strvec_clear(&args); - return ret; + cmd.git_cmd = 1; + cmd.close_object_store = 1; + return run_command(&cmd); } /** @@ -653,52 +647,50 @@ static int update_submodules(void) */ static int run_merge(void) { - int ret; - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&args, "merge", NULL); + strvec_pushl(&cmd.args, "merge", NULL); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); if (opt_progress) - strvec_push(&args, opt_progress); + strvec_push(&cmd.args, opt_progress); /* Options passed to git-merge */ if (opt_diffstat) - strvec_push(&args, opt_diffstat); + strvec_push(&cmd.args, opt_diffstat); if (opt_log) - strvec_push(&args, opt_log); + strvec_push(&cmd.args, opt_log); if (opt_signoff) - strvec_push(&args, opt_signoff); + strvec_push(&cmd.args, opt_signoff); if (opt_squash) - strvec_push(&args, opt_squash); + strvec_push(&cmd.args, opt_squash); if (opt_commit) - strvec_push(&args, opt_commit); + strvec_push(&cmd.args, opt_commit); if (opt_edit) - strvec_push(&args, opt_edit); + strvec_push(&cmd.args, opt_edit); if (cleanup_arg) - strvec_pushf(&args, "--cleanup=%s", cleanup_arg); + strvec_pushf(&cmd.args, "--cleanup=%s", cleanup_arg); if (opt_ff) - strvec_push(&args, opt_ff); + strvec_push(&cmd.args, opt_ff); if (opt_verify) - strvec_push(&args, opt_verify); + strvec_push(&cmd.args, opt_verify); if (opt_verify_signatures) - strvec_push(&args, opt_verify_signatures); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); + strvec_push(&cmd.args, opt_verify_signatures); + strvec_pushv(&cmd.args, opt_strategies.v); + strvec_pushv(&cmd.args, opt_strategy_opts.v); if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); + strvec_push(&cmd.args, opt_gpg_sign); if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); + strvec_push(&cmd.args, "--no-autostash"); else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); + strvec_push(&cmd.args, "--autostash"); if (opt_allow_unrelated_histories > 0) - strvec_push(&args, "--allow-unrelated-histories"); + strvec_push(&cmd.args, "--allow-unrelated-histories"); - strvec_push(&args, "FETCH_HEAD"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; + strvec_push(&cmd.args, "FETCH_HEAD"); + cmd.git_cmd = 1; + return run_command(&cmd); } /** @@ -879,43 +871,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase, static int run_rebase(const struct object_id *newbase, const struct object_id *upstream) { - int ret; - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_push(&args, "rebase"); + strvec_push(&cmd.args, "rebase"); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); /* Options passed to git-rebase */ if (opt_rebase == REBASE_MERGES) - strvec_push(&args, "--rebase-merges"); + strvec_push(&cmd.args, "--rebase-merges"); else if (opt_rebase == REBASE_INTERACTIVE) - strvec_push(&args, "--interactive"); + strvec_push(&cmd.args, "--interactive"); if (opt_diffstat) - strvec_push(&args, opt_diffstat); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); + strvec_push(&cmd.args, opt_diffstat); + strvec_pushv(&cmd.args, opt_strategies.v); + strvec_pushv(&cmd.args, opt_strategy_opts.v); if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); + strvec_push(&cmd.args, opt_gpg_sign); if (opt_signoff) - strvec_push(&args, opt_signoff); + strvec_push(&cmd.args, opt_signoff); if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); + strvec_push(&cmd.args, "--no-autostash"); else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); + strvec_push(&cmd.args, "--autostash"); if (opt_verify_signatures && !strcmp(opt_verify_signatures, "--verify-signatures")) warning(_("ignoring --verify-signatures for rebase")); - strvec_push(&args, "--onto"); - strvec_push(&args, oid_to_hex(newbase)); + strvec_push(&cmd.args, "--onto"); + strvec_push(&cmd.args, oid_to_hex(newbase)); - strvec_push(&args, oid_to_hex(upstream)); + strvec_push(&cmd.args, oid_to_hex(upstream)); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; + cmd.git_cmd = 1; + return run_command(&cmd); } static int get_can_ff(struct object_id *orig_head, @@ -990,6 +980,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) int rebase_unspecified = 0; int can_ff; int divergent; + int ret; if (!getenv("GIT_REFLOG_ACTION")) set_reflog_message(argc, argv); @@ -1034,7 +1025,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_rebase < 0) opt_rebase = config_get_rebase(&rebase_unspecified); - if (read_cache_unmerged()) + if (repo_read_index_unmerged(the_repository)) die_resolve_conflict("pull"); if (file_exists(git_path_merge_head(the_repository))) @@ -1047,7 +1038,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_autostash == -1) opt_autostash = config_autostash; - if (is_null_oid(&orig_head) && !is_cache_unborn()) + if (is_null_oid(&orig_head) && !is_index_unborn(&the_index)) die(_("Updating an unborn branch with changes added to the index.")); if (!opt_autostash) @@ -1100,7 +1091,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (is_null_oid(&orig_head)) { if (merge_heads.nr > 1) die(_("Cannot merge multiple branches into empty head.")); - return pull_into_void(merge_heads.oid, &curr_head); + ret = pull_into_void(merge_heads.oid, &curr_head); + goto cleanup; } if (merge_heads.nr > 1) { if (opt_rebase) @@ -1125,8 +1117,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix) } if (opt_rebase) { - int ret = 0; - struct object_id newbase; struct object_id upstream; get_rebase_newbase_and_upstream(&newbase, &upstream, &curr_head, @@ -1149,12 +1139,16 @@ int cmd_pull(int argc, const char **argv, const char *prefix) recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)) ret = rebase_submodules(); - return ret; + goto cleanup; } else { - int ret = run_merge(); + ret = run_merge(); if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON || recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)) ret = update_submodules(); - return ret; + goto cleanup; } + +cleanup: + oid_array_clear(&merge_heads); + return ret; } diff --git a/builtin/push.c b/builtin/push.c index 86b44f8aa7..12a402aea3 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -63,16 +63,9 @@ static struct refspec rs = REFSPEC_INIT_PUSH; static struct string_list push_options_config = STRING_LIST_INIT_DUP; static void refspec_append_mapped(struct refspec *refspec, const char *ref, - struct remote *remote, struct ref *local_refs) + struct remote *remote, struct ref *matched) { const char *branch_name; - struct ref *matched = NULL; - - /* Does "ref" uniquely name our ref? */ - if (count_refspec_match(ref, local_refs, &matched) != 1) { - refspec_append(refspec, ref); - return; - } if (remote->push.nr) { struct refspec_item query; @@ -120,15 +113,28 @@ static void set_refspecs(const char **refs, int nr, const char *repo) die(_("--delete only accepts plain target ref names")); refspec_appendf(&rs, ":%s", ref); } else if (!strchr(ref, ':')) { - if (!remote) { - /* lazily grab remote and local_refs */ - remote = remote_get(repo); + struct ref *matched = NULL; + + /* lazily grab local_refs */ + if (!local_refs) local_refs = get_local_heads(); + + /* Does "ref" uniquely name our ref? */ + 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); + + refspec_append_mapped(&rs, ref, remote, matched); } - refspec_append_mapped(&rs, ref, remote, local_refs); } else refspec_append(&rs, ref); } + free_refs(local_refs); } static int push_url_of_remote(struct remote *remote, const char ***url_p) @@ -169,9 +175,9 @@ static NORETURN void die_push_simple(struct branch *branch, if (git_branch_track != BRANCH_TRACK_SIMPLE) advice_automergesimple_maybe = _("\n" "To avoid automatically configuring " - "upstream branches when their name\n" - "doesn't match the local branch, see option " - "'simple' of branch.autosetupmerge\n" + "an upstream branch when its name\n" + "won't match the local branch, see option " + "'simple' of branch.autoSetupMerge\n" "in 'git help config'.\n"); die(_("The upstream branch of your current branch does not match\n" "the name of your current branch. To push to the upstream branch\n" @@ -466,8 +472,16 @@ static int option_parse_recurse_submodules(const struct option *opt, if (unset) *recurse_submodules = RECURSE_SUBMODULES_OFF; - else - *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg); + else { + if (!strcmp(arg, "only-is-on-demand")) { + if (*recurse_submodules == RECURSE_SUBMODULES_ONLY) { + warning(_("recursing into submodule with push.recurseSubmodules=only; using on-demand instead")); + *recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; + } + } else { + *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg); + } + } return 0; } @@ -494,11 +508,6 @@ static int git_push_config(const char *k, const char *v, void *cb) { const char *slot_name; int *flags = cb; - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; if (!strcmp(k, "push.followtags")) { if (git_config_bool(k, v)) diff --git a/builtin/range-diff.c b/builtin/range-diff.c index 50318849d6..aecfae12d3 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -38,16 +38,18 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) OPT_END() }; struct option *options; - int res = 0; + int i, dash_dash = -1, res = 0; struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; + struct object_id oid; + const char *three_dots = NULL; git_config(git_diff_ui_config, NULL); repo_diff_setup(the_repository, &diffopt); - options = parse_options_concat(range_diff_options, diffopt.parseopts); + options = add_diff_options(range_diff_options, &diffopt); argc = parse_options(argc, argv, prefix, options, - builtin_range_diff_usage, 0); + builtin_range_diff_usage, PARSE_OPT_KEEP_DASHDASH); diff_setup_done(&diffopt); @@ -55,40 +57,91 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) if (!simple_color) diffopt.use_color = 1; - if (argc == 2) { - if (!is_range_diff_range(argv[0])) - die(_("not a commit range: '%s'"), argv[0]); - strbuf_addstr(&range1, argv[0]); + for (i = 0; i < argc; i++) + if (!strcmp(argv[i], "--")) { + dash_dash = i; + break; + } + + if (dash_dash == 3 || + (dash_dash < 0 && argc > 2 && + !get_oid_committish(argv[0], &oid) && + !get_oid_committish(argv[1], &oid) && + !get_oid_committish(argv[2], &oid))) { + if (dash_dash < 0) + ; /* already validated arguments */ + else if (get_oid_committish(argv[0], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[0]); + else if (get_oid_committish(argv[1], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[1]); + else if (get_oid_committish(argv[2], &oid)) + usage_msg_optf(_("not a revision: '%s'"), + builtin_range_diff_usage, options, + argv[2]); - if (!is_range_diff_range(argv[1])) - die(_("not a commit range: '%s'"), argv[1]); - strbuf_addstr(&range2, argv[1]); - } else if (argc == 3) { strbuf_addf(&range1, "%s..%s", argv[0], argv[1]); strbuf_addf(&range2, "%s..%s", argv[0], argv[2]); - } else if (argc == 1) { - const char *b = strstr(argv[0], "..."), *a = argv[0]; + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 3 : dash_dash)); + } else if (dash_dash == 2 || + (dash_dash < 0 && argc > 1 && + is_range_diff_range(argv[0]) && + is_range_diff_range(argv[1]))) { + if (dash_dash < 0) + ; /* already validated arguments */ + else if (!is_range_diff_range(argv[0])) + usage_msg_optf(_("not a commit range: '%s'"), + builtin_range_diff_usage, options, + argv[0]); + else if (!is_range_diff_range(argv[1])) + usage_msg_optf(_("not a commit range: '%s'"), + builtin_range_diff_usage, options, + argv[1]); + + strbuf_addstr(&range1, argv[0]); + strbuf_addstr(&range2, argv[1]); + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 2 : dash_dash)); + } else if (dash_dash == 1 || + (dash_dash < 0 && argc > 0 && + (three_dots = strstr(argv[0], "...")))) { + const char *a, *b; int a_len; - if (!b) { - error(_("single arg format must be symmetric range")); - usage_with_options(builtin_range_diff_usage, options); - } + if (dash_dash < 0) + ; /* already validated arguments */ + else if (!(three_dots = strstr(argv[0], "..."))) + usage_msg_optf(_("not a symmetric range: '%s'"), + builtin_range_diff_usage, options, + argv[0]); - a_len = (int)(b - a); - if (!a_len) { + if (three_dots == argv[0]) { a = "HEAD"; a_len = strlen(a); + } else { + a = argv[0]; + a_len = (int)(three_dots - a); } - b += 3; - if (!*b) + + if (three_dots[3]) + b = three_dots + 3; + else b = "HEAD"; + strbuf_addf(&range1, "%s..%.*s", b, a_len, a); strbuf_addf(&range2, "%.*s..%s", a_len, a, b); - } else { - error(_("need two commit ranges")); - usage_with_options(builtin_range_diff_usage, options); - } + + strvec_pushv(&other_arg, argv + + (dash_dash < 0 ? 1 : dash_dash)); + } else + usage_msg_opt(_("need two commit ranges"), + builtin_range_diff_usage, options); FREE_AND_NULL(options); range_diff_opts.dual_color = simple_color < 1; diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 9f1f33e954..11759c415f 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -4,9 +4,10 @@ * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "object.h" #include "tree.h" @@ -38,7 +39,9 @@ static int list_tree(struct object_id *oid) } static const char * const read_tree_usage[] = { - N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"), + N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)\n" + " [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]\n" + " (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"), NULL }; @@ -85,9 +88,9 @@ static int debug_merge(const struct cache_entry * const *stages, { int i; - printf("* %d-way merge\n", o->merge_size); + printf("* %d-way merge\n", o->internal.merge_size); debug_stage("index", stages[0], o); - for (i = 1; i <= o->merge_size; i++) { + for (i = 1; i <= o->internal.merge_size; i++) { char buf[24]; xsnprintf(buf, sizeof(buf), "ent#%d", i); debug_stage(buf, stages[i], o); @@ -112,6 +115,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) int prefix_set = 0; struct lock_file lock_file = LOCK_INIT; const struct option read_tree_options[] = { + OPT__SUPER_PREFIX(&opts.super_prefix), OPT_CALLBACK_F(0, "index-output", NULL, N_("file"), N_("write resulting index to <file>"), PARSE_OPT_NONEG, index_output_cb), @@ -141,7 +145,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")), OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout, N_("skip applying sparse checkout filter")), - OPT_BOOL(0, "debug-unpack", &opts.debug_unpack, + OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack, N_("debug unpack-trees")), OPT_CALLBACK_F(0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", @@ -174,7 +178,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); /* * NEEDSWORK @@ -186,11 +190,11 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) */ if (opts.reset || opts.merge || opts.prefix) { - if (read_cache_unmerged() && (opts.prefix || opts.merge)) + if (repo_read_index_unmerged(the_repository) && (opts.prefix || opts.merge)) die(_("You need to resolve your current index first")); stage = opts.merge = 1; } - resolve_undo_clear(); + resolve_undo_clear_index(&the_index); for (i = 0; i < argc; i++) { const char *arg = argv[i]; @@ -230,7 +234,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_cache_unborn(); + opts.initial_checkout = is_index_unborn(&the_index); break; case 3: default: @@ -244,10 +248,14 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) opts.head_idx = 1; } - if (opts.debug_unpack) + if (opts.internal.debug_unpack) opts.fn = debug_merge; - cache_tree_free(&active_cache_tree); + /* If we're going to prime_cache_tree later, skip cache tree update */ + if (nr_trees == 1 && !opts.prefix) + opts.skip_cache_tree_update = 1; + + cache_tree_free(&the_index.cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; parse_tree(tree); @@ -256,7 +264,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) if (unpack_trees(nr_trees, t, &opts)) return 128; - if (opts.debug_unpack || opts.dry_run) + if (opts.internal.debug_unpack || opts.dry_run) return 0; /* do not write the index out */ /* diff --git a/builtin/rebase.c b/builtin/rebase.c index 7ab50cda2a..5b7b908b66 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -4,8 +4,9 @@ * Copyright (c) 2018 Pratik Karki */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "hex.h" #include "run-command.h" #include "exec-cmd.h" #include "strvec.h" @@ -30,8 +31,6 @@ #include "reset.h" #include "hook.h" -#define DEFAULT_REFLOG_ACTION "rebase" - static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] " "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"), @@ -59,6 +58,26 @@ enum empty_type { EMPTY_ASK }; +enum action { + ACTION_NONE = 0, + ACTION_CONTINUE, + ACTION_SKIP, + ACTION_ABORT, + ACTION_QUIT, + ACTION_EDIT_TODO, + ACTION_SHOW_CURRENT_PATCH +}; + +static const char *action_names[] = { + "undefined", + "continue", + "skip", + "abort", + "quit", + "edit_todo", + "show_current_patch" +}; + struct rebase_options { enum rebase_type type; enum empty_type empty; @@ -68,7 +87,7 @@ struct rebase_options { const char *upstream_name; const char *upstream_arg; char *head_name; - struct object_id orig_head; + struct commit *orig_head; struct commit *onto; const char *onto_name; const char *revisions; @@ -85,7 +104,8 @@ struct rebase_options { REBASE_INTERACTIVE_EXPLICIT = 1<<4, } flags; struct strvec git_am_opts; - const char *action; + enum action action; + char *reflog_action; int signoff; int allow_rerere_autoupdate; int keep_empty; @@ -94,7 +114,7 @@ struct rebase_options { int autostash; int committer_date_is_author_date; int ignore_date; - char *cmd; + struct string_list exec; int allow_empty_message; int rebase_merges, rebase_cousins; char *strategy, *strategy_opts; @@ -102,6 +122,9 @@ struct rebase_options { int reschedule_failed_exec; int reapply_cherry_picks; int fork_point; + int update_refs; + int config_autosquash; + int config_update_refs; }; #define REBASE_OPTIONS_INIT { \ @@ -111,8 +134,15 @@ struct rebase_options { .default_backend = "merge", \ .flags = REBASE_NO_QUIET, \ .git_am_opts = STRVEC_INIT, \ + .exec = STRING_LIST_INIT_NODUP, \ .git_format_patch_opt = STRBUF_INIT, \ .fork_point = -1, \ + .reapply_cherry_picks = -1, \ + .allow_empty_message = 1, \ + .autosquash = -1, \ + .config_autosquash = -1, \ + .update_refs = -1, \ + .config_update_refs = -1, \ } static struct replay_opts get_replay_opts(const struct rebase_options *opts) @@ -138,6 +168,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) opts->committer_date_is_author_date; replay.ignore_date = opts->ignore_date; replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); + replay.reflog_action = xstrdup(opts->reflog_action); if (opts->strategy) replay.strategy = xstrdup_or_null(opts->strategy); else if (!replay.strategy && replay.default_strategy) { @@ -156,24 +187,6 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) return replay; } -enum action { - ACTION_NONE = 0, - ACTION_CONTINUE, - ACTION_SKIP, - ACTION_ABORT, - ACTION_QUIT, - ACTION_EDIT_TODO, - ACTION_SHOW_CURRENT_PATCH -}; - -static const char *action_names[] = { "undefined", - "continue", - "skip", - "abort", - "quit", - "edit_todo", - "show_current_patch" }; - static int edit_todo_file(unsigned flags) { const char *todo_file = rebase_path_todo(); @@ -240,38 +253,22 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name, return write_basic_state(opts, head_name, onto, orig_head); } -static void split_exec_commands(const char *cmd, struct string_list *commands) -{ - if (cmd && *cmd) { - string_list_split(commands, cmd, '\n', -1); - - /* rebase.c adds a new line to cmd after every command, - * so here the last command is always empty */ - string_list_remove_empty_items(commands, 0); - } -} - static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) { - int ret; + int ret = -1; char *revisions = NULL, *shortrevisions = NULL; struct strvec make_script_args = STRVEC_INIT; struct todo_list todo_list = TODO_LIST_INIT; struct replay_opts replay = get_replay_opts(opts); - struct string_list commands = STRING_LIST_INIT_DUP; - if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head, + if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid, &revisions, &shortrevisions)) - return -1; + goto cleanup; if (init_basic_state(&replay, opts->head_name ? opts->head_name : "detached HEAD", - opts->onto, &opts->orig_head)) { - free(revisions); - free(shortrevisions); - - return -1; - } + opts->onto, &opts->orig_head->object.oid)) + goto cleanup; if (!opts->upstream && opts->squash_onto) write_file(path_squash_onto(), "%s\n", @@ -289,19 +286,19 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) if (ret) error(_("could not generate todo list")); else { - discard_cache(); + discard_index(&the_index); if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, &todo_list)) BUG("unusable todo list"); - split_exec_commands(opts->cmd, &commands); ret = complete_action(the_repository, &replay, flags, shortrevisions, opts->onto_name, opts->onto, - &opts->orig_head, &commands, opts->autosquash, - &todo_list); + &opts->orig_head->object.oid, &opts->exec, + opts->autosquash, opts->update_refs, &todo_list); } - string_list_clear(&commands, 0); +cleanup: + replay_opts_release(&replay); free(revisions); free(shortrevisions); todo_list_release(&todo_list); @@ -310,8 +307,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) return ret; } -static int run_sequencer_rebase(struct rebase_options *opts, - enum action command) +static int run_sequencer_rebase(struct rebase_options *opts) { unsigned flags = 0; int abbreviate_commands = 0, ret = 0; @@ -326,7 +322,7 @@ static int run_sequencer_rebase(struct rebase_options *opts, flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0; flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0; - switch (command) { + switch (opts->action) { case ACTION_NONE: { if (!opts->onto && !opts->upstream) die(_("a base commit must be provided with --upstream or --onto")); @@ -344,6 +340,7 @@ static int run_sequencer_rebase(struct rebase_options *opts, struct replay_opts replay_opts = get_replay_opts(opts); ret = sequencer_continue(the_repository, &replay_opts); + replay_opts_release(&replay_opts); break; } case ACTION_EDIT_TODO: @@ -359,7 +356,7 @@ static int run_sequencer_rebase(struct rebase_options *opts, break; } default: - BUG("invalid command '%d'", command); + BUG("invalid command '%d'", opts->action); } return ret; @@ -429,9 +426,9 @@ static int read_basic_state(struct rebase_options *opts) opts->head_name = starts_with(head_name.buf, "refs/") ? xstrdup(head_name.buf) : NULL; strbuf_release(&head_name); - if (get_oid(buf.buf, &oid)) - return error(_("could not get 'onto': '%s'"), buf.buf); - opts->onto = lookup_commit_or_die(&oid, buf.buf); + if (get_oid_hex(buf.buf, &oid) || + !(opts->onto = lookup_commit_object(the_repository, &oid))) + return error(_("invalid onto: '%s'"), buf.buf); /* * We always write to orig-head, but interactive rebase used to write to @@ -446,7 +443,8 @@ static int read_basic_state(struct rebase_options *opts) } else if (!read_oneliner(&buf, state_dir_path("head", opts), READ_ONELINER_WARN_MISSING)) return -1; - if (get_oid(buf.buf, &opts->orig_head)) + if (get_oid_hex(buf.buf, &oid) || + !(opts->orig_head = lookup_commit_object(the_repository, &oid))) return error(_("invalid orig-head: '%s'"), buf.buf); if (file_exists(state_dir_path("quiet", opts))) @@ -515,7 +513,7 @@ static int rebase_write_basic_state(struct rebase_options *opts) write_file(state_dir_path("onto", opts), "%s", opts->onto ? oid_to_hex(&opts->onto->object.oid) : ""); write_file(state_dir_path("orig-head", opts), "%s", - oid_to_hex(&opts->orig_head)); + oid_to_hex(&opts->orig_head->object.oid)); if (!(opts->flags & REBASE_NO_QUIET)) write_file(state_dir_path("quiet", opts), "%s", ""); if (opts->flags & REBASE_VERBOSE) @@ -558,6 +556,7 @@ static int finish_rebase(struct rebase_options *opts) replay.action = REPLAY_INTERACTIVE_REBASE; ret = sequencer_remove_state(&replay); + replay_opts_release(&replay); } else { strbuf_addstr(&dir, opts->state_dir); if (remove_dir_recursively(&dir, 0)) @@ -581,10 +580,11 @@ static int move_to_original_branch(struct rebase_options *opts) if (!opts->onto) BUG("move_to_original_branch without onto"); - strbuf_addf(&branch_reflog, "rebase finished: %s onto %s", + strbuf_addf(&branch_reflog, "%s (finish): %s onto %s", + opts->reflog_action, opts->head_name, oid_to_hex(&opts->onto->object.oid)); - strbuf_addf(&head_reflog, "rebase finished: returning to %s", - opts->head_name); + strbuf_addf(&head_reflog, "%s (finish): returning to %s", + opts->reflog_action, opts->head_name); ropts.branch = opts->head_name; ropts.flags = RESET_HEAD_REFS_ONLY; ropts.branch_msg = branch_reflog.buf; @@ -613,8 +613,9 @@ static int run_am(struct rebase_options *opts) am.git_cmd = 1; strvec_push(&am.args, "am"); - - if (opts->action && !strcmp("continue", opts->action)) { + strvec_pushf(&am.env, GIT_REFLOG_ACTION_ENVIRONMENT "=%s (pick)", + opts->reflog_action); + if (opts->action == ACTION_CONTINUE) { strvec_push(&am.args, "--resolved"); strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); if (opts->gpg_sign_opt) @@ -625,7 +626,7 @@ static int run_am(struct rebase_options *opts) return move_to_original_branch(opts); } - if (opts->action && !strcmp("skip", opts->action)) { + if (opts->action == ACTION_SKIP) { strvec_push(&am.args, "--skip"); strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); status = run_command(&am); @@ -634,7 +635,7 @@ static int run_am(struct rebase_options *opts) return move_to_original_branch(opts); } - if (opts->action && !strcmp("show-current-patch", opts->action)) { + if (opts->action == ACTION_SHOW_CURRENT_PATCH) { strvec_push(&am.args, "--show-current-patch"); return run_command(&am); } @@ -644,7 +645,7 @@ static int run_am(struct rebase_options *opts) /* this is now equivalent to !opts->upstream */ &opts->onto->object.oid : &opts->upstream->object.oid), - oid_to_hex(&opts->orig_head)); + oid_to_hex(&opts->orig_head->object.oid)); rebased_patches = xstrdup(git_path("rebased-patches")); format_patch.out = open(rebased_patches, @@ -660,7 +661,7 @@ static int run_am(struct rebase_options *opts) format_patch.git_cmd = 1; strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout", "--full-index", "--cherry-pick", "--right-only", - "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", + "--default-prefix", "--no-renames", "--no-cover-letter", "--pretty=mboxrd", "--topo-order", "--no-base", NULL); if (opts->git_format_patch_opt.len) @@ -678,9 +679,9 @@ static int run_am(struct rebase_options *opts) free(rebased_patches); strvec_clear(&am.args); - ropts.oid = &opts->orig_head; + ropts.oid = &opts->orig_head->object.oid; ropts.branch = opts->head_name; - ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; + ropts.default_reflog_action = opts->reflog_action; reset_head(the_repository, &ropts); error(_("\ngit encountered an error while preparing the " "patches to replay\n" @@ -727,7 +728,7 @@ static int run_am(struct rebase_options *opts) return status; } -static int run_specific_rebase(struct rebase_options *opts, enum action action) +static int run_specific_rebase(struct rebase_options *opts) { int status; @@ -745,7 +746,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) opts->gpg_sign_opt = tmp; } - status = run_sequencer_rebase(opts, action); + status = run_sequencer_rebase(opts); } else if (opts->type == REBASE_APPLY) status = run_am(opts); else @@ -784,7 +785,7 @@ static int rebase_config(const char *var, const char *value, void *data) } if (!strcmp(var, "rebase.autosquash")) { - opts->autosquash = git_config_bool(var, value); + opts->config_autosquash = git_config_bool(var, value); return 0; } @@ -800,6 +801,11 @@ static int rebase_config(const char *var, const char *value, void *data) return 0; } + if (!strcmp(var, "rebase.updaterefs")) { + opts->config_update_refs = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "rebase.reschedulefailedexec")) { opts->reschedule_failed_exec = git_config_bool(var, value); return 0; @@ -824,9 +830,8 @@ static int checkout_up_to_date(struct rebase_options *options) int ret = 0; strbuf_addf(&buf, "%s: checkout %s", - getenv(GIT_REFLOG_ACTION_ENVIRONMENT), - options->switch_to); - ropts.oid = &options->orig_head; + options->reflog_action, options->switch_to); + ropts.oid = &options->orig_head->object.oid; ropts.branch = options->head_name; ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK; if (!ropts.branch) @@ -859,32 +864,23 @@ static int is_linear_history(struct commit *from, struct commit *to) static int can_fast_forward(struct commit *onto, struct commit *upstream, struct commit *restrict_revision, - struct object_id *head_oid, struct object_id *merge_base) + struct commit *head, struct object_id *branch_base) { - struct commit *head = lookup_commit(the_repository, head_oid); struct commit_list *merge_bases = NULL; int res = 0; - if (!head) - goto done; + if (is_null_oid(branch_base)) + goto done; /* fill_branch_base() found multiple merge bases */ - merge_bases = get_merge_bases(onto, head); - if (!merge_bases || merge_bases->next) { - oidcpy(merge_base, null_oid()); - goto done; - } - - oidcpy(merge_base, &merge_bases->item->object.oid); - if (!oideq(merge_base, &onto->object.oid)) + if (!oideq(branch_base, &onto->object.oid)) goto done; - if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base)) + if (restrict_revision && !oideq(&restrict_revision->object.oid, branch_base)) goto done; if (!upstream) goto done; - free_commit_list(merge_bases); merge_bases = get_merge_bases(upstream, head); if (!merge_bases || merge_bases->next) goto done; @@ -899,6 +895,20 @@ done: return res && is_linear_history(onto, head); } +static void fill_branch_base(struct rebase_options *options, + struct object_id *branch_base) +{ + struct commit_list *merge_bases = NULL; + + merge_bases = get_merge_bases(options->onto, options->orig_head); + if (!merge_bases || merge_bases->next) + oidcpy(branch_base, null_oid()); + else + oidcpy(branch_base, &merge_bases->item->object.oid); + + free_commit_list(merge_bases); +} + static int parse_opt_am(const struct option *opt, const char *arg, int unset) { struct rebase_options *opts = opt->value; @@ -906,6 +916,9 @@ static int parse_opt_am(const struct option *opt, const char *arg, int unset) BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY) + die(_("apply options and merge options cannot be used together")); + opts->type = REBASE_APPLY; return 0; @@ -919,8 +932,10 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset) BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - if (!is_merge(opts)) - opts->type = REBASE_MERGE; + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE) + die(_("apply options and merge options cannot be used together")); + + opts->type = REBASE_MERGE; return 0; } @@ -934,6 +949,9 @@ static int parse_opt_interactive(const struct option *opt, const char *arg, BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE) + die(_("apply options and merge options cannot be used together")); + opts->type = REBASE_MERGE; opts->flags |= REBASE_INTERACTIVE_EXPLICIT; @@ -993,23 +1011,6 @@ static void NORETURN error_on_missing_default_upstream(void) exit(1); } -static void set_reflog_action(struct rebase_options *options) -{ - const char *env; - struct strbuf buf = STRBUF_INIT; - - if (!is_merge(options)) - return; - - env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - if (env && strcmp("rebase", env)) - return; /* only override it if it is "rebase" */ - - strbuf_addf(&buf, "rebase (%s)", options->action); - setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1); - strbuf_release(&buf); -} - static int check_exec_cmd(const char *cmd) { if (strchr(cmd, '\n')) @@ -1032,15 +1033,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; - struct object_id merge_base; + struct object_id branch_base; int ignore_whitespace = 0; - enum action action = ACTION_NONE; const char *gpg_sign = NULL; - struct string_list exec = STRING_LIST_INIT_NODUP; const char *rebase_merges = NULL; struct string_list strategy_options = STRING_LIST_INIT_NODUP; struct object_id squash_onto; char *squash_onto_name = NULL; + char *keep_base_onto_name = NULL; int reschedule_failed_exec = -1; int allow_preemptive_ff = 1; int preserve_merges_selected = 0; @@ -1083,18 +1083,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_BIT(0, "no-ff", &options.flags, N_("cherry-pick all commits, even if unchanged"), REBASE_FORCE), - OPT_CMDMODE(0, "continue", &action, N_("continue"), + OPT_CMDMODE(0, "continue", &options.action, N_("continue"), ACTION_CONTINUE), - OPT_CMDMODE(0, "skip", &action, + OPT_CMDMODE(0, "skip", &options.action, N_("skip current patch and continue"), ACTION_SKIP), - OPT_CMDMODE(0, "abort", &action, + OPT_CMDMODE(0, "abort", &options.action, N_("abort and check out the original branch"), ACTION_ABORT), - OPT_CMDMODE(0, "quit", &action, + OPT_CMDMODE(0, "quit", &options.action, N_("abort but keep HEAD where it is"), ACTION_QUIT), - OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list " + OPT_CMDMODE(0, "edit-todo", &options.action, N_("edit the todo list " "during an interactive rebase"), ACTION_EDIT_TODO), - OPT_CMDMODE(0, "show-current-patch", &action, + OPT_CMDMODE(0, "show-current-patch", &options.action, N_("show the patch file being applied or merged"), ACTION_SHOW_CURRENT_PATCH), OPT_CALLBACK_F(0, "apply", &options, NULL, @@ -1110,8 +1110,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_interactive), OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected, - N_("(DEPRECATED) try to recreate merges instead of " - "ignoring them"), + N_("(REMOVED) was: try to recreate merges " + "instead of ignoring them"), 1, PARSE_OPT_HIDDEN), OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}", @@ -1124,11 +1124,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "autosquash", &options.autosquash, N_("move commits that begin with " "squash!/fixup! under -i")), + OPT_BOOL(0, "update-refs", &options.update_refs, + N_("update branches that point to commits " + "that are being rebased")), { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_AUTOSTASH(&options.autostash), - OPT_STRING_LIST('x', "exec", &exec, N_("exec"), + OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"), N_("add exec lines after each commit of the " "editable list")), OPT_BOOL_F(0, "allow-empty-message", @@ -1165,7 +1168,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - options.allow_empty_message = 1; git_config(rebase_config, &options); /* options.gpg_sign_opt will be either "-S" or NULL */ gpg_sign = options.gpg_sign_opt ? "" : NULL; @@ -1182,8 +1184,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } else if (is_directory(merge_dir())) { strbuf_reset(&buf); strbuf_addf(&buf, "%s/rewritten", merge_dir()); - if (is_directory(buf.buf)) { - die("`rebase -p` is no longer supported"); + if (!(options.action == ACTION_ABORT) && is_directory(buf.buf)) { + die(_("`rebase --preserve-merges` (-p) is no longer supported.\n" + "Use `git rebase --abort` to terminate current rebase.\n" + "Or downgrade to v2.33, or earlier, to complete the rebase.")); } else { strbuf_reset(&buf); strbuf_addf(&buf, "%s/interactive", merge_dir()); @@ -1203,9 +1207,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) builtin_rebase_usage, 0); if (preserve_merges_selected) - die(_("--preserve-merges was replaced by --rebase-merges")); + die(_("--preserve-merges was replaced by --rebase-merges\n" + "Note: Your `pull.rebase` configuration may also be set to 'preserve',\n" + "which is no longer supported; use 'merges' instead")); - if (action != ACTION_NONE && total_argc != 2) { + if (options.action != ACTION_NONE && total_argc != 2) { usage_with_options(builtin_rebase_usage, builtin_rebase_options); } @@ -1219,42 +1225,47 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto"); if (options.root) die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root"); + /* + * --keep-base defaults to --no-fork-point to keep the + * base the same. + */ + if (options.fork_point < 0) + options.fork_point = 0; } - if (options.root && options.fork_point > 0) die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point"); - if (action != ACTION_NONE && !in_progress) + if (options.action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); - setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); - if (action == ACTION_EDIT_TODO && !is_merge(&options)) + if (options.action == ACTION_EDIT_TODO && !is_merge(&options)) die(_("The --edit-todo action can only be used during " "interactive rebase.")); if (trace2_is_enabled()) { if (is_merge(&options)) trace2_cmd_mode("interactive"); - else if (exec.nr) + else if (options.exec.nr) trace2_cmd_mode("interactive-exec"); else - trace2_cmd_mode(action_names[action]); + trace2_cmd_mode(action_names[options.action]); } - switch (action) { + options.reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); + options.reflog_action = + xstrdup(options.reflog_action ? options.reflog_action : "rebase"); + + switch (options.action) { case ACTION_CONTINUE: { struct object_id head; struct lock_file lock_file = LOCK_INIT; int fd; - options.action = "continue"; - set_reflog_action(&options); - /* Sanity check */ if (get_oid("HEAD", &head)) die(_("Cannot read HEAD")); - fd = hold_locked_index(&lock_file, 0); + fd = repo_hold_locked_index(the_repository, &lock_file, 0); if (repo_read_index(the_repository) < 0) die(_("could not read index")); refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, @@ -1275,9 +1286,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) case ACTION_SKIP: { struct string_list merge_rr = STRING_LIST_INIT_DUP; - options.action = "skip"; - set_reflog_action(&options); - rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); ropts.flags = RESET_HEAD_HARD; @@ -1290,21 +1298,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } case ACTION_ABORT: { struct string_list merge_rr = STRING_LIST_INIT_DUP; - options.action = "abort"; - set_reflog_action(&options); + struct strbuf head_msg = STRBUF_INIT; rerere_clear(the_repository, &merge_rr); string_list_clear(&merge_rr, 1); if (read_basic_state(&options)) exit(1); - ropts.oid = &options.orig_head; + + strbuf_addf(&head_msg, "%s (abort): returning to %s", + options.reflog_action, + options.head_name ? options.head_name + : oid_to_hex(&options.orig_head->object.oid)); + ropts.oid = &options.orig_head->object.oid; + ropts.head_msg = head_msg.buf; ropts.branch = options.head_name; ropts.flags = RESET_HEAD_HARD; - ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; if (reset_head(the_repository, &ropts) < 0) die(_("could not move back to %s"), - oid_to_hex(&options.orig_head)); + oid_to_hex(&options.orig_head->object.oid)); + strbuf_release(&head_msg); remove_branch_state(the_repository, 0); ret = finish_rebase(&options); goto cleanup; @@ -1316,6 +1329,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) replay.action = REPLAY_INTERACTIVE_REBASE; ret = sequencer_remove_state(&replay); + replay_opts_release(&replay); } else { strbuf_reset(&buf); strbuf_addstr(&buf, options.state_dir); @@ -1327,17 +1341,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } case ACTION_EDIT_TODO: - options.action = "edit-todo"; options.dont_finish_rebase = 1; goto run_rebase; case ACTION_SHOW_CURRENT_PATCH: - options.action = "show-current-patch"; options.dont_finish_rebase = 1; goto run_rebase; case ACTION_NONE: break; default: - BUG("action: %d", action); + BUG("action: %d", options.action); } /* Make sure no rebase is in progress */ @@ -1361,9 +1373,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || - (action != ACTION_NONE) || - (exec.nr > 0) || - options.autosquash) { + (options.action != ACTION_NONE) || + (options.exec.nr > 0) || + (options.autosquash == -1 && options.config_autosquash == 1) || + options.autosquash == 1) { allow_preemptive_ff = 0; } if (options.committer_date_is_author_date || options.ignore_date) @@ -1386,8 +1399,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } } - for (i = 0; i < exec.nr; i++) - if (check_exec_cmd(exec.items[i].string)) + for (i = 0; i < options.exec.nr; i++) + if (check_exec_cmd(options.exec.items[i].string)) exit(1); if (!(options.flags & REBASE_NO_QUIET)) @@ -1396,23 +1409,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.empty != EMPTY_UNSPECIFIED) imply_merge(&options, "--empty"); - if (options.reapply_cherry_picks) - imply_merge(&options, "--reapply-cherry-picks"); + if (options.reapply_cherry_picks < 0) + /* + * We default to --no-reapply-cherry-picks unless + * --keep-base is given; when --keep-base is given, we want + * to default to --reapply-cherry-picks. + */ + options.reapply_cherry_picks = keep_base; + else if (!keep_base) + /* + * The apply backend always searches for and drops cherry + * picks. This is often not wanted with --keep-base, so + * --keep-base allows --reapply-cherry-picks to be + * simulated by altering the upstream such that + * cherry-picks cannot be detected and thus all commits are + * reapplied. Thus, --[no-]reapply-cherry-picks is + * supported when --keep-base is specified, but not when + * --keep-base is left out. + */ + imply_merge(&options, options.reapply_cherry_picks ? + "--reapply-cherry-picks" : + "--no-reapply-cherry-picks"); if (gpg_sign) options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign); - if (exec.nr) { - int i; - + if (options.exec.nr) imply_merge(&options, "--exec"); - strbuf_reset(&buf); - for (i = 0; i < exec.nr; i++) - strbuf_addf(&buf, "exec %s\n", exec.items[i].string); - options.cmd = xstrdup(buf.buf); - } - if (rebase_merges) { if (!*rebase_merges) ; /* default mode; do nothing */ @@ -1485,15 +1509,29 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (strcmp(options.git_am_opts.v[i], "-q")) break; - if (i >= 0) { + if (i >= 0 || options.type == REBASE_APPLY) { if (is_merge(&options)) die(_("apply options and merge options " "cannot be used together")); + else if (options.autosquash == -1 && options.config_autosquash == 1) + die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash")); + else if (options.update_refs == -1 && options.config_update_refs == 1) + die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs")); else options.type = REBASE_APPLY; } } + if (options.update_refs == 1) + imply_merge(&options, "--update-refs"); + options.update_refs = (options.update_refs >= 0) ? options.update_refs : + ((options.config_update_refs >= 0) ? options.config_update_refs : 0); + + if (options.autosquash == 1) + imply_merge(&options, "--autosquash"); + options.autosquash = (options.autosquash >= 0) ? options.autosquash : + ((options.config_autosquash >= 0) ? options.config_autosquash : 0); + if (options.type == REBASE_UNSPECIFIED) { if (!strcmp(options.default_backend, "merge")) imply_merge(&options, "--merge"); @@ -1523,7 +1561,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.empty == EMPTY_UNSPECIFIED) { if (options.flags & REBASE_INTERACTIVE_EXPLICIT) options.empty = EMPTY_ASK; - else if (exec.nr > 0) + else if (options.exec.nr > 0) options.empty = EMPTY_KEEP; else options.empty = EMPTY_DROP; @@ -1590,25 +1628,27 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) */ if (argc == 1) { /* Is it "rebase other branchname" or "rebase other commit"? */ + struct object_id branch_oid; branch_name = argv[0]; options.switch_to = argv[0]; /* Is it a local branch? */ strbuf_reset(&buf); strbuf_addf(&buf, "refs/heads/%s", branch_name); - if (!read_ref(buf.buf, &options.orig_head)) { + if (!read_ref(buf.buf, &branch_oid)) { die_if_checked_out(buf.buf, 1); options.head_name = xstrdup(buf.buf); + options.orig_head = + lookup_commit_object(the_repository, + &branch_oid); /* If not is it a valid ref (branch or commit)? */ } else { - struct commit *commit = + options.orig_head = lookup_commit_reference_by_name(branch_name); - if (!commit) - die(_("no such branch/commit '%s'"), - branch_name); - oidcpy(&options.orig_head, &commit->object.oid); options.head_name = NULL; } + if (!options.orig_head) + die(_("no such branch/commit '%s'"), branch_name); } else if (argc == 0) { /* Do not need to switch branches, we are already on it. */ options.head_name = @@ -1625,8 +1665,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) FREE_AND_NULL(options.head_name); branch_name = "HEAD"; } - if (get_oid("HEAD", &options.orig_head)) - die(_("Could not resolve HEAD to a revision")); + options.orig_head = lookup_commit_reference_by_name("HEAD"); + if (!options.orig_head) + die(_("Could not resolve HEAD to a commit")); } else BUG("unexpected number of arguments left to parse"); @@ -1636,11 +1677,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) strbuf_addstr(&buf, options.upstream_name); strbuf_addstr(&buf, "..."); strbuf_addstr(&buf, branch_name); - options.onto_name = xstrdup(buf.buf); + options.onto_name = keep_base_onto_name = xstrdup(buf.buf); } else if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - if (get_oid_mb(options.onto_name, &merge_base) < 0) { + if (get_oid_mb(options.onto_name, &branch_base) < 0) { if (keep_base) die(_("'%s': need exactly one merge base with branch"), options.upstream_name); @@ -1648,7 +1689,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("'%s': need exactly one merge base"), options.onto_name); } - options.onto = lookup_commit_or_die(&merge_base, + options.onto = lookup_commit_or_die(&branch_base, options.onto_name); } else { options.onto = @@ -1656,15 +1697,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!options.onto) die(_("Does not point to a valid commit '%s'"), options.onto_name); + fill_branch_base(&options, &branch_base); } - if (options.fork_point > 0) { - struct commit *head = - lookup_commit_reference(the_repository, - &options.orig_head); + if (keep_base && options.reapply_cherry_picks) + options.upstream = options.onto; + + if (options.fork_point > 0) options.restrict_revision = - get_fork_point(options.upstream_name, head); - } + get_fork_point(options.upstream_name, options.orig_head); if (repo_read_index(the_repository) < 0) die(_("could not read index")); @@ -1689,13 +1730,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * Check if we are already based on onto with linear history, * in which case we could fast-forward without replacing the commits * with new commits recreated by replaying their changes. - * - * Note that can_fast_forward() initializes merge_base, so we have to - * call it before checking allow_preemptive_ff. */ - if (can_fast_forward(options.onto, options.upstream, options.restrict_revision, - &options.orig_head, &merge_base) && - allow_preemptive_ff) { + if (allow_preemptive_ff && + can_fast_forward(options.onto, options.upstream, options.restrict_revision, + options.orig_head, &branch_base)) { int flag; if (!(options.flags & REBASE_FORCE)) { @@ -1736,12 +1774,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct diff_options opts; if (options.flags & REBASE_VERBOSE) { - if (is_null_oid(&merge_base)) + if (is_null_oid(&branch_base)) printf(_("Changes to %s:\n"), oid_to_hex(&options.onto->object.oid)); else printf(_("Changes from %s to %s:\n"), - oid_to_hex(&merge_base), + oid_to_hex(&branch_base), oid_to_hex(&options.onto->object.oid)); } @@ -1753,8 +1791,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&opts); - diff_tree_oid(is_null_oid(&merge_base) ? - the_hash_algo->empty_tree : &merge_base, + diff_tree_oid(is_null_oid(&branch_base) ? + the_hash_algo->empty_tree : &branch_base, &options.onto->object.oid, "", &opts); diffcore_std(&opts); diff_flush(&opts); @@ -1768,14 +1806,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) printf(_("First, rewinding head to replay your work on top of " "it...\n")); - strbuf_addf(&msg, "%s: checkout %s", - getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name); + strbuf_addf(&msg, "%s (start): checkout %s", + options.reflog_action, options.onto_name); ropts.oid = &options.onto->object.oid; - ropts.orig_head = &options.orig_head, + ropts.orig_head = &options.orig_head->object.oid, ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD | RESET_HEAD_RUN_POST_CHECKOUT_HOOK; ropts.head_msg = msg.buf; - ropts.default_reflog_action = DEFAULT_REFLOG_ACTION; + ropts.default_reflog_action = options.reflog_action; if (reset_head(the_repository, &ropts)) die(_("Could not detach HEAD")); strbuf_release(&msg); @@ -1784,19 +1822,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * If the onto is a proper descendant of the tip of the branch, then * we just fast-forwarded. */ - strbuf_reset(&msg); - if (oideq(&merge_base, &options.orig_head)) { + if (oideq(&branch_base, &options.orig_head->object.oid)) { printf(_("Fast-forwarded %s to %s.\n"), branch_name, options.onto_name); - strbuf_addf(&msg, "rebase finished: %s onto %s", - options.head_name ? options.head_name : "detached HEAD", - oid_to_hex(&options.onto->object.oid)); - memset(&ropts, 0, sizeof(ropts)); - ropts.branch = options.head_name; - ropts.flags = RESET_HEAD_REFS_ONLY; - ropts.head_msg = msg.buf; - reset_head(the_repository, &ropts); - strbuf_release(&msg); + move_to_original_branch(&options); ret = finish_rebase(&options); goto cleanup; } @@ -1806,21 +1835,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) (options.restrict_revision ? oid_to_hex(&options.restrict_revision->object.oid) : oid_to_hex(&options.upstream->object.oid)), - oid_to_hex(&options.orig_head)); + oid_to_hex(&options.orig_head->object.oid)); options.revisions = revisions.buf; run_rebase: - ret = run_specific_rebase(&options, action); + ret = run_specific_rebase(&options); 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); - free(options.cmd); + string_list_clear(&options.exec, 0); free(options.strategy); + free(options.strategy_opts); strbuf_release(&options.git_format_patch_opt); free(squash_onto_name); + free(keep_base_onto_name); + string_list_clear(&strategy_options, 0); return !!ret; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 31b48e728b..6a24ab4f45 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "repository.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "pack.h" #include "refs.h" @@ -80,6 +81,7 @@ 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 struct string_list hidden_refs = STRING_LIST_INIT_DUP; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; static const char *NONCE_BAD = "BAD"; @@ -130,15 +132,11 @@ static enum deny_action parse_deny_action(const char *var, const char *value) static int receive_pack_config(const char *var, const char *value, void *cb) { - int status = parse_hide_refs_config(var, value, "receive"); + int status = parse_hide_refs_config(var, value, "receive", &hidden_refs); if (status) return status; - status = git_gpg_config(var, value, NULL); - if (status) - return status; - if (strcmp(var, "receive.denydeletes") == 0) { deny_deletes = git_config_bool(var, value); return 0; @@ -291,12 +289,12 @@ static void show_ref(const char *path, const struct object_id *oid) } static int show_ref_cb(const char *path_full, const struct object_id *oid, - int flag, void *data) + int flag UNUSED, void *data) { struct oidset *seen = data; const char *path = strip_namespace(path_full); - if (ref_is_hidden(path, path_full)) + if (ref_is_hidden(path, path_full, &hidden_refs)) return 0; /* @@ -465,7 +463,7 @@ static void rp_error(const char *err, ...) va_end(params); } -static int copy_to_sideband(int in, int out, void *arg) +static int copy_to_sideband(int in, int out UNUSED, void *arg UNUSED) { char data[128]; int keepalive_active = 0; @@ -1462,8 +1460,10 @@ static const char *update(struct command *cmd, struct shallow_info *si) find_shared_symref(worktrees, "HEAD", name); /* only refs/... are allowed */ - if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { - rp_error("refusing to create funny ref '%s' remotely", name); + if (!starts_with(name, "refs/") || + check_refname_format(name + 5, is_null_oid(new_oid) ? + REFNAME_ALLOW_ONELEVEL : 0)) { + rp_error("refusing to update funny ref '%s' remotely", name); ret = "funny refname"; goto out; } @@ -1794,7 +1794,7 @@ static void reject_updates_to_hidden(struct command *commands) strbuf_setlen(&refname_full, prefix_len); strbuf_addstr(&refname_full, cmd->ref_name); - if (!ref_is_hidden(cmd->ref_name, refname_full.buf)) + if (!ref_is_hidden(cmd->ref_name, refname_full.buf, &hidden_refs)) continue; if (is_null_oid(&cmd->new_oid)) cmd->error_string = "deny deleting a hidden ref"; @@ -1928,6 +1928,8 @@ static void execute_commands(struct command *commands, opt.err_fd = err_fd; opt.progress = err_fd && !quiet; opt.env = tmp_objdir_env(tmp_objdir); + opt.exclude_hidden_refs_section = "receive"; + if (check_connected(iterate_receive_command_list, &data, &opt)) set_connectivity_errors(commands, si); @@ -2029,6 +2031,16 @@ static struct command **queue_command(struct command **tail, return &cmd->next; } +static void free_commands(struct command *commands) +{ + while (commands) { + struct command *next = commands->next; + + free(commands); + commands = next; + } +} + static void queue_commands_from_cert(struct command **tail, struct strbuf *push_cert) { @@ -2566,6 +2578,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) run_receive_hook(commands, "post-receive", 1, &push_options); run_update_post_hook(commands); + free_commands(commands); string_list_clear(&push_options, 0); if (auto_gc) { struct child_process proc = CHILD_PROCESS_INIT; @@ -2591,6 +2604,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) packet_flush(1); oid_array_clear(&shallow); oid_array_clear(&ref); + string_list_clear(&hidden_refs, 0); free((void *)push_cert_nonce); return 0; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 4dd297dce8..270681dcdf 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -56,7 +56,8 @@ struct worktree_reflogs { struct string_list reflogs; }; -static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data) +static int collect_reflog(const char *ref, const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct worktree_reflogs *cb = cb_data; struct worktree *worktree = cb->worktree; @@ -66,7 +67,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus * Avoid collecting the same shared ref multiple times because * they are available via all worktrees. */ - if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL) + if (!worktree->is_current && + parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED) return 0; strbuf_worktree_ref(worktree, &newref, ref); @@ -193,6 +195,8 @@ static int expire_unreachable_callback(const struct option *opt, { struct cmd_reflog_expire_cb *cmd = opt->value; + BUG_ON_OPT_NEG(unset); + if (parse_expiry_date(arg, &cmd->expire_unreachable)) die(_("invalid timestamp '%s' given to '--%s'"), arg, opt->long_name); @@ -207,6 +211,8 @@ static int expire_total_callback(const struct option *opt, { struct cmd_reflog_expire_cb *cmd = opt->value; + BUG_ON_OPT_NEG(unset); + if (parse_expiry_date(arg, &cmd->expire_total)) die(_("invalid timestamp '%s' given to '--%s'"), arg, opt->long_name); @@ -223,7 +229,7 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix) parse_options(argc, argv, prefix, options, reflog_show_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); return cmd_log_reflog(argc, argv, prefix); } @@ -404,40 +410,21 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) int cmd_reflog(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), + OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), + OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), + OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), OPT_END() }; argc = parse_options(argc, argv, prefix, options, reflog_usage, + PARSE_OPT_SUBCOMMAND_OPTIONAL | PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN | - PARSE_OPT_NO_INTERNAL_HELP); - - /* - * With "git reflog" we default to showing it. !argc is - * impossible with PARSE_OPT_KEEP_ARGV0. - */ - if (argc == 1) - goto log_reflog; - - if (!strcmp(argv[1], "-h")) - usage_with_options(reflog_usage, options); - else if (*argv[1] == '-') - goto log_reflog; - - if (!strcmp(argv[1], "show")) - return cmd_reflog_show(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "expire")) - return cmd_reflog_expire(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "delete")) - return cmd_reflog_delete(argc - 1, argv + 1, prefix); - else if (!strcmp(argv[1], "exists")) - return cmd_reflog_exists(argc - 1, argv + 1, prefix); - - /* - * Fall-through for e.g. "git reflog -1", "git reflog master", - * as well as the plain "git reflog" above goto above. - */ -log_reflog: - return cmd_log_reflog(argc, argv, prefix); + PARSE_OPT_KEEP_UNKNOWN_OPT); + if (fn) + return fn(argc - 1, argv + 1, prefix); + else + return cmd_log_reflog(argc, argv, prefix); } diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index fd3538d4f0..ee338bf440 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -169,6 +169,8 @@ static int command_loop(const char *child) while (1) { size_t i; + const char *arg; + if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { if (ferror(stdin)) die("Command input error"); @@ -182,10 +184,10 @@ static int command_loop(const char *child) if (!strcmp(buffer, "capabilities")) { printf("*connect\n\n"); fflush(stdout); - } else if (!strncmp(buffer, "connect ", 8)) { + } else if (skip_prefix(buffer, "connect ", &arg)) { printf("\n"); fflush(stdout); - return run_child(child, buffer + 8); + return run_child(child, arg); } else { fprintf(stderr, "Bad command"); return 1; diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c index 91dfe07e06..b2a3980b1d 100644 --- a/builtin/remote-fd.c +++ b/builtin/remote-fd.c @@ -40,7 +40,7 @@ static void command_loop(int input_fd, int output_fd) if (!strcmp(buffer, "capabilities")) { printf("*connect\n\n"); fflush(stdout); - } else if (!strncmp(buffer, "connect ", 8)) { + } else if (starts_with(buffer, "connect ")) { printf("\n"); fflush(stdout); if (bidirectional_transfer_loop(input_fd, diff --git a/builtin/remote.c b/builtin/remote.c index d4b69fe778..729f6f3643 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -92,13 +92,15 @@ static int verbose; static int fetch_remote(const char *name) { - const char *argv[] = { "fetch", name, NULL, NULL }; - if (verbose) { - argv[1] = "-v"; - argv[2] = name; - } + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_push(&cmd.args, "fetch"); + if (verbose) + strvec_push(&cmd.args, "-v"); + strvec_push(&cmd.args, name); + cmd.git_cmd = 1; printf_ln(_("Updating %s"), name); - if (run_command_v_opt(argv, RUN_GIT_CMD)) + if (run_command(&cmd)) return error(_("Could not fetch %s"), name); return 0; } @@ -150,7 +152,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) return 0; } -static int add(int argc, const char **argv) +static int add(int argc, const char **argv, const char *prefix) { int fetch = 0, fetch_tags = TAGS_DEFAULT; unsigned mirror = MIRROR_NONE; @@ -177,8 +179,8 @@ static int add(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_add_usage, 0); if (argc != 2) usage_with_options(builtin_remote_add_usage, options); @@ -264,7 +266,8 @@ static const char *abbrev_ref(const char *name, const char *prefix) } #define abbrev_branch(name) abbrev_ref((name), "refs/heads/") -static int config_read_branches(const char *key, const char *value, void *cb) +static int config_read_branches(const char *key, const char *value, + void *data UNUSED) { const char *orig_key = key; char *name; @@ -344,12 +347,13 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct string_list new_refs, stale, tracked, heads, push; + struct string_list new_refs, skipped, stale, tracked, heads, push; int queried; }; #define REF_STATES_INIT { \ .new_refs = STRING_LIST_INIT_DUP, \ + .skipped = STRING_LIST_INIT_DUP, \ .stale = STRING_LIST_INIT_DUP, \ .tracked = STRING_LIST_INIT_DUP, \ .heads = STRING_LIST_INIT_DUP, \ @@ -368,7 +372,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat states->remote->fetch.raw[i]); for (ref = fetch_map; ref; ref = ref->next) { - if (!ref->peer_ref || !ref_exists(ref->peer_ref->name)) + 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)) string_list_append(&states->new_refs, abbrev_branch(ref->name)); else string_list_append(&states->tracked, abbrev_branch(ref->name)); @@ -383,6 +389,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat free_refs(fetch_map); string_list_sort(&states->new_refs); + string_list_sort(&states->skipped); string_list_sort(&states->tracked); string_list_sort(&states->stale); @@ -534,7 +541,8 @@ struct branches_for_remote { }; static int add_branch_for_removal(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct branches_for_remote *branches = cb_data; struct refspec_item refspec; @@ -576,7 +584,8 @@ struct rename_info { }; static int read_remote_branches(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags UNUSED, void *cb_data) { struct rename_info *rename = cb_data; struct strbuf buf = STRBUF_INIT; @@ -676,7 +685,7 @@ static void handle_push_default(const char* old_name, const char* new_name) } -static int mv(int argc, const char **argv) +static int mv(int argc, const char **argv, const char *prefix) { int show_progress = isatty(2); struct option options[] = { @@ -691,7 +700,7 @@ static int mv(int argc, const char **argv) int i, refs_renamed_nr = 0, refspec_updated = 0; struct progress *progress = NULL; - argc = parse_options(argc, argv, NULL, options, + argc = parse_options(argc, argv, prefix, options, builtin_remote_rename_usage, 0); if (argc != 2) @@ -726,29 +735,31 @@ static int mv(int argc, const char **argv) return error(_("Could not rename config section '%s' to '%s'"), buf.buf, buf2.buf); - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); - git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.raw_nr; i++) { - char *ptr; - - strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch.raw[i]); - ptr = strstr(buf2.buf, old_remote_context.buf); - if (ptr) { - refspec_updated = 1; - strbuf_splice(&buf2, - ptr-buf2.buf + strlen(":refs/remotes/"), - strlen(rename.old_name), rename.new_name, - strlen(rename.new_name)); - } else - warning(_("Not updating non-default fetch refspec\n" - "\t%s\n" - "\tPlease update the configuration manually if necessary."), - buf2.buf); - - git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); + if (oldremote->fetch.raw_nr) { + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); + git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); + for (i = 0; i < oldremote->fetch.raw_nr; i++) { + char *ptr; + + strbuf_reset(&buf2); + strbuf_addstr(&buf2, oldremote->fetch.raw[i]); + ptr = strstr(buf2.buf, old_remote_context.buf); + if (ptr) { + refspec_updated = 1; + strbuf_splice(&buf2, + ptr-buf2.buf + strlen(":refs/remotes/"), + strlen(rename.old_name), rename.new_name, + strlen(rename.new_name)); + } else + warning(_("Not updating non-default fetch refspec\n" + "\t%s\n" + "\tPlease update the configuration manually if necessary."), + buf2.buf); + + git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); + } } read_branches(); @@ -840,7 +851,7 @@ static int mv(int argc, const char **argv) return 0; } -static int rm(int argc, const char **argv) +static int rm(int argc, const char **argv, const char *prefix) { struct option options[] = { OPT_END() @@ -858,12 +869,14 @@ static int rm(int argc, const char **argv) cb_data.skipped = &skipped; cb_data.keep = &known_remotes; - if (argc != 2) + argc = parse_options(argc, argv, prefix, options, + builtin_remote_rm_usage, 0); + if (argc != 1) usage_with_options(builtin_remote_rm_usage, options); - remote = remote_get(argv[1]); + remote = remote_get(argv[0]); if (!remote_is_configured(remote, 1)) { - error(_("No such remote: '%s'"), argv[1]); + error(_("No such remote: '%s'"), argv[0]); exit(2); } @@ -931,7 +944,7 @@ static int rm(int argc, const char **argv) return result; } -static void clear_push_info(void *util, const char *string) +static void clear_push_info(void *util, const char *string UNUSED) { struct push_info *info = util; free(info->dest); @@ -941,6 +954,7 @@ static void clear_push_info(void *util, const char *string) static void free_remote_ref_states(struct ref_states *states) { string_list_clear(&states->new_refs, 0); + string_list_clear(&states->skipped, 0); string_list_clear(&states->stale, 1); string_list_clear(&states->tracked, 0); string_list_clear(&states->heads, 0); @@ -948,7 +962,8 @@ static void free_remote_ref_states(struct ref_states *states) } static int append_ref_to_tracked_list(const char *refname, - const struct object_id *oid, int flags, void *cb_data) + const struct object_id *oid UNUSED, + int flags, void *cb_data) { struct ref_states *states = cb_data; struct refspec_item refspec; @@ -1035,6 +1050,8 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data) arg = states->remote->name; } else if (string_list_has_string(&states->tracked, name)) arg = _(" tracked"); + else if (string_list_has_string(&states->skipped, name)) + arg = _(" skipped"); else if (string_list_has_string(&states->stale, name)) arg = _(" stale (use 'git remote prune' to remove)"); else @@ -1222,10 +1239,9 @@ static int get_one_entry(struct remote *remote, void *priv) static int show_all(void) { - struct string_list list = STRING_LIST_INIT_NODUP; + struct string_list list = STRING_LIST_INIT_DUP; int result; - list.strdup_strings = 1; result = for_each_remote(get_one_entry, &list); if (!result) { @@ -1248,7 +1264,7 @@ static int show_all(void) return result; } -static int show(int argc, const char **argv) +static int show(int argc, const char **argv, const char *prefix) { int no_query = 0, result = 0, query_flag = 0; struct option options[] = { @@ -1257,7 +1273,8 @@ static int show(int argc, const char **argv) }; struct show_info info = SHOW_INFO_INIT; - argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_show_usage, 0); if (argc < 1) @@ -1308,6 +1325,7 @@ static int show(int argc, const char **argv) /* remote branch info */ info.width = 0; for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info); + for_each_string_list(&info.states.skipped, add_remote_to_show_info, &info); for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info); for_each_string_list(&info.states.stale, add_remote_to_show_info, &info); if (info.list.nr) @@ -1350,7 +1368,7 @@ static int show(int argc, const char **argv) return result; } -static int set_head(int argc, const char **argv) +static int set_head(int argc, const char **argv, const char *prefix) { int i, opt_a = 0, opt_d = 0, result = 0; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; @@ -1363,8 +1381,8 @@ static int set_head(int argc, const char **argv) N_("delete refs/remotes/<name>/HEAD")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_sethead_usage, 0); if (argc) strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); @@ -1455,7 +1473,7 @@ static int prune_remote(const char *remote, int dry_run) return result; } -static int prune(int argc, const char **argv) +static int prune(int argc, const char **argv, const char *prefix) { int dry_run = 0, result = 0; struct option options[] = { @@ -1463,8 +1481,8 @@ static int prune(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_prune_usage, 0); if (argc < 1) usage_with_options(builtin_remote_prune_usage, options); @@ -1475,7 +1493,7 @@ static int prune(int argc, const char **argv) return result; } -static int get_remote_default(const char *key, const char *value, void *priv) +static int get_remote_default(const char *key, const char *value UNUSED, void *priv) { if (strcmp(key, "remotes.default") == 0) { int *found = priv; @@ -1484,7 +1502,7 @@ static int get_remote_default(const char *key, const char *value, void *priv) return 0; } -static int update(int argc, const char **argv) +static int update(int argc, const char **argv, const char *prefix) { int i, prune = -1; struct option options[] = { @@ -1492,36 +1510,35 @@ static int update(int argc, const char **argv) N_("prune remotes after fetching")), OPT_END() }; - struct strvec fetch_argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int default_defined = 0; - int retval; - argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); - strvec_push(&fetch_argv, "fetch"); + strvec_push(&cmd.args, "fetch"); if (prune != -1) - strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune"); + strvec_push(&cmd.args, prune ? "--prune" : "--no-prune"); if (verbose) - strvec_push(&fetch_argv, "-v"); - strvec_push(&fetch_argv, "--multiple"); + strvec_push(&cmd.args, "-v"); + strvec_push(&cmd.args, "--multiple"); if (argc < 2) - strvec_push(&fetch_argv, "default"); + strvec_push(&cmd.args, "default"); for (i = 1; i < argc; i++) - strvec_push(&fetch_argv, argv[i]); + strvec_push(&cmd.args, argv[i]); - if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) { + if (strcmp(cmd.args.v[cmd.args.nr-1], "default") == 0) { git_config(get_remote_default, &default_defined); if (!default_defined) { - strvec_pop(&fetch_argv); - strvec_push(&fetch_argv, "--all"); + strvec_pop(&cmd.args); + strvec_push(&cmd.args, "--all"); } } - retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD); - strvec_clear(&fetch_argv); - return retval; + cmd.git_cmd = 1; + return run_command(&cmd); } static int remove_all_fetch_refspecs(const char *key) @@ -1567,7 +1584,7 @@ static int set_remote_branches(const char *remotename, const char **branches, return 0; } -static int set_branches(int argc, const char **argv) +static int set_branches(int argc, const char **argv, const char *prefix) { int add_mode = 0; struct option options[] = { @@ -1575,7 +1592,7 @@ static int set_branches(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, NULL, options, + argc = parse_options(argc, argv, prefix, options, builtin_remote_setbranches_usage, 0); if (argc == 0) { error(_("no remote specified")); @@ -1586,7 +1603,7 @@ static int set_branches(int argc, const char **argv) return set_remote_branches(argv[0], argv + 1, add_mode); } -static int get_url(int argc, const char **argv) +static int get_url(int argc, const char **argv, const char *prefix) { int i, push_mode = 0, all_mode = 0; const char *remotename = NULL; @@ -1600,7 +1617,8 @@ static int get_url(int argc, const char **argv) N_("return all URLs")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0); + argc = parse_options(argc, argv, prefix, options, + builtin_remote_geturl_usage, 0); if (argc != 1) usage_with_options(builtin_remote_geturl_usage, options); @@ -1639,7 +1657,7 @@ static int get_url(int argc, const char **argv) return 0; } -static int set_url(int argc, const char **argv) +static int set_url(int argc, const char **argv, const char *prefix) { int i, push_mode = 0, add_mode = 0, delete_mode = 0; int matches = 0, negative_matches = 0; @@ -1660,7 +1678,8 @@ static int set_url(int argc, const char **argv) N_("delete URLs")), OPT_END() }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage, + argc = parse_options(argc, argv, prefix, options, + builtin_remote_seturl_usage, PARSE_OPT_KEEP_ARGV0); if (add_mode && delete_mode) @@ -1731,41 +1750,33 @@ out: int cmd_remote(int argc, const char **argv, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("rename", &fn, mv), + OPT_SUBCOMMAND_F("rm", &fn, rm, PARSE_OPT_NOCOMPLETE), + OPT_SUBCOMMAND("remove", &fn, rm), + OPT_SUBCOMMAND("set-head", &fn, set_head), + OPT_SUBCOMMAND("set-branches", &fn, set_branches), + OPT_SUBCOMMAND("get-url", &fn, get_url), + OPT_SUBCOMMAND("set-url", &fn, set_url), + OPT_SUBCOMMAND("show", &fn, show), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("update", &fn, update), OPT_END() }; - int result; argc = parse_options(argc, argv, prefix, options, builtin_remote_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + PARSE_OPT_SUBCOMMAND_OPTIONAL); - if (argc < 1) - result = show_all(); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv); - else if (!strcmp(argv[0], "rename")) - result = mv(argc, argv); - else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) - result = rm(argc, argv); - else if (!strcmp(argv[0], "set-head")) - result = set_head(argc, argv); - else if (!strcmp(argv[0], "set-branches")) - result = set_branches(argc, argv); - else if (!strcmp(argv[0], "get-url")) - result = get_url(argc, argv); - else if (!strcmp(argv[0], "set-url")) - result = set_url(argc, argv); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv); - else if (!strcmp(argv[0], "update")) - result = update(argc, argv); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_remote_usage, options); + if (fn) { + return !!fn(argc, argv, prefix); + } else { + if (argc) { + error(_("unknown subcommand: `%s'"), argv[0]); + usage_with_options(builtin_remote_usage, options); + } + return !!show_all(); } - - return result ? 1 : 0; } diff --git a/builtin/repack.c b/builtin/repack.c index c957b2959f..87f73c8923 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -1,7 +1,8 @@ #include "builtin.h" -#include "cache.h" +#include "alloc.h" #include "config.h" #include "dir.h" +#include "hex.h" #include "parse-options.h" #include "run-command.h" #include "sigchain.h" @@ -32,7 +33,6 @@ static int write_bitmaps = -1; static int use_delta_islands; static int run_update_server_info = 1; static char *packdir, *packtmp_name, *packtmp; -static char *cruft_expiration; static const char *const git_repack_usage[] = { N_("git repack [<options>]"), @@ -41,7 +41,7 @@ static const char *const git_repack_usage[] = { static const char incremental_bitmap_conflict_error[] = N_( "Incremental repacks are incompatible with bitmap indexes. Use\n" -"--no-write-bitmap-index or disable the pack.writebitmaps configuration." +"--no-write-bitmap-index or disable the pack.writeBitmaps configuration." ); struct pack_objects_args { @@ -92,44 +92,6 @@ static int repack_config(const char *var, const char *value, void *cb) } /* - * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files. - */ -static void remove_temporary_files(void) -{ - struct strbuf buf = STRBUF_INIT; - size_t dirlen, prefixlen; - DIR *dir; - struct dirent *e; - - dir = opendir(packdir); - if (!dir) - return; - - /* Point at the slash at the end of ".../objects/pack/" */ - dirlen = strlen(packdir) + 1; - strbuf_addstr(&buf, packtmp); - /* Hold the length of ".tmp-%d-pack-" */ - prefixlen = buf.len - dirlen; - - while ((e = readdir(dir))) { - if (strncmp(e->d_name, buf.buf + dirlen, prefixlen)) - continue; - strbuf_setlen(&buf, dirlen); - strbuf_addstr(&buf, e->d_name); - unlink(buf.buf); - } - closedir(dir); - strbuf_release(&buf); -} - -static void remove_pack_on_signal(int signo) -{ - remove_temporary_files(); - sigchain_pop(signo); - raise(signo); -} - -/* * Adds all packs hex strings to either fname_nonkept_list or * fname_kept_list based on whether each pack has a corresponding * .keep file or not. Packs without a .keep file are not to be kept @@ -188,7 +150,8 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) } static void prepare_pack_objects(struct child_process *cmd, - const struct pack_objects_args *args) + const struct pack_objects_args *args, + const char *out) { strvec_push(&cmd->args, "pack-objects"); if (args->window) @@ -211,7 +174,7 @@ static void prepare_pack_objects(struct child_process *cmd, strvec_push(&cmd->args, "--quiet"); if (delta_base_offset) strvec_push(&cmd->args, "--delta-base-offset"); - strvec_push(&cmd->args, packtmp); + strvec_push(&cmd->args, out); cmd->git_cmd = 1; cmd->out = -1; } @@ -220,8 +183,9 @@ static void prepare_pack_objects(struct child_process *cmd, * Write oid to the given struct child_process's stdin, starting it first if * necessary. */ -static int write_oid(const struct object_id *oid, struct packed_git *pack, - uint32_t pos, void *data) +static int write_oid(const struct object_id *oid, + struct packed_git *pack UNUSED, + uint32_t pos UNUSED, void *data) { struct child_process *cmd = data; @@ -247,11 +211,15 @@ static struct { {".idx"}, }; -static unsigned populate_pack_exts(char *name) +struct generated_pack_data { + struct tempfile *tempfiles[ARRAY_SIZE(exts)]; +}; + +static struct generated_pack_data *populate_pack_exts(const char *name) { struct stat statbuf; struct strbuf path = STRBUF_INIT; - unsigned ret = 0; + struct generated_pack_data *data = xcalloc(1, sizeof(*data)); int i; for (i = 0; i < ARRAY_SIZE(exts); i++) { @@ -261,11 +229,11 @@ static unsigned populate_pack_exts(char *name) if (stat(path.buf, &statbuf)) continue; - ret |= (1 << i); + data->tempfiles[i] = register_tempfile(path.buf); } strbuf_release(&path); - return ret; + return data; } static void repack_promisor_objects(const struct pack_objects_args *args, @@ -275,7 +243,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args, FILE *out; struct strbuf line = STRBUF_INIT; - prepare_pack_objects(&cmd, args); + prepare_pack_objects(&cmd, args, packtmp); cmd.in = -1; /* @@ -320,7 +288,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args, line.buf); write_promisor_file(promisor_name, NULL, 0); - item->util = (void *)(uintptr_t)populate_pack_exts(item->string); + item->util = populate_pack_exts(item->string); free(promisor_name); } @@ -514,9 +482,9 @@ struct midx_snapshot_ref_data { int preferred; }; -static int midx_snapshot_ref_one(const char *refname, +static int midx_snapshot_ref_one(const char *refname UNUSED, const struct object_id *oid, - int flag, void *_data) + int flag UNUSED, void *_data) { struct midx_snapshot_ref_data *data = _data; struct object_id peeled; @@ -661,8 +629,39 @@ static int write_midx_included_packs(struct string_list *include, return finish_command(&cmd); } +static void remove_redundant_bitmaps(struct string_list *include, + const char *packdir) +{ + struct strbuf path = STRBUF_INIT; + struct string_list_item *item; + size_t packdir_len; + + strbuf_addstr(&path, packdir); + strbuf_addch(&path, '/'); + packdir_len = path.len; + + /* + * Remove any pack bitmaps corresponding to packs which are now + * included in the MIDX. + */ + for_each_string_list_item(item, include) { + strbuf_addstr(&path, item->string); + strbuf_strip_suffix(&path, ".idx"); + strbuf_addstr(&path, ".bitmap"); + + if (unlink(path.buf) && errno != ENOENT) + warning_errno(_("could not remove stale bitmap: %s"), + path.buf); + + strbuf_setlen(&path, packdir_len); + } + strbuf_release(&path); +} + static int write_cruft_pack(const struct pack_objects_args *args, + const char *destination, const char *pack_prefix, + const char *cruft_expiration, struct string_list *names, struct string_list *existing_packs, struct string_list *existing_kept_packs) @@ -672,8 +671,10 @@ static int write_cruft_pack(const struct pack_objects_args *args, struct string_list_item *item; FILE *in, *out; int ret; + const char *scratch; + int local = skip_prefix(destination, packdir, &scratch); - prepare_pack_objects(&cmd, args); + prepare_pack_objects(&cmd, args, destination); strvec_push(&cmd.args, "--cruft"); if (cruft_expiration) @@ -698,6 +699,10 @@ static int write_cruft_pack(const struct pack_objects_args *args, * By the time it is read here, it contains only the pack(s) * that were just written, which is exactly the set of packs we * want to consider kept. + * + * If `--expire-to` is given, the double-use served by `names` + * ensures that the pack written to `--expire-to` excludes any + * objects contained in the cruft pack. */ in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) @@ -710,10 +715,19 @@ static int write_cruft_pack(const struct pack_objects_args *args, out = xfdopen(cmd.out, "r"); while (strbuf_getline_lf(&line, out) != EOF) { + struct string_list_item *item; + if (line.len != the_hash_algo->hexsz) die(_("repack: Expecting full hex object ID lines only " "from pack-objects.")); - string_list_append(names, line.buf); + /* + * avoid putting packs written outside of the repository in the + * list of names + */ + if (local) { + item = string_list_append(names, line.buf); + item->util = populate_pack_exts(line.buf); + } } fclose(out); @@ -727,7 +741,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct string_list names = STRING_LIST_INIT_DUP; - struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP; struct string_list existing_kept_packs = STRING_LIST_INIT_DUP; struct pack_geometry *geometry = NULL; @@ -746,6 +759,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct pack_objects_args cruft_po_args = {NULL}; int geometric_factor = 0; int write_midx = 0; + const char *cruft_expiration = NULL; + const char *expire_to = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -795,6 +810,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("find a geometric progression with factor <N>")), OPT_BOOL('m', "write-midx", &write_midx, N_("write a multi-pack index of the resulting packs")), + OPT_STRING(0, "expire-to", &expire_to, N_("dir"), + N_("pack prefix to store a pack containing pruned objects")), OPT_END() }; @@ -860,9 +877,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) split_pack_geometry(geometry, geometric_factor); } - sigchain_push_common(remove_pack_on_signal); - - prepare_pack_objects(&cmd, &po_args); + prepare_pack_objects(&cmd, &po_args, packtmp); show_progress = !po_args.quiet && isatty(2); @@ -935,7 +950,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) ret = start_command(&cmd); if (ret) - return ret; + goto cleanup; if (geometry) { FILE *in = xfdopen(cmd.in, "w"); @@ -953,14 +968,18 @@ int cmd_repack(int argc, const char **argv, const char *prefix) out = xfdopen(cmd.out, "r"); while (strbuf_getline_lf(&line, out) != EOF) { + struct string_list_item *item; + if (line.len != the_hash_algo->hexsz) die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - string_list_append(&names, line.buf); + item = string_list_append(&names, line.buf); + item->util = populate_pack_exts(item->string); } + strbuf_release(&line); fclose(out); ret = finish_command(&cmd); if (ret) - return ret; + goto cleanup; if (!names.nr && !po_args.quiet) printf_ln(_("Nothing new to pack.")); @@ -985,49 +1004,81 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cruft_po_args.local = po_args.local; cruft_po_args.quiet = po_args.quiet; - ret = write_cruft_pack(&cruft_po_args, pack_prefix, &names, + ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, + cruft_expiration, &names, &existing_nonkept_packs, &existing_kept_packs); if (ret) - return ret; + goto cleanup; + + if (delete_redundant && expire_to) { + /* + * If `--expire-to` is given with `-d`, it's possible + * that we're about to prune some objects. With cruft + * packs, pruning is implicit: any objects from existing + * packs that weren't picked up by new packs are removed + * when their packs are deleted. + * + * Generate an additional cruft pack, with one twist: + * `names` now includes the name of the cruft pack + * written in the previous step. So the contents of + * _this_ cruft pack exclude everything contained in the + * existing cruft pack (that is, all of the unreachable + * objects which are no older than + * `--cruft-expiration`). + * + * To make this work, cruft_expiration must become NULL + * so that this cruft pack doesn't actually prune any + * objects. If it were non-NULL, this call would always + * generate an empty pack (since every object not in the + * cruft pack generated above will have an mtime older + * than the expiration). + */ + ret = write_cruft_pack(&cruft_po_args, expire_to, + pack_prefix, + NULL, + &names, + &existing_nonkept_packs, + &existing_kept_packs); + if (ret) + goto cleanup; + } } string_list_sort(&names); - for_each_string_list_item(item, &names) { - item->util = (void *)(uintptr_t)populate_pack_exts(item->string); - } - close_object_store(the_repository->objects); /* * Ok we have prepared all new packfiles. */ for_each_string_list_item(item, &names) { + struct generated_pack_data *data = item->util; + for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname, *fname_old; + char *fname; fname = mkpathdup("%s/pack-%s%s", packdir, item->string, exts[ext].name); - fname_old = mkpathdup("%s-%s%s", - packtmp, item->string, exts[ext].name); - if (((uintptr_t)item->util) & ((uintptr_t)1 << ext)) { + if (data->tempfiles[ext]) { + const char *fname_old = get_tempfile_path(data->tempfiles[ext]); struct stat statbuffer; + if (!stat(fname_old, &statbuffer)) { statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); chmod(fname_old, statbuffer.st_mode); } - if (rename(fname_old, fname)) - die_errno(_("renaming '%s' failed"), fname_old); + if (rename_tempfile(&data->tempfiles[ext], fname)) + die_errno(_("renaming pack to '%s' failed"), fname); } else if (!exts[ext].optional) - die(_("missing required file: %s"), fname_old); + die(_("pack-objects did not write a '%s' file for pack %s-%s"), + exts[ext].name, packtmp, item->string); else if (unlink(fname) < 0 && errno != ENOENT) die_errno(_("could not unlink: %s"), fname); free(fname); - free(fname_old); } } /* End of pack replacement. */ @@ -1060,10 +1111,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, show_progress, write_bitmaps > 0); + if (!ret && write_bitmaps) + remove_redundant_bitmaps(&include, packdir); + string_list_clear(&include, 0); if (ret) - return ret; + goto cleanup; } reprepare_packed_git(the_repository); @@ -1090,6 +1144,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); + if ((p->pack_keep) || + (string_list_has_string(&existing_kept_packs, + buf.buf))) + continue; + remove_redundant_pack(packdir, buf.buf); } strbuf_release(&buf); @@ -1107,7 +1166,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (run_update_server_info) update_server_info(0); - remove_temporary_files(); if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) { unsigned flags = 0; @@ -1116,12 +1174,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) write_midx_file(get_object_directory(), NULL, NULL, flags); } - string_list_clear(&names, 0); - string_list_clear(&rollback, 0); +cleanup: + string_list_clear(&names, 1); string_list_clear(&existing_nonkept_packs, 0); string_list_clear(&existing_kept_packs, 0); clear_pack_geometry(geometry); - strbuf_release(&line); - return 0; + return ret; } diff --git a/builtin/replace.c b/builtin/replace.c index 583702a098..71d8e949e3 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -11,10 +11,12 @@ #include "cache.h" #include "config.h" #include "builtin.h" +#include "hex.h" #include "refs.h" #include "parse-options.h" #include "run-command.h" #include "object-store.h" +#include "replace-object.h" #include "repository.h" #include "tag.h" @@ -106,6 +108,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) size_t base_len; int had_error = 0; struct object_id oid; + const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref; strbuf_addstr(&ref, git_replace_ref_base); base_len = ref.len; @@ -147,6 +150,8 @@ static int check_ref_valid(struct object_id *object, struct strbuf *ref, int force) { + const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref; + strbuf_reset(ref); strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object)); if (check_refname_format(ref->buf, 0)) diff --git a/builtin/rerere.c b/builtin/rerere.c index 83d7a778e3..94ffb8c21a 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -10,11 +10,11 @@ #include "pathspec.h" static const char * const rerere_usage[] = { - N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"), + N_("git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"), NULL, }; -static int outf(void *dummy, mmbuffer_t *ptr, int nbuf) +static int outf(void *dummy UNUSED, mmbuffer_t *ptr, int nbuf) { int i; for (i = 0; i < nbuf; i++) diff --git a/builtin/reset.c b/builtin/reset.c index 344fff8f3a..24b04aeecb 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -7,9 +7,10 @@ * * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "tag.h" #include "object.h" @@ -26,6 +27,7 @@ #include "submodule.h" #include "submodule-config.h" #include "dir.h" +#include "add-interactive.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000) @@ -73,16 +75,18 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t case HARD: opts.update = 1; opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED; + opts.skip_cache_tree_update = 1; break; case MIXED: opts.reset = UNPACK_RESET_PROTECT_UNTRACKED; + opts.skip_cache_tree_update = 1; /* but opts.update=0, so working tree not updated */ break; default: BUG("invalid reset_type passed to reset_index"); } - read_cache_unmerged(); + repo_read_index_unmerged(the_repository); if (reset_type == KEEP) { struct object_id head_oid; @@ -131,7 +135,8 @@ static void print_new_head_line(struct commit *commit) } static void update_index_from_diff(struct diff_queue_struct *q, - struct diff_options *opt, void *data) + struct diff_options *opt UNUSED, + void *data) { int i; int intent_to_add = *(int *)data; @@ -143,7 +148,7 @@ 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_cache(one->path); + remove_file_from_index(&the_index, one->path); continue; } @@ -158,8 +163,8 @@ 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 = cache_name_pos(one->path, strlen(one->path)); - if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) || + 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))) ce->ce_flags |= CE_SKIP_WORKTREE; @@ -170,92 +175,11 @@ 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_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); + add_index_entry(&the_index, ce, + ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } } -static int pathspec_needs_expanded_index(const struct pathspec *pathspec) -{ - unsigned int i, pos; - int res = 0; - char *skip_worktree_seen = NULL; - - /* - * When using a magic pathspec, assume for the sake of simplicity that - * the index needs to be expanded to match all matchable files. - */ - if (pathspec->magic) - return 1; - - for (i = 0; i < pathspec->nr; i++) { - struct pathspec_item item = pathspec->items[i]; - - /* - * If the pathspec item has a wildcard, the index should be expanded - * if the pathspec has the possibility of matching a subset of entries inside - * of a sparse directory (but not the entire directory). - * - * If the pathspec item is a literal path, the index only needs to be expanded - * if a) the pathspec isn't in the sparse checkout cone (to make sure we don't - * expand for in-cone files) and b) it doesn't match any sparse directories - * (since we can reset whole sparse directories without expanding them). - */ - if (item.nowildcard_len < item.len) { - /* - * Special case: if the pattern is a path inside the cone - * followed by only wildcards, the pattern cannot match - * partial sparse directories, so we know we don't need to - * expand the index. - * - * Examples: - * - in-cone/foo***: doesn't need expanded index - * - not-in-cone/bar*: may need expanded index - * - **.c: may need expanded index - */ - if (strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len && - path_in_cone_mode_sparse_checkout(item.original, &the_index)) - continue; - - for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; - - if (!S_ISSPARSEDIR(ce->ce_mode)) - continue; - - /* - * If the pre-wildcard length is longer than the sparse - * directory name and the sparse directory is the first - * component of the pathspec, need to expand the index. - */ - if (item.nowildcard_len > ce_namelen(ce) && - !strncmp(item.original, ce->name, ce_namelen(ce))) { - res = 1; - break; - } - - /* - * If the pre-wildcard length is shorter than the sparse - * directory and the pathspec does not match the whole - * directory, need to expand the index. - */ - if (!strncmp(item.original, ce->name, item.nowildcard_len) && - wildmatch(item.original, ce->name, 0)) { - res = 1; - break; - } - } - } else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) && - !matches_skip_worktree(pathspec, i, &skip_worktree_seen)) - res = 1; - - if (res > 0) - break; - } - - free(skip_worktree_seen); - return res; -} - static int read_from_tree(const struct pathspec *pathspec, struct object_id *tree_oid, int intent_to_add) @@ -273,7 +197,7 @@ static int read_from_tree(const struct pathspec *pathspec, opt.change = diff_change; opt.add_remove = diff_addremove; - if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec)) + if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec)) ensure_full_index(&the_index); if (do_diff_cache(tree_oid, &opt)) @@ -300,7 +224,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_cache()) + if (is_merge() || unmerged_index(&the_index)) die(_("Cannot do a %s reset in the middle of a merge."), _(reset_type_names[reset_type])); @@ -394,7 +318,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int reset_type = NONE, update_ref_status = 0, quiet = 0; int no_refresh = 0; int patch_mode = 0, pathspec_file_nul = 0, unborn; - const char *rev, *pathspec_from_file = NULL; + const char *rev; + char *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -468,7 +393,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - return run_add_interactive(rev, "--patch=reset", &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, + &pathspec); + goto cleanup; } /* git reset tree [--] paths... can be used to @@ -502,7 +429,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); /* Soft reset does not touch the index file nor the working tree @@ -513,11 +440,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type != SOFT) { struct lock_file lock = LOCK_INIT; - hold_locked_index(&lock, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock, + LOCK_DIE_ON_ERROR); if (reset_type == MIXED) { int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; - if (read_from_tree(&pathspec, &oid, intent_to_add)) - return 1; + if (read_from_tree(&pathspec, &oid, intent_to_add)) { + update_ref_status = 1; + goto cleanup; + } the_index.updated_skipworktree = 1; if (!no_refresh && get_git_work_tree()) { uint64_t t_begin, t_delta_in_ms; @@ -563,5 +493,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (!pathspec.nr) remove_branch_state(the_repository, 0); + discard_index(&the_index); + +cleanup: + clear_pathspec(&pathspec); + free(pathspec_from_file); return update_ref_status; } diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 30fd8e83ea..85e522dff8 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -2,6 +2,7 @@ #include "config.h" #include "commit.h" #include "diff.h" +#include "hex.h" #include "revision.h" #include "list-objects.h" #include "list-objects-filter.h" @@ -20,7 +21,8 @@ #include "packfile.h" static const char rev_list_usage[] = -"git rev-list [<options>] <commit-id>... [-- <path>...]\n" +"git rev-list [<options>] <commit>... [--] [<path>...]\n" +"\n" " limiting output:\n" " --max-count=<n>\n" " --max-age=<epoch>\n" @@ -37,6 +39,7 @@ static const char rev_list_usage[] = " --tags\n" " --remotes\n" " --stdin\n" +" --exclude-hidden=[fetch|receive|uploadpack]\n" " --quiet\n" " ordering output:\n" " --topo-order\n" @@ -46,6 +49,7 @@ static const char rev_list_usage[] = " --parents\n" " --children\n" " --objects | --objects-edge\n" +" --disk-usage[=human]\n" " --unpacked\n" " --header | --pretty\n" " --[no-]object-names\n" @@ -81,6 +85,7 @@ static int arg_show_object_names = 1; static int show_disk_usage; static off_t total_disk_usage; +static int human_readable; static off_t get_object_disk_usage(struct object *obj) { @@ -253,7 +258,8 @@ static inline void finish_object__ma(struct object *obj) } } -static int finish_object(struct object *obj, const char *name, void *cb_data) +static int finish_object(struct object *obj, const char *name UNUSED, + void *cb_data) { struct rev_list_info *info = cb_data; if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) { @@ -358,16 +364,27 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) static int show_object_fast( const struct object_id *oid, - enum object_type type, - int exclude, - uint32_t name_hash, - struct packed_git *found_pack, - off_t found_offset) + enum object_type type UNUSED, + int exclude UNUSED, + uint32_t name_hash UNUSED, + struct packed_git *found_pack UNUSED, + off_t found_offset UNUSED) { fprintf(stdout, "%s\n", oid_to_hex(oid)); return 1; } +static void print_disk_usage(off_t size) +{ + struct strbuf sb = STRBUF_INIT; + if (human_readable) + strbuf_humanise_bytes(&sb, size); + else + strbuf_addf(&sb, "%"PRIuMAX, (uintmax_t)size); + puts(sb.buf); + strbuf_release(&sb); +} + static inline int parse_missing_action_value(const char *value) { if (!strcmp(value, "error")) { @@ -473,6 +490,7 @@ static int try_bitmap_disk_usage(struct rev_info *revs, int filter_provided_objects) { struct bitmap_index *bitmap_git; + off_t size_from_bitmap; if (!show_disk_usage) return -1; @@ -481,8 +499,8 @@ static int try_bitmap_disk_usage(struct rev_info *revs, if (!bitmap_git) return -1; - printf("%"PRIuMAX"\n", - (uintmax_t)get_disk_usage_from_bitmap(bitmap_git, revs)); + size_from_bitmap = get_disk_usage_from_bitmap(bitmap_git, revs); + print_disk_usage(size_from_bitmap); return 0; } @@ -624,7 +642,21 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) continue; } - if (!strcmp(arg, "--disk-usage")) { + if (skip_prefix(arg, "--disk-usage", &arg)) { + if (*arg == '=') { + if (!strcmp(++arg, "human")) { + human_readable = 1; + } else + die(_("invalid value for '%s': '%s', the only allowed format is '%s'"), + "--disk-usage=<format>", arg, "human"); + } else if (*arg) { + /* + * Arguably should goto a label to continue chain of ifs? + * Doesn't matter unless we try to add --disk-usage-foo + * afterwards. + */ + usage(rev_list_usage); + } show_disk_usage = 1; info.flags |= REV_LIST_QUIET; continue; @@ -753,7 +785,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } if (show_disk_usage) - printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage); + print_disk_usage(total_disk_usage); cleanup: release_revisions(&revs); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index b259d8990a..e1fa9c6348 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -3,10 +3,12 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" +#include "alloc.h" #include "config.h" #include "commit.h" +#include "hex.h" #include "refs.h" #include "quote.h" #include "builtin.h" @@ -39,7 +41,7 @@ static int abbrev_ref_strict; static int output_sq; static int stuck_long; -static struct string_list *ref_excludes; +static struct ref_exclusions ref_excludes = REF_EXCLUSIONS_INIT; /* * Some arguments are relevant "revision" arguments, @@ -195,15 +197,17 @@ static int show_default(void) return 0; } -static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) +static int show_reference(const char *refname, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { - if (ref_excluded(ref_excludes, refname)) + if (ref_excluded(&ref_excludes, refname)) return 0; show_rev(NORMAL, oid, refname); return 0; } -static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) +static int anti_reference(const char *refname, const struct object_id *oid, + int flag UNUSED, void *cb_data UNUSED) { show_rev(REVERSED, oid, refname); return 0; @@ -479,6 +483,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) if (!s) s = help; + if (s == sb.buf) + die(_("missing opt-spec before option flags")); + if (s - sb.buf == 1) /* short option only */ o->short_name = *sb.buf; else if (sb.buf[1] != ',') /* long option only */ @@ -525,6 +532,7 @@ 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); return 0; } @@ -580,7 +588,7 @@ static void handle_ref_opt(const char *pattern, const char *prefix) for_each_glob_ref_in(show_reference, pattern, prefix, NULL); else for_each_ref_in(prefix, show_reference, NULL); - clear_ref_exclusion(&ref_excludes); + clear_ref_exclusions(&ref_excludes); } enum format_type { @@ -858,7 +866,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!strcmp(arg, "--all")) { for_each_ref(show_reference, NULL); - clear_ref_exclusion(&ref_excludes); + clear_ref_exclusions(&ref_excludes); continue; } if (skip_prefix(arg, "--disambiguate=", &arg)) { @@ -871,10 +879,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (opt_with_value(arg, "--branches", &arg)) { + if (ref_excludes.hidden_refs_configured) + return error(_("--exclude-hidden cannot be used together with --branches")); handle_ref_opt(arg, "refs/heads/"); continue; } if (opt_with_value(arg, "--tags", &arg)) { + if (ref_excludes.hidden_refs_configured) + return error(_("--exclude-hidden cannot be used together with --tags")); handle_ref_opt(arg, "refs/tags/"); continue; } @@ -883,6 +895,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (opt_with_value(arg, "--remotes", &arg)) { + if (ref_excludes.hidden_refs_configured) + return error(_("--exclude-hidden cannot be used together with --remotes")); handle_ref_opt(arg, "refs/remotes/"); continue; } @@ -890,6 +904,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) add_ref_exclusion(&ref_excludes, arg); continue; } + if (skip_prefix(arg, "--exclude-hidden=", &arg)) { + exclude_hidden_refs(&ref_excludes, arg); + continue; + } if (!strcmp(arg, "--show-toplevel")) { const char *work_tree = get_git_work_tree(); if (work_tree) @@ -992,7 +1010,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--shared-index-path")) { - if (read_cache() < 0) + 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; diff --git a/builtin/revert.c b/builtin/revert.c index 51776abea6..62986a7b1b 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -1,4 +1,5 @@ -#include "cache.h" +#include "git-compat-util.h" +#include "alloc.h" #include "config.h" #include "builtin.h" #include "parse-options.h" @@ -21,14 +22,15 @@ */ static const char * const revert_usage[] = { - N_("git revert [<options>] <commit-ish>..."), - N_("git revert <subcommand>"), + N_("git revert [--[no-]edit] [-n] [-m <parent-number>] [-s] [-S[<keyid>]] <commit>..."), + N_("git revert (--continue | --skip | --abort | --quit)"), NULL }; static const char * const cherry_pick_usage[] = { - N_("git cherry-pick [<options>] <commit-ish>..."), - N_("git cherry-pick <subcommand>"), + N_("git cherry-pick [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]\n" + " [-S[<keyid>]] <commit>..."), + N_("git cherry-pick (--continue | --skip | --abort | --quit)"), NULL }; @@ -130,11 +132,18 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) OPT_END(), }; options = parse_options_concat(options, cp_extra); + } else if (opts->action == REPLAY_REVERT) { + struct option cp_extra[] = { + OPT_BOOL(0, "reference", &opts->commit_use_reference, + N_("use the 'reference' format to refer to commits")), + OPT_END(), + }; + options = parse_options_concat(options, cp_extra); } argc = parse_options(argc, argv, NULL, options, usage_str, PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -213,6 +222,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) opts->strategy = xstrdup_or_null(opts->strategy); if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM")) opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM")); + free(options); if (cmd == 'q') { int ret = sequencer_remove_state(opts); @@ -239,6 +249,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix) res = run_sequencer(argc, argv, &opts); if (res < 0) die(_("revert failed")); + replay_opts_release(&opts); return res; } @@ -252,5 +263,6 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix) res = run_sequencer(argc, argv, &opts); if (res < 0) die(_("cherry-pick failed")); + replay_opts_release(&opts); return res; } diff --git a/builtin/rm.c b/builtin/rm.c index 84a935a16e..dc198f7908 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -3,8 +3,9 @@ * * Copyright (C) Linus Torvalds 2006 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "alloc.h" #include "advice.h" #include "config.h" #include "lockfile.h" @@ -17,7 +18,9 @@ #include "pathspec.h" static const char * const builtin_rm_usage[] = { - N_("git rm [<options>] [--] <file>..."), + N_("git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n" + " [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" + " [--] [<pathspec>...]"), NULL }; @@ -33,8 +36,8 @@ static int get_ours_cache_pos(const char *path, int pos) { int i = -pos - 1; - while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) { - if (ce_stage(active_cache[i]) == 2) + while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) { + if (ce_stage(the_index.cache[i]) == 2) return i; i++; } @@ -70,13 +73,13 @@ static void submodules_absorb_gitdir_if_needed(void) int pos; const struct cache_entry *ce; - pos = cache_name_pos(name, strlen(name)); + pos = index_name_pos(&the_index, name, strlen(name)); if (pos < 0) { pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; } - ce = active_cache[pos]; + ce = the_index.cache[pos]; if (!S_ISGITLINK(ce->ce_mode) || !file_exists(ce->name) || @@ -84,8 +87,7 @@ static void submodules_absorb_gitdir_if_needed(void) continue; if (!submodule_uses_gitfile(name)) - absorb_git_dir_into_superproject(name, - ABSORB_GITDIR_RECURSE_SUBMODULES); + absorb_git_dir_into_superproject(name, NULL); } } @@ -115,7 +117,7 @@ static int check_local_mod(struct object_id *head, int index_only) int local_changes = 0; int staged_changes = 0; - pos = cache_name_pos(name, strlen(name)); + pos = index_name_pos(&the_index, name, strlen(name)); if (pos < 0) { /* * Skip unmerged entries except for populated submodules @@ -125,11 +127,11 @@ static int check_local_mod(struct object_id *head, int index_only) if (pos < 0) continue; - if (!S_ISGITLINK(active_cache[pos]->ce_mode) || + if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) || is_empty_dir(name)) continue; } - ce = active_cache[pos]; + ce = the_index.cache[pos]; if (lstat(ce->name, &st) < 0) { if (!is_missing_file_error(errno)) @@ -166,7 +168,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 (ce_match_stat(ce, &st, 0) || + if (ie_match_stat(&the_index, ce, &st, 0) || (S_ISGITLINK(ce->ce_mode) && bad_to_remove_submodule(ce->name, SUBMODULE_REMOVAL_DIE_ON_ERROR | @@ -287,19 +289,22 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!index_only) setup_work_tree(); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL); seen = xcalloc(pathspec.nr, 1); - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + if (pathspec_needs_expanded_index(&the_index, &pathspec)) + ensure_full_index(&the_index); + + for (i = 0; i < the_index.cache_nr; i++) { + const struct cache_entry *ce = the_index.cache[i]; if (!include_sparse && (ce_skip_worktree(ce) || @@ -381,7 +386,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!quiet) printf("rm '%s'\n", path); - if (remove_file_from_cache(path)) + if (remove_file_from_index(&the_index, path)) die(_("git rm: unable to remove %s"), path); } diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 64962be016..fb5b2bad2c 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "config.h" #include "commit.h" +#include "hex.h" #include "refs.h" #include "pkt-line.h" #include "sideband.h" @@ -20,6 +21,7 @@ static const char * const send_pack_usage[] = { N_("git send-pack [--mirror] [--dry-run] [--force]\n" " [--receive-pack=<git-receive-pack>]\n" " [--verbose] [--thin] [--atomic]\n" + " [--[no-]signed | --signed=(true|false|if-asked)]\n" " [<host>:]<directory> (--all | <ref>...)"), NULL, }; @@ -129,8 +131,6 @@ static void print_helper_status(struct ref *ref) static int send_pack_config(const char *k, const char *v, void *cb) { - git_gpg_config(k, v, NULL); - if (!strcmp(k, "push.gpgsign")) { const char *value; if (!git_config_get_value("push.gpgsign", &value)) { diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 35825f075e..27a87167e1 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -132,7 +132,9 @@ static void read_from_stdin(struct shortlog *log) match = committer_match; break; case SHORTLOG_GROUP_TRAILER: - die(_("using --group=trailer with stdin is not supported")); + die(_("using %s with stdin is not supported"), "--group=trailer"); + case SHORTLOG_GROUP_FORMAT: + die(_("using %s with stdin is not supported"), "--group=format"); default: BUG("unhandled shortlog group"); } @@ -170,6 +172,9 @@ static void insert_records_from_trailers(struct shortlog *log, const char *commit_buffer, *body; struct strbuf ident = STRBUF_INIT; + if (!log->trailers.nr) + return; + /* * Using format_commit_message("%B") would be simpler here, but * this saves us copying the message. @@ -200,9 +205,34 @@ static void insert_records_from_trailers(struct shortlog *log, unuse_commit_buffer(commit, commit_buffer); } +static int shortlog_needs_dedup(const struct shortlog *log) +{ + return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr; +} + +static void insert_records_from_format(struct shortlog *log, + struct strset *dups, + struct commit *commit, + struct pretty_print_context *ctx, + const char *oneline) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list_item *item; + + for_each_string_list_item(item, &log->format) { + strbuf_reset(&buf); + + format_commit_message(commit, item->string, &buf, ctx); + + if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf)) + insert_one_record(log, buf.buf, oneline); + } + + strbuf_release(&buf); +} + void shortlog_add_commit(struct shortlog *log, struct commit *commit) { - struct strbuf ident = STRBUF_INIT; struct strbuf oneline = STRBUF_INIT; struct strset dups = STRSET_INIT; struct pretty_print_context ctx = {0}; @@ -211,7 +241,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; ctx.print_email_subject = 1; - ctx.date_mode.type = DATE_NORMAL; + ctx.date_mode = log->date_mode; ctx.output_encoding = get_log_output_encoding(); if (!log->summary) { @@ -222,30 +252,10 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) } oneline_str = oneline.len ? oneline.buf : "<none>"; - if (log->groups & SHORTLOG_GROUP_AUTHOR) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%aN <%aE>" : "%aN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - strset_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_COMMITTER) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%cN <%cE>" : "%cN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - strset_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_TRAILER) { - insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); - } + insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); + insert_records_from_format(log, &dups, commit, &ctx, oneline_str); strset_clear(&dups); - strbuf_release(&ident); strbuf_release(&oneline); } @@ -314,6 +324,7 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns if (unset) { log->groups = 0; string_list_clear(&log->trailers, 0); + string_list_clear(&log->format, 0); } else if (!strcasecmp(arg, "author")) log->groups |= SHORTLOG_GROUP_AUTHOR; else if (!strcasecmp(arg, "committer")) @@ -321,8 +332,15 @@ static int parse_group_option(const struct option *opt, const char *arg, int uns else if (skip_prefix(arg, "trailer:", &field)) { log->groups |= SHORTLOG_GROUP_TRAILER; string_list_append(&log->trailers, field); - } else + } else if (skip_prefix(arg, "format:", &field)) { + log->groups |= SHORTLOG_GROUP_FORMAT; + string_list_append(&log->format, field); + } else if (strchr(arg, '%')) { + log->groups |= SHORTLOG_GROUP_FORMAT; + string_list_append(&log->format, arg); + } else { return error(_("unknown group type: %s"), arg); + } return 0; } @@ -340,6 +358,19 @@ void shortlog_init(struct shortlog *log) log->in2 = DEFAULT_INDENT2; log->trailers.strdup_strings = 1; log->trailers.cmp = strcasecmp; + log->format.strdup_strings = 1; +} + +void shortlog_finish_setup(struct shortlog *log) +{ + if (log->groups & SHORTLOG_GROUP_AUTHOR) + string_list_append(&log->format, + log->email ? "%aN <%aE>" : "%aN"); + if (log->groups & SHORTLOG_GROUP_COMMITTER) + string_list_append(&log->format, + log->email ? "%cN <%cE>" : "%cN"); + + string_list_sort(&log->trailers); } int cmd_shortlog(int argc, const char **argv, const char *prefix) @@ -381,6 +412,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: + case PARSE_OPT_SUBCOMMAND: exit(129); case PARSE_OPT_COMPLETE: exit(0); @@ -406,10 +438,11 @@ parse_done: log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT; log.abbrev = rev.abbrev; log.file = rev.diffopt.file; + log.date_mode = rev.date_mode; if (!log.groups) log.groups = SHORTLOG_GROUP_AUTHOR; - string_list_sort(&log.trailers); + shortlog_finish_setup(&log); /* assume HEAD if from a tty */ if (!nongit && !rev.pending.nr && isatty(0)) @@ -443,7 +476,7 @@ void shortlog_output(struct shortlog *log) struct strbuf sb = STRBUF_INIT; if (log->sort_by_number) - QSORT(log->list.items, log->list.nr, + STABLE_QSORT(log->list.items, log->list.nr, log->summary ? compare_by_counter : compare_by_list); for (i = 0; i < log->list.nr; i++) { const struct string_list_item *item = &log->list.items[i]; @@ -478,4 +511,5 @@ void shortlog_output(struct shortlog *log) log->list.strdup_strings = 1; string_list_clear(&log->list, 1); clear_mailmap(&log->mailmap); + string_list_clear(&log->format, 0); } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 64c649c6a2..8342b68aef 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -1,5 +1,6 @@ #include "cache.h" #include "config.h" +#include "hex.h" #include "pretty.h" #include "refs.h" #include "builtin.h" @@ -14,7 +15,8 @@ static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" " [--current] [--color[=<when>] | --no-color] [--sparse]\n" " [--more=<n> | --list | --independent | --merge-base]\n" - " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), + " [--no-name | --sha1-name] [--topics]\n" + " [(<rev> | <glob>)...]"), N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"), NULL }; @@ -404,7 +406,7 @@ static int append_ref(const char *refname, const struct object_id *oid, } static int append_head_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object_id tmp; int ofs = 11; @@ -419,7 +421,7 @@ static int append_head_ref(const char *refname, const struct object_id *oid, } static int append_remote_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { struct object_id tmp; int ofs = 13; @@ -434,7 +436,7 @@ static int append_remote_ref(const char *refname, const struct object_id *oid, } static int append_tag_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data UNUSED) { if (!starts_with(refname, "refs/tags/")) return 0; @@ -955,5 +957,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (shown_merge_point && --extra < 0) break; } + free(head); return 0; } diff --git a/builtin/show-index.c b/builtin/show-index.c index 0e0b9fb95b..98ec40ddf4 100644 --- a/builtin/show-index.c +++ b/builtin/show-index.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "hex.h" #include "pack.h" #include "parse-options.h" diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 5fa207a044..1f28d7fe4b 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "cache.h" #include "config.h" +#include "hex.h" #include "refs.h" #include "object-store.h" #include "object.h" @@ -9,7 +10,9 @@ #include "parse-options.h" static const char * const show_ref_usage[] = { - N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"), + N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n" + " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n" + " [--heads] [--] [<pattern>...]"), N_("git show-ref --exclude-existing[=<pattern>]"), NULL }; @@ -47,7 +50,7 @@ static void show_one(const char *refname, const struct object_id *oid) } static int show_ref(const char *refname, const struct object_id *oid, - int flag, void *cbdata) + int flag UNUSED, void *cbdata UNUSED) { if (show_head && !strcmp(refname, "HEAD")) goto match; @@ -77,8 +80,9 @@ match: return 0; } -static int add_existing(const char *refname, const struct object_id *oid, - int flag, void *cbdata) +static int add_existing(const char *refname, + const struct object_id *oid UNUSED, + int flag UNUSED, void *cbdata) { struct string_list *list = (struct string_list *)cbdata; string_list_insert(list, refname); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index f91e29b56a..8d5ae6f2a6 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -20,7 +20,7 @@ static const char *empty_base = ""; static char const * const builtin_sparse_checkout_usage[] = { - N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"), + N_("git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"), NULL }; @@ -48,7 +48,7 @@ static char const * const builtin_sparse_checkout_list_usage[] = { NULL }; -static int sparse_checkout_list(int argc, const char **argv) +static int sparse_checkout_list(int argc, const char **argv, const char *prefix) { static struct option builtin_sparse_checkout_list_options[] = { OPT_END(), @@ -60,7 +60,7 @@ static int sparse_checkout_list(int argc, const char **argv) if (!core_apply_sparse_checkout) die(_("this worktree is not sparse")); - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_list_options, builtin_sparse_checkout_list_usage, 0); @@ -218,14 +218,13 @@ static int update_working_directory(struct pattern_list *pl) o.src_index = r->index; o.dst_index = r->index; o.skip_sparse_checkout = 0; - o.pl = pl; setup_work_tree(); repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); setup_unpack_trees_porcelain(&o, "sparse-checkout"); - result = update_sparsity(&o); + result = update_sparsity(&o, pl); clear_unpack_trees_porcelain(&o); if (result == UPDATE_SPARSITY_WARNINGS) @@ -431,7 +430,7 @@ static struct sparse_checkout_init_opts { int sparse_index; } init_opts; -static int sparse_checkout_init(int argc, const char **argv) +static int sparse_checkout_init(int argc, const char **argv, const char *prefix) { struct pattern_list pl; char *sparse_filename; @@ -452,7 +451,7 @@ static int sparse_checkout_init(int argc, const char **argv) init_opts.cone_mode = -1; init_opts.sparse_index = -1; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_init_options, builtin_sparse_checkout_init_usage, 0); @@ -767,7 +766,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_add_options, builtin_sparse_checkout_add_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); sanitize_paths(argc, argv, prefix, add_opts.skip_checks); @@ -813,7 +812,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_set_options, builtin_sparse_checkout_set_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index)) return 1; @@ -843,7 +842,8 @@ static struct sparse_checkout_reapply_opts { int sparse_index; } reapply_opts; -static int sparse_checkout_reapply(int argc, const char **argv) +static int sparse_checkout_reapply(int argc, const char **argv, + const char *prefix) { static struct option builtin_sparse_checkout_reapply_options[] = { OPT_BOOL(0, "cone", &reapply_opts.cone_mode, @@ -859,7 +859,7 @@ static int sparse_checkout_reapply(int argc, const char **argv) reapply_opts.cone_mode = -1; reapply_opts.sparse_index = -1; - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_reapply_options, builtin_sparse_checkout_reapply_usage, 0); @@ -876,7 +876,8 @@ static char const * const builtin_sparse_checkout_disable_usage[] = { NULL }; -static int sparse_checkout_disable(int argc, const char **argv) +static int sparse_checkout_disable(int argc, const char **argv, + const char *prefix) { static struct option builtin_sparse_checkout_disable_options[] = { OPT_END(), @@ -895,7 +896,7 @@ static int sparse_checkout_disable(int argc, const char **argv) * forcibly return to a dense checkout regardless of initial state. */ - argc = parse_options(argc, argv, NULL, + argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_disable_options, builtin_sparse_checkout_disable_usage, 0); @@ -922,39 +923,25 @@ static int sparse_checkout_disable(int argc, const char **argv) int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) { - static struct option builtin_sparse_checkout_options[] = { + parse_opt_subcommand_fn *fn = NULL; + struct option builtin_sparse_checkout_options[] = { + OPT_SUBCOMMAND("list", &fn, sparse_checkout_list), + OPT_SUBCOMMAND("init", &fn, sparse_checkout_init), + OPT_SUBCOMMAND("set", &fn, sparse_checkout_set), + OPT_SUBCOMMAND("add", &fn, sparse_checkout_add), + OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply), + OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable), OPT_END(), }; - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); - argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_options, - builtin_sparse_checkout_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + builtin_sparse_checkout_usage, 0); git_config(git_default_config, NULL); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - if (argc > 0) { - if (!strcmp(argv[0], "list")) - return sparse_checkout_list(argc, argv); - if (!strcmp(argv[0], "init")) - return sparse_checkout_init(argc, argv); - if (!strcmp(argv[0], "set")) - return sparse_checkout_set(argc, argv, prefix); - if (!strcmp(argv[0], "add")) - return sparse_checkout_add(argc, argv, prefix); - if (!strcmp(argv[0], "reapply")) - return sparse_checkout_reapply(argc, argv); - if (!strcmp(argv[0], "disable")) - return sparse_checkout_disable(argc, argv); - } - - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); + return fn(argc, argv, prefix); } diff --git a/builtin/stash.c b/builtin/stash.c index 30fa101460..6a12fed271 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1,6 +1,7 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" +#include "hex.h" #include "parse-options.h" #include "refs.h" #include "lockfile.h" @@ -18,75 +19,99 @@ #include "diffcore.h" #include "exec-cmd.h" #include "reflog.h" +#include "add-interactive.h" #define INCLUDE_ALL_FILES 2 +#define BUILTIN_STASH_LIST_USAGE \ + N_("git stash list [<log-options>]") +#define BUILTIN_STASH_SHOW_USAGE \ + N_("git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]") +#define BUILTIN_STASH_DROP_USAGE \ + N_("git stash drop [-q | --quiet] [<stash>]") +#define BUILTIN_STASH_POP_USAGE \ + N_("git stash pop [--index] [-q | --quiet] [<stash>]") +#define BUILTIN_STASH_APPLY_USAGE \ + N_("git stash apply [--index] [-q | --quiet] [<stash>]") +#define BUILTIN_STASH_BRANCH_USAGE \ + N_("git stash branch <branchname> [<stash>]") +#define BUILTIN_STASH_STORE_USAGE \ + N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>") +#define BUILTIN_STASH_PUSH_USAGE \ + N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \ + " [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \ + " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \ + " [--] [<pathspec>...]]") +#define BUILTIN_STASH_SAVE_USAGE \ + N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \ + " [-u | --include-untracked] [-a | --all] [<message>]") +#define BUILTIN_STASH_CREATE_USAGE \ + N_("git stash create [<message>]") +#define BUILTIN_STASH_CLEAR_USAGE \ + "git stash clear" + static const char * const git_stash_usage[] = { - N_("git stash list [<options>]"), - N_("git stash show [<options>] [<stash>]"), - N_("git stash drop [-q|--quiet] [<stash>]"), - N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"), - N_("git stash branch <branchname> [<stash>]"), - "git stash clear", - N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n" - " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" - " [--] [<pathspec>...]]"), - N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [<message>]"), + BUILTIN_STASH_LIST_USAGE, + BUILTIN_STASH_SHOW_USAGE, + BUILTIN_STASH_DROP_USAGE, + BUILTIN_STASH_POP_USAGE, + BUILTIN_STASH_APPLY_USAGE, + BUILTIN_STASH_BRANCH_USAGE, + BUILTIN_STASH_PUSH_USAGE, + BUILTIN_STASH_SAVE_USAGE, + BUILTIN_STASH_CLEAR_USAGE, + BUILTIN_STASH_CREATE_USAGE, + BUILTIN_STASH_STORE_USAGE, NULL }; static const char * const git_stash_list_usage[] = { - N_("git stash list [<options>]"), + BUILTIN_STASH_LIST_USAGE, NULL }; static const char * const git_stash_show_usage[] = { - N_("git stash show [<options>] [<stash>]"), + BUILTIN_STASH_SHOW_USAGE, NULL }; static const char * const git_stash_drop_usage[] = { - N_("git stash drop [-q|--quiet] [<stash>]"), + BUILTIN_STASH_DROP_USAGE, NULL }; static const char * const git_stash_pop_usage[] = { - N_("git stash pop [--index] [-q|--quiet] [<stash>]"), + BUILTIN_STASH_POP_USAGE, NULL }; static const char * const git_stash_apply_usage[] = { - N_("git stash apply [--index] [-q|--quiet] [<stash>]"), + BUILTIN_STASH_APPLY_USAGE, NULL }; static const char * const git_stash_branch_usage[] = { - N_("git stash branch <branchname> [<stash>]"), + BUILTIN_STASH_BRANCH_USAGE, NULL }; static const char * const git_stash_clear_usage[] = { - "git stash clear", + BUILTIN_STASH_CLEAR_USAGE, NULL }; static const char * const git_stash_store_usage[] = { - N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"), + BUILTIN_STASH_STORE_USAGE, NULL }; static const char * const git_stash_push_usage[] = { - N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n" - " [--] [<pathspec>...]]"), + BUILTIN_STASH_PUSH_USAGE, NULL }; static const char * const git_stash_save_usage[] = { - N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [<message>]"), + BUILTIN_STASH_SAVE_USAGE, NULL }; @@ -238,11 +263,11 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) struct tree *tree; struct lock_file lock_file = LOCK_INIT; - read_cache_preload(NULL); - if (refresh_cache(REFRESH_QUIET)) + repo_read_index_preload(the_repository, NULL, 0); + if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL)) return -1; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof(opts)); @@ -431,10 +456,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(active_cache[pos])) { + if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) { struct stat st; - ce = active_cache[pos]; + ce = the_index.cache[pos]; if (!lstat(ce->name, &st)) { /* Conflicting path present; relocate it */ struct strbuf new_path = STRBUF_INIT; @@ -500,11 +525,13 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, struct tree *head, *merge, *merge_base; struct lock_file lock = LOCK_INIT; - read_cache_preload(NULL); - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) + repo_read_index_preload(the_repository, NULL, 0); + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0, + NULL, NULL, NULL)) return -1; - if (write_cache_as_tree(&c_tree, 0, NULL)) + if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0, + NULL)) return error(_("cannot apply a stash in the middle of a merge")); if (index) { @@ -526,14 +553,15 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, return error(_("conflicts in index. " "Try without --index.")); - discard_cache(); - read_cache(); - if (write_cache_as_tree(&index_tree, 0, NULL)) + discard_index(&the_index); + repo_read_index(the_repository); + if (write_index_as_tree(&index_tree, &the_index, + get_index_file(), 0, NULL)) return error(_("could not save index tree")); reset_head(); - discard_cache(); - read_cache(); + discard_index(&the_index); + repo_read_index(the_repository); } } @@ -638,9 +666,12 @@ cleanup: return ret; } -static int reject_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) +static int reject_reflog_ent(struct object_id *ooid UNUSED, + struct object_id *noid UNUSED, + const char *email UNUSED, + timestamp_t timestamp UNUSED, + int tz UNUSED, const char *message UNUSED, + void *cb_data UNUSED) { return 1; } @@ -782,7 +813,7 @@ static int list_stash(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_stash_list_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (!ref_exists(ref_stash)) return 0; @@ -873,7 +904,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) init_revisions(&rev, prefix); argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_DASHDASH); strvec_push(&revision_args, argv[0]); @@ -979,7 +1010,7 @@ static int store_stash(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_stash_store_usage, - PARSE_OPT_KEEP_UNKNOWN); + PARSE_OPT_KEEP_UNKNOWN_OPT); if (argc != 1) { if (!quiet) @@ -1056,7 +1087,7 @@ static int check_changes_tracked_files(const struct pathspec *ps) if (get_oid("HEAD", &dummy)) return -1; - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) return -1; init_revisions(&rev, NULL); @@ -1110,7 +1141,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg, int ret = 0; struct strbuf untracked_msg = STRBUF_INIT; struct child_process cp_upd_index = CHILD_PROCESS_INIT; - struct index_state istate = { NULL }; + struct index_state istate = INDEX_STATE_INIT(the_repository); cp_upd_index.git_cmd = 1; strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add", @@ -1138,7 +1169,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg, } done: - discard_index(&istate); + release_index(&istate); strbuf_release(&untracked_msg); remove_path(stash_index_path.buf); return ret; @@ -1149,7 +1180,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch, { int ret = 0; struct child_process cp_diff_tree = CHILD_PROCESS_INIT; - struct index_state istate = { NULL }; + struct index_state istate = INDEX_STATE_INIT(the_repository); if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file, 0, NULL)) { @@ -1172,7 +1203,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch, } done: - discard_index(&istate); + release_index(&istate); return ret; } @@ -1182,7 +1213,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; struct child_process cp_diff_tree = CHILD_PROCESS_INIT; - struct index_state istate = { NULL }; + struct index_state istate = INDEX_STATE_INIT(the_repository); char *old_index_env = NULL, *old_repo_index_file; remove_path(stash_index_path.buf); @@ -1202,7 +1233,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = run_add_interactive(NULL, "--patch=stash", ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1233,7 +1264,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, } done: - discard_index(&istate); + release_index(&istate); remove_path(stash_index_path.buf); return ret; } @@ -1244,7 +1275,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps struct rev_info rev; struct child_process cp_upd_index = CHILD_PROCESS_INIT; struct strbuf diff_output = STRBUF_INIT; - struct index_state istate = { NULL }; + struct index_state istate = INDEX_STATE_INIT(the_repository); init_revisions(&rev, NULL); copy_pathspec(&rev.prune_data, ps); @@ -1260,7 +1291,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps rev.diffopt.format_callback = add_diff_to_buf; rev.diffopt.format_callback_data = &diff_output; - if (read_cache_preload(&rev.diffopt.pathspec) < 0) { + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { ret = -1; goto done; } @@ -1292,7 +1323,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps } done: - discard_index(&istate); + release_index(&istate); release_revisions(&rev); strbuf_release(&diff_output); remove_path(stash_index_path.buf); @@ -1318,8 +1349,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b prepare_fallback_ident("git stash", "git@stash"); - read_cache_preload(NULL); - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) { + repo_read_index_preload(the_repository, NULL, 0); + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0, + NULL, NULL, NULL) < 0) { ret = -1; goto done; } @@ -1349,7 +1381,8 @@ 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_cache_as_tree(&info->i_tree, 0, NULL) || + if (write_index_as_tree(&info->i_tree, &the_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)) { if (!quiet) @@ -1487,15 +1520,15 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q goto done; } - read_cache_preload(NULL); + repo_read_index_preload(the_repository, NULL, 0); if (!include_untracked && ps->nr) { int i; char *ps_matched = xcalloc(ps->nr, 1); /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&the_index); - for (i = 0; i < active_nr; i++) - ce_path_match(&the_index, active_cache[i], ps, + for (i = 0; i < the_index.cache_nr; i++) + ce_path_match(&the_index, the_index.cache[i], ps, ps_matched); if (report_path_error(ps_matched, ps)) { @@ -1507,7 +1540,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q free(ps_matched); } - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) { + if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0, + NULL, NULL, NULL)) { ret = -1; goto done; } @@ -1564,7 +1598,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q goto done; } } - discard_cache(); + discard_index(&the_index); if (ps->nr) { struct child_process cp_add = CHILD_PROCESS_INIT; struct child_process cp_diff = CHILD_PROCESS_INIT; @@ -1660,8 +1694,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q } done: + strbuf_release(&patch); free_stash_info(&info); strbuf_release(&stash_msg_buf); + strbuf_release(&untracked_files); return ret; } @@ -1696,6 +1732,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END() }; + int ret; if (argc) { force_assume = !strcmp(argv[0], "-p"); @@ -1735,8 +1772,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } - return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, + include_untracked, only_staged); + clear_pathspec(&ps); + return ret; +} + +static int push_stash_unassumed(int argc, const char **argv, const char *prefix) +{ + return push_stash(argc, argv, prefix, 0); } static int save_stash(int argc, const char **argv, const char *prefix) @@ -1787,15 +1831,28 @@ int cmd_stash(int argc, const char **argv, const char *prefix) pid_t pid = getpid(); const char *index_file; struct strvec args = STRVEC_INIT; - + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("apply", &fn, apply_stash), + OPT_SUBCOMMAND("clear", &fn, clear_stash), + OPT_SUBCOMMAND("drop", &fn, drop_stash), + OPT_SUBCOMMAND("pop", &fn, pop_stash), + OPT_SUBCOMMAND("branch", &fn, branch_stash), + OPT_SUBCOMMAND("list", &fn, list_stash), + OPT_SUBCOMMAND("show", &fn, show_stash), + OPT_SUBCOMMAND("store", &fn, store_stash), + OPT_SUBCOMMAND("create", &fn, create_stash), + OPT_SUBCOMMAND("push", &fn, push_stash_unassumed), + OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; git_config(git_stash_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, - PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); + PARSE_OPT_SUBCOMMAND_OPTIONAL | + PARSE_OPT_KEEP_UNKNOWN_OPT | + PARSE_OPT_KEEP_DASHDASH); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1804,33 +1861,10 @@ int cmd_stash(int argc, const char **argv, const char *prefix) strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, (uintmax_t)pid); - if (!argc) - return !!push_stash(0, NULL, prefix, 0); - else if (!strcmp(argv[0], "apply")) - return !!apply_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "clear")) - return !!clear_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "drop")) - return !!drop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "pop")) - return !!pop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "branch")) - return !!branch_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "list")) - return !!list_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - return !!show_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "store")) - return !!store_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "create")) - return !!create_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "push")) - return !!push_stash(argc, argv, prefix, 0); - else if (!strcmp(argv[0], "save")) - return !!save_stash(argc, argv, prefix); - else if (*argv[0] != '-') - usage_msg_optf(_("unknown subcommand: %s"), - git_stash_usage, options, argv[0]); + if (fn) + return !!fn(argc, argv, prefix); + else if (!argc) + return !!push_stash_unassumed(0, NULL, prefix); /* Assume 'stash push' */ strvec_push(&args, "push"); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 5c77dfcffe..d05d1a8462 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,5 +1,7 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" +#include "alloc.h" +#include "hex.h" #include "repository.h" #include "cache.h" #include "config.h" @@ -31,45 +33,61 @@ typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); -static char *repo_get_default_remote(struct repository *repo) +static int repo_get_default_remote(struct repository *repo, char **default_remote) { - char *dest = NULL, *ret; + char *dest = NULL; struct strbuf sb = STRBUF_INIT; struct ref_store *store = get_main_ref_store(repo); const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL, NULL); if (!refname) - die(_("No such ref: %s"), "HEAD"); + return die_message(_("No such ref: %s"), "HEAD"); /* detached HEAD */ - if (!strcmp(refname, "HEAD")) - return xstrdup("origin"); + if (!strcmp(refname, "HEAD")) { + *default_remote = xstrdup("origin"); + return 0; + } if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); + return die_message(_("Expecting a full ref name, got %s"), + refname); strbuf_addf(&sb, "branch.%s.remote", refname); if (repo_config_get_string(repo, sb.buf, &dest)) - ret = xstrdup("origin"); + *default_remote = xstrdup("origin"); else - ret = dest; + *default_remote = dest; strbuf_release(&sb); - return ret; + return 0; } -static char *get_default_remote_submodule(const char *module_path) +static int get_default_remote_submodule(const char *module_path, char **default_remote) { struct repository subrepo; + int ret; + + if (repo_submodule_init(&subrepo, the_repository, module_path, + null_oid()) < 0) + return die_message(_("could not get a repository handle for submodule '%s'"), + module_path); + ret = repo_get_default_remote(&subrepo, default_remote); + repo_clear(&subrepo); - repo_submodule_init(&subrepo, the_repository, module_path, null_oid()); - return repo_get_default_remote(&subrepo); + return ret; } static char *get_default_remote(void) { - return repo_get_default_remote(the_repository); + char *default_remote; + int code = repo_get_default_remote(the_repository, &default_remote); + + if (code) + exit(code); + + return default_remote; } static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet) @@ -96,31 +114,9 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int return resolved_url; } -static int resolve_relative_url_test(int argc, const char **argv, const char *prefix) -{ - char *remoteurl, *res; - const char *up_path, *url; - - if (argc != 4) - die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>"); - - up_path = argv[1]; - remoteurl = xstrdup(argv[2]); - url = argv[3]; - - if (!strcmp(up_path, "(null)")) - up_path = NULL; - - res = relative_url(remoteurl, url, up_path); - puts(res); - free(res); - free(remoteurl); - return 0; -} - -static char *do_get_submodule_displaypath(const char *path, - const char *prefix, - const char *super_prefix) +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix, + const char *super_prefix) { if (prefix && super_prefix) { BUG("cannot have prefix '%s' and superprefix '%s'", @@ -137,13 +133,6 @@ static char *do_get_submodule_displaypath(const char *path, } } -/* the result should be freed by the caller. */ -static char *get_submodule_displaypath(const char *path, const char *prefix) -{ - const char *super_prefix = get_super_prefix(); - return do_get_submodule_displaypath(path, prefix, super_prefix); -} - static char *compute_rev_name(const char *sub_path, const char* object_id) { struct strbuf sb = STRBUF_INIT; @@ -188,13 +177,19 @@ struct module_list { }; #define MODULE_LIST_INIT { 0 } -static int module_list_compute(int argc, const char **argv, +static void module_list_release(struct module_list *ml) +{ + free(ml->entries); +} + +static int module_list_compute(const char **argv, const char *prefix, struct pathspec *pathspec, struct module_list *list) { int i, result = 0; char *ps_matched = NULL; + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); @@ -202,11 +197,11 @@ static int module_list_compute(int argc, const char **argv, if (pathspec->nr) ps_matched = xcalloc(pathspec->nr, 1); - if (read_cache() < 0) + if (repo_read_index(the_repository) < 0) die(_("index file corrupt")); - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < the_index.cache_nr; i++) { + const struct cache_entry *ce = the_index.cache[i]; if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce), 0, ps_matched, 1) || @@ -215,8 +210,8 @@ static int module_list_compute(int argc, const char **argv, ALLOC_GROW(list->entries, list->nr + 1, list->alloc); list->entries[list->nr++] = ce; - while (i + 1 < active_nr && - !strcmp(ce->name, active_cache[i + 1]->name)) + while (i + 1 < the_index.cache_nr && + !strcmp(ce->name, the_index.cache[i + 1]->name)) /* * Skip entries with the same name in different stages * to make sure an entry is returned only once. @@ -249,7 +244,7 @@ static void module_list_active(struct module_list *list) active_modules.entries[active_modules.nr++] = ce; } - free(list->entries); + module_list_release(list); *list = active_modules; } @@ -272,49 +267,11 @@ static char *get_up_path(const char *path) return strbuf_detach(&sb, NULL); } -static int module_list(int argc, const char **argv, const char *prefix) -{ - int i; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - - struct option module_list_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("alternative anchor for relative paths")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper list [--prefix=<path>] [<path>...]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_list_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - for (i = 0; i < list.nr; i++) { - const struct cache_entry *ce = list.entries[i]; - - if (ce_stage(ce)) - printf("%06o %s U\t", ce->ce_mode, - oid_to_hex(null_oid())); - else - printf("%06o %s %d\t", ce->ce_mode, - oid_to_hex(&ce->oid), ce_stage(ce)); - - fprintf(stdout, "%s\n", ce->name); - } - return 0; -} - static void for_each_listed_submodule(const struct module_list *list, each_submodule_fn fn, void *cb_data) { int i; + for (i = 0; i < list->nr; i++) fn(list->entries[i], cb_data); } @@ -323,6 +280,7 @@ struct foreach_cb { int argc; const char **argv; const char *prefix; + const char *super_prefix; int quiet; int recursive; }; @@ -334,12 +292,12 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, struct foreach_cb *info = cb_data; const char *path = list_item->name; const struct object_id *ce_oid = &list_item->oid; - const struct submodule *sub; struct child_process cp = CHILD_PROCESS_INIT; char *displaypath; - displaypath = get_submodule_displaypath(path, info->prefix); + displaypath = get_submodule_displaypath(path, info->prefix, + info->super_prefix); sub = submodule_from_path(the_repository, null_oid(), path); @@ -409,10 +367,10 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, cpr.dir = path; prepare_submodule_repo_env(&cpr.env); - strvec_pushl(&cpr.args, "--super-prefix", NULL); - strvec_pushf(&cpr.args, "%s/", displaypath); strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive", NULL); + strvec_pushl(&cpr.args, "--super-prefix", NULL); + strvec_pushf(&cpr.args, "%s/", displaypath); if (info->quiet) strvec_push(&cpr.args, "--quiet"); @@ -433,26 +391,26 @@ cleanup: static int module_foreach(int argc, const char **argv, const char *prefix) { struct foreach_cb info = FOREACH_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; - struct option module_foreach_options[] = { + OPT__SUPER_PREFIX(&info.super_prefix), OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")), OPT_BOOL(0, "recursive", &info.recursive, N_("recurse into nested submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"), + N_("git submodule foreach [--quiet] [--recursive] [--] <command>"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_foreach_options, git_submodule_helper_usage, 0); - if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(NULL, prefix, &pathspec, &list) < 0) + goto cleanup; info.argc = argc; info.argv = argv; @@ -460,7 +418,11 @@ static int module_foreach(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } static int starts_with_dot_slash(const char *const path) @@ -477,22 +439,21 @@ static int starts_with_dot_dot_slash(const char *const path) struct init_cb { const char *prefix; - const char *superprefix; + const char *super_prefix; unsigned int flags; }; #define INIT_CB_INIT { 0 } static void init_submodule(const char *path, const char *prefix, - const char *superprefix, unsigned int flags) + const char *super_prefix, + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; - char *upd = NULL, *url = NULL, *displaypath; + const char *upd; + char *url = NULL, *displaypath; - /* try superprefix from the environment, if it is not passed explicitly */ - if (!superprefix) - superprefix = get_super_prefix(); - displaypath = do_get_submodule_displaypath(path, prefix, superprefix); + displaypath = get_submodule_displaypath(path, prefix, super_prefix); sub = submodule_from_path(the_repository, null_oid(), path); @@ -529,6 +490,7 @@ static void init_submodule(const char *path, const char *prefix, if (starts_with_dot_dot_slash(url) || starts_with_dot_slash(url)) { char *oldurl = url; + url = resolve_relative_url(oldurl, NULL, 0); free(oldurl); } @@ -545,14 +507,15 @@ static void init_submodule(const char *path, const char *prefix, /* Copy "update" setting when it is not set yet */ strbuf_addf(&sb, "submodule.%s.update", sub->name); - if (git_config_get_string(sb.buf, &upd) && + if (git_config_get_string_tmp(sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { if (sub->update_strategy.type == SM_UPDATE_COMMAND) { fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"), sub->name); - upd = xstrdup("none"); - } else - upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy)); + upd = "none"; + } else { + upd = submodule_update_type_to_string(sub->update_strategy.type); + } if (git_config_set_gently(sb.buf, upd)) die(_("Failed to register update mode for submodule path '%s'"), displaypath); @@ -560,37 +523,37 @@ static void init_submodule(const char *path, const char *prefix, strbuf_release(&sb); free(displaypath); free(url); - free(upd); } static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct init_cb *info = cb_data; - init_submodule(list_item->name, info->prefix, info->superprefix, info->flags); + + init_submodule(list_item->name, info->prefix, info->super_prefix, + info->flags); } static int module_init(int argc, const char **argv, const char *prefix) { struct init_cb info = INIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - struct option module_init_options[] = { OPT__QUIET(&quiet, N_("suppress output for initializing a submodule")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper init [<options>] [<path>]"), + N_("git submodule init [<options>] [<path>]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_init_options, git_submodule_helper_usage, 0); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, prefix, &pathspec, &list) < 0) + goto cleanup; /* * If there are no path args and submodule.active is set then, @@ -605,11 +568,16 @@ static int module_init(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, init_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct status_cb { const char *prefix; + const char *super_prefix; unsigned int flags; }; #define STATUS_CB_INIT { 0 } @@ -623,20 +591,23 @@ static void print_status(unsigned int flags, char state, const char *path, printf("%c%s %s", state, oid_to_hex(oid), displaypath); if (state == ' ' || state == '+') { - const char *name = compute_rev_name(path, oid_to_hex(oid)); + char *name = compute_rev_name(path, oid_to_hex(oid)); if (name) printf(" (%s)", name); + free(name); } printf("\n"); } -static int handle_submodule_head_ref(const char *refname, - const struct object_id *oid, int flags, +static int handle_submodule_head_ref(const char *refname UNUSED, + const struct object_id *oid, + int flags UNUSED, void *cb_data) { struct object_id *output = cb_data; + if (oid) oidcpy(output, oid); @@ -645,7 +616,7 @@ static int handle_submodule_head_ref(const char *refname, static void status_submodule(const char *path, const struct object_id *ce_oid, unsigned int ce_flags, const char *prefix, - unsigned int flags) + const char *super_prefix, unsigned int flags) { char *displaypath; struct strvec diff_files_args = STRVEC_INIT; @@ -653,12 +624,15 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, int diff_files_result; struct strbuf buf = STRBUF_INIT; const char *git_dir; + struct setup_revision_opt opt = { + .free_removed_argv_elements = 1, + }; if (!submodule_from_path(the_repository, null_oid(), path)) die(_("no submodule mapping found in .gitmodules for path '%s'"), path); - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(path, prefix, super_prefix); if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { print_status(flags, 'U', path, null_oid(), displaypath); @@ -686,9 +660,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = 0; - diff_files_args.nr = setup_revisions(diff_files_args.nr, - diff_files_args.v, - &rev, NULL); + setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt); diff_files_result = run_diff_files(&rev, 0); if (!diff_result_code(&rev.diffopt, diff_files_result)) { @@ -718,10 +690,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, cpr.dir = path; prepare_submodule_repo_env(&cpr.env); - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); strvec_pushl(&cpr.args, "submodule--helper", "status", "--recursive", NULL); + strvec_push(&cpr.args, "--super-prefix"); + strvec_pushf(&cpr.args, "%s/", displaypath); if (flags & OPT_CACHED) strvec_push(&cpr.args, "--cached"); @@ -743,34 +715,35 @@ static void status_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct status_cb *info = cb_data; + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, - info->prefix, info->flags); + info->prefix, info->super_prefix, info->flags); } static int module_status(int argc, const char **argv, const char *prefix) { struct status_cb info = STATUS_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - struct option module_status_options[] = { + OPT__SUPER_PREFIX(&info.super_prefix), OPT__QUIET(&quiet, N_("suppress submodule status output")), OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_status_options, git_submodule_helper_usage, 0); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, prefix, &pathspec, &list) < 0) + goto cleanup; info.prefix = prefix; if (quiet) @@ -778,25 +751,11 @@ static int module_status(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, status_submodule_cb, &info); - return 0; -} - -static int module_name(int argc, const char **argv, const char *prefix) -{ - const struct submodule *sub; - - if (argc != 2) - usage(_("git submodule--helper name <path>")); - - sub = submodule_from_path(the_repository, null_oid(), argv[1]); - - if (!sub) - die(_("no submodule mapping found in .gitmodules for path '%s'"), - argv[1]); - - printf("%s\n", sub->name); - - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct module_cb { @@ -805,20 +764,39 @@ struct module_cb { struct object_id oid_src; struct object_id oid_dst; char status; - const char *sm_path; + char *sm_path; }; #define MODULE_CB_INIT { 0 } +static void module_cb_release(struct module_cb *mcb) +{ + free(mcb->sm_path); +} + struct module_cb_list { struct module_cb **entries; int alloc, nr; }; #define MODULE_CB_LIST_INIT { 0 } +static void module_cb_list_release(struct module_cb_list *mcbl) +{ + int i; + + for (i = 0; i < mcbl->nr; i++) { + struct module_cb *mcb = mcbl->entries[i]; + + module_cb_release(mcb); + free(mcb); + } + free(mcbl->entries); +} + struct summary_cb { int argc; const char **argv; const char *prefix; + const char *super_prefix; unsigned int cached: 1; unsigned int for_status: 1; unsigned int files: 1; @@ -851,7 +829,7 @@ static char *verify_submodule_committish(const char *sm_path, return strbuf_detach(&result, NULL); } -static void print_submodule_summary(struct summary_cb *info, char *errmsg, +static void print_submodule_summary(struct summary_cb *info, const char *errmsg, int total_commits, const char *displaypath, const char *src_abbrev, const char *dst_abbrev, struct module_cb *p) @@ -909,12 +887,13 @@ static void generate_submodule_summary(struct summary_cb *info, { char *displaypath, *src_abbrev = NULL, *dst_abbrev; int missing_src = 0, missing_dst = 0; - char *errmsg = NULL; + struct strbuf errmsg = STRBUF_INIT; int total_commits = -1; if (!info->cached && oideq(&p->oid_dst, null_oid())) { if (S_ISGITLINK(p->mod_dst)) { struct ref_store *refs = get_submodule_ref_store(p->sm_path); + if (refs) refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst); } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) { @@ -979,7 +958,8 @@ static void generate_submodule_summary(struct summary_cb *info, dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7); } - displaypath = get_submodule_displaypath(p->sm_path, info->prefix); + displaypath = get_submodule_displaypath(p->sm_path, info->prefix, + info->super_prefix); if (!missing_src && !missing_dst) { struct child_process cp_rev_list = CHILD_PROCESS_INIT; @@ -1009,28 +989,27 @@ static void generate_submodule_summary(struct summary_cb *info, * submodule, i.e., deleted or changed to blob */ if (S_ISGITLINK(p->mod_dst)) { - struct strbuf errmsg_str = STRBUF_INIT; if (missing_src && missing_dst) { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n", + strbuf_addf(&errmsg, " Warn: %s doesn't contain commits %s and %s\n", displaypath, oid_to_hex(&p->oid_src), oid_to_hex(&p->oid_dst)); } else { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n", + strbuf_addf(&errmsg, " Warn: %s doesn't contain commit %s\n", displaypath, missing_src ? oid_to_hex(&p->oid_src) : oid_to_hex(&p->oid_dst)); } - errmsg = strbuf_detach(&errmsg_str, NULL); } } - print_submodule_summary(info, errmsg, total_commits, - displaypath, src_abbrev, + print_submodule_summary(info, errmsg.len ? errmsg.buf : NULL, + total_commits, displaypath, src_abbrev, dst_abbrev, p); free(displaypath); free(src_abbrev); free(dst_abbrev); + strbuf_release(&errmsg); } static void prepare_submodule_summary(struct summary_cb *info, @@ -1075,7 +1054,7 @@ static void prepare_submodule_summary(struct summary_cb *info, } static void submodule_summary_callback(struct diff_queue_struct *q, - struct diff_options *options, + struct diff_options *options UNUSED, void *data) { int i; @@ -1114,6 +1093,9 @@ static int compute_summary_module_list(struct object_id *head_oid, { struct strvec diff_args = STRVEC_INIT; struct rev_info rev; + struct setup_revision_opt opt = { + .free_removed_argv_elements = 1, + }; struct module_cb_list list = MODULE_CB_LIST_INIT; int ret = 0; @@ -1131,7 +1113,7 @@ static int compute_summary_module_list(struct object_id *head_oid, init_revisions(&rev, info->prefix); rev.abbrev = 0; precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); - setup_revisions(diff_args.nr, diff_args.v, &rev, NULL); + setup_revisions(diff_args.nr, diff_args.v, &rev, &opt); rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = submodule_summary_callback; rev.diffopt.format_callback_data = &list; @@ -1139,13 +1121,13 @@ static int compute_summary_module_list(struct object_id *head_oid, if (!info->cached) { if (diff_cmd == DIFF_INDEX) setup_work_tree(); - if (read_cache_preload(&rev.diffopt.pathspec) < 0) { - perror("read_cache_preload"); + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { + perror("repo_read_index_preload"); ret = -1; goto cleanup; } - } else if (read_cache() < 0) { - perror("read_cache"); + } else if (repo_read_index(the_repository) < 0) { + perror("repo_read_cache"); ret = -1; goto cleanup; } @@ -1158,6 +1140,7 @@ static int compute_summary_module_list(struct object_id *head_oid, cleanup: strvec_clear(&diff_args); release_revisions(&rev); + module_cb_list_release(&list); return ret; } @@ -1171,7 +1154,6 @@ static int module_summary(int argc, const char **argv, const char *prefix) enum diff_cmd diff_cmd = DIFF_INDEX; struct object_id head_oid; int ret; - struct option module_summary_options[] = { OPT_BOOL(0, "cached", &cached, N_("use the commit stored in the index instead of the submodule HEAD")), @@ -1183,9 +1165,8 @@ static int module_summary(int argc, const char **argv, const char *prefix) N_("limit the summary size")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper summary [<options>] [<commit>] [--] [<path>]"), + N_("git submodule summary [<options>] [<commit>] [--] [<path>]"), NULL }; @@ -1233,18 +1214,20 @@ static int module_summary(int argc, const char **argv, const char *prefix) struct sync_cb { const char *prefix; + const char *super_prefix; unsigned int flags; }; #define SYNC_CB_INIT { 0 } static void sync_submodule(const char *path, const char *prefix, - unsigned int flags) + const char *super_prefix, unsigned int flags) { const struct submodule *sub; char *remote_key = NULL; char *sub_origin_url, *super_config_url, *displaypath, *default_remote; struct strbuf sb = STRBUF_INIT; char *sub_config_path = NULL; + int code; if (!is_submodule_active(the_repository, path)) return; @@ -1255,6 +1238,7 @@ static void sync_submodule(const char *path, const char *prefix, if (starts_with_dot_dot_slash(sub->url) || starts_with_dot_slash(sub->url)) { char *up_path = get_up_path(path); + sub_origin_url = resolve_relative_url(sub->url, up_path, 1); super_config_url = resolve_relative_url(sub->url, NULL, 1); free(up_path); @@ -1267,7 +1251,7 @@ static void sync_submodule(const char *path, const char *prefix, super_config_url = xstrdup(""); } - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(path, prefix, super_prefix); if (!(flags & OPT_QUIET)) printf(_("Synchronizing submodule url for '%s'\n"), @@ -1283,10 +1267,9 @@ static void sync_submodule(const char *path, const char *prefix, goto cleanup; strbuf_reset(&sb); - default_remote = get_default_remote_submodule(path); - if (!default_remote) - die(_("failed to get the default remote for submodule '%s'"), - path); + code = get_default_remote_submodule(path, &default_remote); + if (code) + exit(code); remote_key = xstrfmt("remote.%s.url", default_remote); free(default_remote); @@ -1305,10 +1288,11 @@ static void sync_submodule(const char *path, const char *prefix, cpr.dir = path; prepare_submodule_repo_env(&cpr.env); - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); strvec_pushl(&cpr.args, "submodule--helper", "sync", "--recursive", NULL); + strvec_push(&cpr.args, "--super-prefix"); + strvec_pushf(&cpr.args, "%s/", displaypath); + if (flags & OPT_QUIET) strvec_push(&cpr.args, "--quiet"); @@ -1330,34 +1314,36 @@ cleanup: static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data) { struct sync_cb *info = cb_data; - sync_submodule(list_item->name, info->prefix, info->flags); + + sync_submodule(list_item->name, info->prefix, info->super_prefix, + info->flags); } static int module_sync(int argc, const char **argv, const char *prefix) { struct sync_cb info = SYNC_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int recursive = 0; - struct option module_sync_options[] = { + OPT__SUPER_PREFIX(&info.super_prefix), OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")), OPT_BOOL(0, "recursive", &recursive, N_("recurse into nested submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"), + N_("git submodule sync [--quiet] [--recursive] [<path>]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_sync_options, git_submodule_helper_usage, 0); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, prefix, &pathspec, &list) < 0) + goto cleanup; info.prefix = prefix; if (quiet) @@ -1367,7 +1353,11 @@ static int module_sync(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, sync_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct deinit_cb { @@ -1390,7 +1380,7 @@ static void deinit_submodule(const char *path, const char *prefix, if (!sub || !sub->name) goto cleanup; - displaypath = get_submodule_displaypath(path, prefix); + displaypath = get_submodule_displaypath(path, prefix, NULL); /* remove the submodule work tree (unless the user already did it) */ if (is_directory(path)) { @@ -1404,13 +1394,13 @@ static void deinit_submodule(const char *path, const char *prefix, ".git file by using absorbgitdirs."), displaypath); - absorb_git_dir_into_superproject(path, - ABSORB_GITDIR_RECURSE_SUBMODULES); + absorb_git_dir_into_superproject(path, NULL); } if (!(flags & OPT_FORCE)) { struct child_process cp_rm = CHILD_PROCESS_INIT; + cp_rm.git_cmd = 1; strvec_pushl(&cp_rm.args, "rm", "-qn", path, NULL); @@ -1447,6 +1437,7 @@ static void deinit_submodule(const char *path, const char *prefix, /* remove the .git/config entries (unless the user already did it) */ if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) { char *sub_key = xstrfmt("submodule.%s", sub->name); + /* * remove the whole section so we have a clean state when * the user later decides to init this submodule again @@ -1474,23 +1465,22 @@ static void deinit_submodule_cb(const struct cache_entry *list_item, static int module_deinit(int argc, const char **argv, const char *prefix) { struct deinit_cb info = DEINIT_CB_INIT; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; int quiet = 0; int force = 0; int all = 0; - struct option module_deinit_options[] = { OPT__QUIET(&quiet, N_("suppress submodule status output")), OPT__FORCE(&force, N_("remove submodule working trees even if they contain local changes"), 0), OPT_BOOL(0, "all", &all, N_("unregister all submodules")), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, module_deinit_options, git_submodule_helper_usage, 0); @@ -1504,8 +1494,8 @@ static int module_deinit(int argc, const char **argv, const char *prefix) if (!argc && !all) die(_("Use '--all' if you really want to deinitialize all submodules")); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, prefix, &pathspec, &list) < 0) + goto cleanup; info.prefix = prefix; if (quiet) @@ -1515,7 +1505,11 @@ static int module_deinit(int argc, const char **argv, const char *prefix) for_each_listed_submodule(&list, deinit_submodule_cb, &info); - return 0; + ret = 0; +cleanup: + module_list_release(&list); + clear_pathspec(&pathspec); + return ret; } struct module_clone_data { @@ -1525,7 +1519,6 @@ struct module_clone_data { const char *url; const char *depth; struct list_objects_filter_options *filter_options; - struct string_list reference; unsigned int quiet: 1; unsigned int progress: 1; unsigned int dissociate: 1; @@ -1533,7 +1526,6 @@ struct module_clone_data { int single_branch; }; #define MODULE_CLONE_DATA_INIT { \ - .reference = STRING_LIST_INIT_NODUP, \ .single_branch = -1, \ } @@ -1574,7 +1566,9 @@ static int add_possible_reference_from_superproject( struct strbuf err = STRBUF_INIT; strbuf_add(&sb, odb->path, len); - repo_init(&alternate, sb.buf, NULL); + if (repo_init(&alternate, sb.buf, NULL) < 0) + die(_("could not get a repository handle for gitdir '%s'"), + sb.buf); /* * We need to end the new path with '/' to mark it as a dir, @@ -1589,7 +1583,9 @@ static int add_possible_reference_from_superproject( sm_alternate = compute_alternate_path(sb.buf, &err); if (sm_alternate) { - string_list_append(sas->reference, xstrdup(sb.buf)); + char *p = strbuf_detach(&sb, NULL); + + string_list_append(sas->reference, p)->util = p; free(sm_alternate); } else { switch (sas->error_mode) { @@ -1648,23 +1644,31 @@ static void prepare_possible_alternates(const char *sm_name, free(error_strategy); } -static int clone_submodule(struct module_clone_data *clone_data) +static char *clone_submodule_sm_gitdir(const char *name) { - char *p, *sm_gitdir; - char *sm_alternate = NULL, *error_strategy = NULL; struct strbuf sb = STRBUF_INIT; - struct child_process cp = CHILD_PROCESS_INIT; + char *sm_gitdir; - submodule_name_to_gitdir(&sb, the_repository, clone_data->name); + submodule_name_to_gitdir(&sb, the_repository, name); sm_gitdir = absolute_pathdup(sb.buf); - strbuf_reset(&sb); + strbuf_release(&sb); - if (!is_absolute_path(clone_data->path)) { - strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path); - clone_data->path = strbuf_detach(&sb, NULL); - } else { - clone_data->path = xstrdup(clone_data->path); - } + return sm_gitdir; +} + +static int clone_submodule(const struct module_clone_data *clone_data, + struct string_list *reference) +{ + char *p; + char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name); + char *sm_alternate = NULL, *error_strategy = NULL; + struct child_process cp = CHILD_PROCESS_INIT; + const char *clone_data_path = clone_data->path; + char *to_free = NULL; + + if (!is_absolute_path(clone_data->path)) + clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(), + clone_data->path); if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) die(_("refusing to create/use '%s' in another submodule's " @@ -1674,7 +1678,7 @@ static int clone_submodule(struct module_clone_data *clone_data) if (safe_create_leading_directories_const(sm_gitdir) < 0) die(_("could not create directory '%s'"), sm_gitdir); - prepare_possible_alternates(clone_data->name, &clone_data->reference); + prepare_possible_alternates(clone_data->name, reference); strvec_push(&cp.args, "clone"); strvec_push(&cp.args, "--no-checkout"); @@ -1684,9 +1688,10 @@ static int clone_submodule(struct module_clone_data *clone_data) strvec_push(&cp.args, "--progress"); if (clone_data->depth && *(clone_data->depth)) strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL); - if (clone_data->reference.nr) { + if (reference->nr) { struct string_list_item *item; - for_each_string_list_item(item, &clone_data->reference) + + for_each_string_list_item(item, reference) strvec_pushl(&cp.args, "--reference", item->string, NULL); } @@ -1705,7 +1710,7 @@ static int clone_submodule(struct module_clone_data *clone_data) strvec_push(&cp.args, "--"); strvec_push(&cp.args, clone_data->url); - strvec_push(&cp.args, clone_data->path); + strvec_push(&cp.args, clone_data_path); cp.git_cmd = 1; prepare_submodule_repo_env(&cp.env); @@ -1713,23 +1718,25 @@ static int clone_submodule(struct module_clone_data *clone_data) if(run_command(&cp)) die(_("clone of '%s' into submodule path '%s' failed"), - clone_data->url, clone_data->path); + clone_data->url, clone_data_path); } else { - if (clone_data->require_init && !access(clone_data->path, X_OK) && - !is_empty_dir(clone_data->path)) - die(_("directory not empty: '%s'"), clone_data->path); - if (safe_create_leading_directories_const(clone_data->path) < 0) - die(_("could not create directory '%s'"), clone_data->path); - strbuf_addf(&sb, "%s/index", sm_gitdir); - unlink_or_warn(sb.buf); - strbuf_reset(&sb); + char *path; + + if (clone_data->require_init && !access(clone_data_path, X_OK) && + !is_empty_dir(clone_data_path)) + die(_("directory not empty: '%s'"), clone_data_path); + if (safe_create_leading_directories_const(clone_data_path) < 0) + die(_("could not create directory '%s'"), clone_data_path); + path = xstrfmt("%s/index", sm_gitdir); + unlink_or_warn(path); + free(path); } - connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0); + connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0); - p = git_pathdup_submodule(clone_data->path, "config"); + p = git_pathdup_submodule(clone_data_path, "config"); if (!p) - die(_("could not get submodule directory for '%s'"), clone_data->path); + die(_("could not get submodule directory for '%s'"), clone_data_path); /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */ git_config_get_string("submodule.alternateLocation", &sm_alternate); @@ -1744,9 +1751,9 @@ static int clone_submodule(struct module_clone_data *clone_data) free(sm_alternate); free(error_strategy); - strbuf_release(&sb); free(sm_gitdir); free(p); + free(to_free); return 0; } @@ -1754,7 +1761,9 @@ static int module_clone(int argc, const char **argv, const char *prefix) { int dissociate = 0, quiet = 0, progress = 0, require_init = 0; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; - struct list_objects_filter_options filter_options; + struct string_list reference = STRING_LIST_INIT_NODUP; + struct list_objects_filter_options filter_options = + LIST_OBJECTS_FILTER_INIT; struct option module_clone_options[] = { OPT_STRING(0, "prefix", &clone_data.prefix, @@ -1769,7 +1778,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_STRING(0, "url", &clone_data.url, N_("string"), N_("url where to clone the submodule from")), - OPT_STRING_LIST(0, "reference", &clone_data.reference, + OPT_STRING_LIST(0, "reference", &reference, N_("repo"), N_("reference repository")), OPT_BOOL(0, "dissociate", &dissociate, @@ -1787,7 +1796,6 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule--helper clone [--prefix=<path>] [--quiet] " "[--reference <repository>] [--name <name>] [--depth <depth>] " @@ -1796,7 +1804,6 @@ static int module_clone(int argc, const char **argv, const char *prefix) NULL }; - memset(&filter_options, 0, sizeof(filter_options)); argc = parse_options(argc, argv, prefix, module_clone_options, git_submodule_helper_usage, 0); @@ -1810,31 +1817,33 @@ static int module_clone(int argc, const char **argv, const char *prefix) usage_with_options(git_submodule_helper_usage, module_clone_options); - clone_submodule(&clone_data); + clone_submodule(&clone_data, &reference); list_objects_filter_release(&filter_options); + string_list_clear(&reference, 1); return 0; } -static void determine_submodule_update_strategy(struct repository *r, - int just_cloned, - const char *path, - const char *update, - struct submodule_update_strategy *out) +static int determine_submodule_update_strategy(struct repository *r, + int just_cloned, + const char *path, + enum submodule_update_type update, + struct submodule_update_strategy *out) { const struct submodule *sub = submodule_from_path(r, null_oid(), path); char *key; const char *val; + int ret; key = xstrfmt("submodule.%s.update", sub->name); if (update) { - if (parse_submodule_update_strategy(update, out) < 0) - die(_("Invalid update mode '%s' for submodule path '%s'"), - update, path); + out->type = update; } else if (!repo_config_get_string_tmp(r, key, &val)) { - if (parse_submodule_update_strategy(val, out) < 0) - die(_("Invalid update mode '%s' configured for submodule path '%s'"), - val, path); + if (parse_submodule_update_strategy(val, out) < 0) { + ret = die_message(_("Invalid update mode '%s' configured for submodule path '%s'"), + val, path); + goto cleanup; + } } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { if (sub->update_strategy.type == SM_UPDATE_COMMAND) BUG("how did we read update = !command from .gitmodules?"); @@ -1849,7 +1858,10 @@ static void determine_submodule_update_strategy(struct repository *r, out->type == SM_UPDATE_NONE)) out->type = SM_UPDATE_CHECKOUT; + ret = 0; +cleanup: free(key); + return ret; } struct update_clone_data { @@ -1863,7 +1875,7 @@ struct submodule_update_clone { int current; /* configuration parameters which are passed on to the children */ - struct update_data *update_data; + const struct update_data *update_data; /* to be consumed by update_submodule() */ struct update_clone_data *update_clone; @@ -1878,11 +1890,17 @@ struct submodule_update_clone { }; #define SUBMODULE_UPDATE_CLONE_INIT { 0 } +static void submodule_update_clone_release(struct submodule_update_clone *suc) +{ + free(suc->update_clone); + free(suc->failed_clones); +} + struct update_data { const char *prefix; - const char *recursive_prefix; - const char *displaypath; - const char *update_default; + const char *super_prefix; + char *displaypath; + enum submodule_update_type update_default; struct object_id suboid; struct string_list references; struct submodule_update_strategy update_strategy; @@ -1917,6 +1935,12 @@ struct update_data { .max_jobs = 1, \ } +static void update_data_release(struct update_data *ud) +{ + free(ud->displaypath); + module_list_release(&ud->list); +} + static void next_submodule_warn_missing(struct submodule_update_clone *suc, struct strbuf *out, const char *displaypath) { @@ -1949,30 +1973,21 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, const char *update_string; enum submodule_update_type update_type; char *key; - struct strbuf displaypath_sb = STRBUF_INIT; + const struct update_data *ud = suc->update_data; + char *displaypath = get_submodule_displaypath(ce->name, ud->prefix, + ud->super_prefix); struct strbuf sb = STRBUF_INIT; - const char *displaypath = NULL; int needs_cloning = 0; int need_free_url = 0; if (ce_stage(ce)) { - if (suc->update_data->recursive_prefix) - strbuf_addf(&sb, "%s/%s", suc->update_data->recursive_prefix, ce->name); - else - strbuf_addstr(&sb, ce->name); - strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf); + strbuf_addf(out, _("Skipping unmerged submodule %s"), displaypath); strbuf_addch(out, '\n'); goto cleanup; } sub = submodule_from_path(the_repository, null_oid(), ce->name); - if (suc->update_data->recursive_prefix) - displaypath = relative_path(suc->update_data->recursive_prefix, - ce->name, &displaypath_sb); - else - displaypath = ce->name; - if (!sub) { next_submodule_warn_missing(suc, out, displaypath); goto cleanup; @@ -2051,6 +2066,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, strvec_pushl(&child->args, "--url", url, NULL); if (suc->update_data->references.nr) { struct string_list_item *item; + for_each_string_list_item(item, &suc->update_data->references) strvec_pushl(&child->args, "--reference", item->string, NULL); } @@ -2062,7 +2078,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, "--no-single-branch"); cleanup: - strbuf_release(&displaypath_sb); + free(displaypath); strbuf_release(&sb); if (need_free_url) free((void*)url); @@ -2083,6 +2099,7 @@ static int update_clone_get_next_task(struct child_process *child, ce = suc->update_data->list.entries[suc->current]; if (prepare_to_clone_next_submodule(ce, child, suc, err)) { int *p = xmalloc(sizeof(*p)); + *p = suc->current; *idx_task_cb = p; suc->current++; @@ -2098,6 +2115,7 @@ static int update_clone_get_next_task(struct child_process *child, index = suc->current - suc->update_data->list.nr; if (index < suc->failed_clones_nr) { int *p; + ce = suc->failed_clones[index]; if (!prepare_to_clone_next_submodule(ce, child, suc, err)) { suc->current ++; @@ -2116,11 +2134,12 @@ static int update_clone_get_next_task(struct child_process *child, return 0; } -static int update_clone_start_failure(struct strbuf *err, +static int update_clone_start_failure(struct strbuf *err UNUSED, void *suc_cb, - void *idx_task_cb) + void *idx_task_cb UNUSED) { struct submodule_update_clone *suc = suc_cb; + suc->quickstop = 1; return 1; } @@ -2132,9 +2151,9 @@ static int update_clone_task_finished(int result, { const struct cache_entry *ce; struct submodule_update_clone *suc = suc_cb; - int *idxP = idx_task_cb; int idx = *idxP; + free(idxP); if (!result) @@ -2167,19 +2186,20 @@ static int git_update_clone_config(const char *var, const char *value, void *cb) { int *max_jobs = cb; + if (!strcmp(var, "submodule.fetchjobs")) *max_jobs = parse_submodule_fetchjobs(var, value); return 0; } -static int is_tip_reachable(const char *path, struct object_id *oid) +static int is_tip_reachable(const char *path, const struct object_id *oid) { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf rev = STRBUF_INIT; char *hex = oid_to_hex(oid); cp.git_cmd = 1; - cp.dir = xstrdup(path); + cp.dir = path; cp.no_stderr = 1; strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL); @@ -2191,13 +2211,14 @@ static int is_tip_reachable(const char *path, struct object_id *oid) return 1; } -static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid) +static int fetch_in_submodule(const char *module_path, int depth, int quiet, + const struct object_id *oid) { struct child_process cp = CHILD_PROCESS_INIT; prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; - cp.dir = xstrdup(module_path); + cp.dir = module_path; strvec_push(&cp.args, "fetch"); if (quiet) @@ -2207,17 +2228,19 @@ static int fetch_in_submodule(const char *module_path, int depth, int quiet, str if (oid) { char *hex = oid_to_hex(oid); char *remote = get_default_remote(); + strvec_pushl(&cp.args, remote, hex, NULL); + free(remote); } return run_command(&cp); } -static int run_update_command(struct update_data *ud, int subforce) +static int run_update_command(const struct update_data *ud, int subforce) { struct child_process cp = CHILD_PROCESS_INIT; char *oid = oid_to_hex(&ud->oid); - int must_die_on_failure = 0; + int ret; switch (ud->update_strategy.type) { case SM_UPDATE_CHECKOUT: @@ -2231,55 +2254,50 @@ static int run_update_command(struct update_data *ud, int subforce) strvec_push(&cp.args, "rebase"); if (ud->quiet) strvec_push(&cp.args, "--quiet"); - must_die_on_failure = 1; break; case SM_UPDATE_MERGE: cp.git_cmd = 1; strvec_push(&cp.args, "merge"); if (ud->quiet) strvec_push(&cp.args, "--quiet"); - must_die_on_failure = 1; break; case SM_UPDATE_COMMAND: cp.use_shell = 1; strvec_push(&cp.args, ud->update_strategy.command); - must_die_on_failure = 1; break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } strvec_push(&cp.args, oid); - cp.dir = xstrdup(ud->sm_path); + cp.dir = ud->sm_path; prepare_submodule_repo_env(&cp.env); - if (run_command(&cp)) { + if ((ret = run_command(&cp))) { switch (ud->update_strategy.type) { case SM_UPDATE_CHECKOUT: die_message(_("Unable to checkout '%s' in submodule path '%s'"), oid, ud->displaypath); + /* No "ret" assignment, use "git checkout"'s */ break; case SM_UPDATE_REBASE: - die_message(_("Unable to rebase '%s' in submodule path '%s'"), - oid, ud->displaypath); + ret = die_message(_("Unable to rebase '%s' in submodule path '%s'"), + oid, ud->displaypath); break; case SM_UPDATE_MERGE: - die_message(_("Unable to merge '%s' in submodule path '%s'"), - oid, ud->displaypath); + ret = die_message(_("Unable to merge '%s' in submodule path '%s'"), + oid, ud->displaypath); break; case SM_UPDATE_COMMAND: - die_message(_("Execution of '%s %s' failed in submodule path '%s'"), - ud->update_strategy.command, oid, ud->displaypath); + ret = die_message(_("Execution of '%s %s' failed in submodule path '%s'"), + ud->update_strategy.command, oid, ud->displaypath); break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } - if (must_die_on_failure) - exit(128); - /* the command failed, but update must continue */ - return 1; + return ret; } if (ud->quiet) @@ -2303,14 +2321,14 @@ static int run_update_command(struct update_data *ud, int subforce) ud->displaypath, ud->update_strategy.command, oid); break; default: - BUG("unexpected update strategy type: %s", - submodule_strategy_to_string(&ud->update_strategy)); + BUG("unexpected update strategy type: %d", + ud->update_strategy.type); } return 0; } -static int run_update_procedure(struct update_data *ud) +static int run_update_procedure(const struct update_data *ud) { int subforce = is_null_oid(&ud->suboid) || ud->force; @@ -2332,59 +2350,67 @@ static int run_update_procedure(struct update_data *ud) */ if (!is_tip_reachable(ud->sm_path, &ud->oid) && fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid)) - die(_("Fetched in submodule path '%s', but it did not " - "contain %s. Direct fetching of that commit failed."), - ud->displaypath, oid_to_hex(&ud->oid)); + return die_message(_("Fetched in submodule path '%s', but it did not " + "contain %s. Direct fetching of that commit failed."), + ud->displaypath, oid_to_hex(&ud->oid)); } return run_update_command(ud, subforce); } -static const char *remote_submodule_branch(const char *path) +static int remote_submodule_branch(const char *path, const char **branch) { const struct submodule *sub; - const char *branch = NULL; char *key; + *branch = NULL; sub = submodule_from_path(the_repository, null_oid(), path); if (!sub) - return NULL; + return die_message(_("could not initialize submodule at path '%s'"), + path); key = xstrfmt("submodule.%s.branch", sub->name); - if (repo_config_get_string_tmp(the_repository, key, &branch)) - branch = sub->branch; + if (repo_config_get_string_tmp(the_repository, key, branch)) + *branch = sub->branch; free(key); - if (!branch) - return "HEAD"; + if (!*branch) { + *branch = "HEAD"; + return 0; + } - if (!strcmp(branch, ".")) { + if (!strcmp(*branch, ".")) { const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); if (!refname) - die(_("No such ref: %s"), "HEAD"); + return die_message(_("No such ref: %s"), "HEAD"); /* detached HEAD */ if (!strcmp(refname, "HEAD")) - die(_("Submodule (%s) branch configured to inherit " - "branch from superproject, but the superproject " - "is not on any branch"), sub->name); + return die_message(_("Submodule (%s) branch configured to inherit " + "branch from superproject, but the superproject " + "is not on any branch"), sub->name); if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); - return refname; + return die_message(_("Expecting a full ref name, got %s"), + refname); + + *branch = refname; + return 0; } - return branch; + /* Our "branch" is coming from repo_config_get_string_tmp() */ + return 0; } -static void ensure_core_worktree(const char *path) +static int ensure_core_worktree(const char *path) { const char *cw; struct repository subrepo; if (repo_submodule_init(&subrepo, the_repository, path, null_oid())) - die(_("could not get a repository handle for submodule '%s'"), path); + return die_message(_("could not get a repository handle for submodule '%s'"), + path); if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) { char *cfg_file, *abs_path; @@ -2402,15 +2428,39 @@ static void ensure_core_worktree(const char *path) free(abs_path); strbuf_release(&sb); } + + repo_clear(&subrepo); + return 0; +} + +static const char *submodule_update_type_to_label(enum submodule_update_type type) +{ + switch (type) { + case SM_UPDATE_CHECKOUT: + return "checkout"; + case SM_UPDATE_MERGE: + return "merge"; + case SM_UPDATE_REBASE: + return "rebase"; + case SM_UPDATE_UNSPECIFIED: + case SM_UPDATE_NONE: + case SM_UPDATE_COMMAND: + break; + } + BUG("unreachable with type %d", type); } -static void update_data_to_args(struct update_data *update_data, struct strvec *args) +static void update_data_to_args(const struct update_data *update_data, + struct strvec *args) { + enum submodule_update_type update_type = update_data->update_default; + strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL); + if (update_data->displaypath) { + strvec_push(args, "--super-prefix"); + strvec_pushf(args, "%s/", update_data->displaypath); + } strvec_pushf(args, "--jobs=%d", update_data->max_jobs); - if (update_data->recursive_prefix) - strvec_pushl(args, "--recursive-prefix", - update_data->recursive_prefix, NULL); if (update_data->quiet) strvec_push(args, "--quiet"); if (update_data->force) @@ -2429,10 +2479,13 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * strvec_push(args, "--require-init"); if (update_data->depth) strvec_pushf(args, "--depth=%d", update_data->depth); - if (update_data->update_default) - strvec_pushl(args, "--update", update_data->update_default, NULL); + if (update_type != SM_UPDATE_UNSPECIFIED) + strvec_pushf(args, "--%s", + submodule_update_type_to_label(update_type)); + if (update_data->references.nr) { struct string_list_item *item; + for_each_string_list_item(item, &update_data->references) strvec_pushl(args, "--reference", item->string, NULL); } @@ -2452,66 +2505,62 @@ static void update_data_to_args(struct update_data *update_data, struct strvec * static int update_submodule(struct update_data *update_data) { - char *prefixed_path; - - ensure_core_worktree(update_data->sm_path); - - if (update_data->recursive_prefix) - prefixed_path = xstrfmt("%s%s", update_data->recursive_prefix, - update_data->sm_path); - else - prefixed_path = xstrdup(update_data->sm_path); - - update_data->displaypath = get_submodule_displaypath(prefixed_path, - update_data->prefix); - free(prefixed_path); + int ret; - determine_submodule_update_strategy(the_repository, update_data->just_cloned, - update_data->sm_path, update_data->update_default, - &update_data->update_strategy); + ret = determine_submodule_update_strategy(the_repository, + update_data->just_cloned, + update_data->sm_path, + update_data->update_default, + &update_data->update_strategy); + if (ret) + return ret; if (update_data->just_cloned) oidcpy(&update_data->suboid, null_oid()); else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid)) - die(_("Unable to find current revision in submodule path '%s'"), - update_data->displaypath); + return die_message(_("Unable to find current revision in submodule path '%s'"), + update_data->displaypath); if (update_data->remote) { - char *remote_name = get_default_remote_submodule(update_data->sm_path); - const char *branch = remote_submodule_branch(update_data->sm_path); - char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); + char *remote_name; + const char *branch; + char *remote_ref; + int code; + + code = get_default_remote_submodule(update_data->sm_path, &remote_name); + if (code) + return code; + code = remote_submodule_branch(update_data->sm_path, &branch); + if (code) + return code; + remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); + + free(remote_name); if (!update_data->nofetch) { if (fetch_in_submodule(update_data->sm_path, update_data->depth, 0, NULL)) - die(_("Unable to fetch in submodule path '%s'"), - update_data->sm_path); + return die_message(_("Unable to fetch in submodule path '%s'"), + update_data->sm_path); } if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid)) - die(_("Unable to find %s revision in submodule path '%s'"), - remote_ref, update_data->sm_path); + return die_message(_("Unable to find %s revision in submodule path '%s'"), + remote_ref, update_data->sm_path); free(remote_ref); } - if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) - if (run_update_procedure(update_data)) - return 1; + if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force) { + ret = run_update_procedure(update_data); + if (ret) + return ret; + } if (update_data->recursive) { struct child_process cp = CHILD_PROCESS_INIT; struct update_data next = *update_data; - int res; - if (update_data->recursive_prefix) - prefixed_path = xstrfmt("%s%s/", update_data->recursive_prefix, - update_data->sm_path); - else - prefixed_path = xstrfmt("%s/", update_data->sm_path); - - next.recursive_prefix = get_submodule_displaypath(prefixed_path, - update_data->prefix); next.prefix = NULL; oidcpy(&next.oid, null_oid()); oidcpy(&next.suboid, null_oid()); @@ -2521,16 +2570,11 @@ static int update_submodule(struct update_data *update_data) prepare_submodule_repo_env(&cp.env); update_data_to_args(&next, &cp.args); - /* die() if child process die()'d */ - res = run_command(&cp); - if (!res) - return 0; - die_message(_("Failed to recurse into submodule path '%s'"), - update_data->displaypath); - if (res == 128) - exit(res); - else if (res) - return 1; + ret = run_command(&cp); + if (ret) + die_message(_("Failed to recurse into submodule path '%s'"), + update_data->displaypath); + return ret; } return 0; @@ -2538,14 +2582,22 @@ static int update_submodule(struct update_data *update_data) static int update_submodules(struct update_data *update_data) { - int i, res = 0; + int i, ret = 0; struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; + const struct run_process_parallel_opts opts = { + .tr2_category = "submodule", + .tr2_label = "parallel/update", + + .processes = update_data->max_jobs, + + .get_next_task = update_clone_get_next_task, + .start_failure = update_clone_start_failure, + .task_finished = update_clone_task_finished, + .data = &suc, + }; suc.update_data = update_data; - run_processes_parallel_tr2(suc.update_data->max_jobs, update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, &suc, "submodule", - "parallel/update"); + run_processes_parallel(&opts); /* * We saved the output and put it out all at once now. @@ -2556,34 +2608,51 @@ static int update_submodules(struct update_data *update_data) * - the listener can avoid doing any work if fetching failed. */ if (suc.quickstop) { - res = 1; + ret = 1; goto cleanup; } for (i = 0; i < suc.update_clone_nr; i++) { struct update_clone_data ucd = suc.update_clone[i]; + int code; oidcpy(&update_data->oid, &ucd.oid); update_data->just_cloned = ucd.just_cloned; update_data->sm_path = ucd.sub->path; - if (update_submodule(update_data)) - res = 1; + code = ensure_core_worktree(update_data->sm_path); + if (code) + goto fail; + + update_data->displaypath = get_submodule_displaypath( + update_data->sm_path, update_data->prefix, + update_data->super_prefix); + code = update_submodule(update_data); + FREE_AND_NULL(update_data->displaypath); +fail: + if (!code) + continue; + ret = code; + if (ret == 128) + goto cleanup; } cleanup: + submodule_update_clone_release(&suc); string_list_clear(&update_data->references, 0); - return res; + return ret; } static int module_update(int argc, const char **argv, const char *prefix) { - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; + struct pathspec pathspec2 = { 0 }; struct update_data opt = UPDATE_DATA_INIT; - struct list_objects_filter_options filter_options; + struct list_objects_filter_options filter_options = + LIST_OBJECTS_FILTER_INIT; int ret; - struct option module_update_options[] = { + OPT__SUPER_PREFIX(&opt.super_prefix), OPT__FORCE(&opt.force, N_("force checkout updates"), 0), OPT_BOOL(0, "init", &opt.init, N_("initialize uninitialized submodules before update")), @@ -2593,16 +2662,15 @@ static int module_update(int argc, const char **argv, const char *prefix) N_("traverse submodules recursively")), OPT_BOOL('N', "no-fetch", &opt.nofetch, N_("don't fetch new objects from the remote site")), - OPT_STRING(0, "prefix", &opt.prefix, - N_("path"), - N_("path into the working tree")), - OPT_STRING(0, "recursive-prefix", &opt.recursive_prefix, - N_("path"), - N_("path into the working tree, across nested " - "submodule boundaries")), - OPT_STRING(0, "update", &opt.update_default, - N_("string"), - N_("rebase, merge, checkout or none")), + OPT_SET_INT(0, "checkout", &opt.update_default, + N_("use the 'checkout' update strategy (default)"), + SM_UPDATE_CHECKOUT), + OPT_SET_INT('m', "merge", &opt.update_default, + N_("use the 'merge' update strategy"), + SM_UPDATE_MERGE), + OPT_SET_INT('r', "rebase", &opt.update_default, + N_("use the 'rebase' update strategy"), + SM_UPDATE_REBASE), OPT_STRING_LIST(0, "reference", &opt.references, N_("repo"), N_("reference repository")), OPT_BOOL(0, "dissociate", &opt.dissociate, @@ -2618,13 +2686,12 @@ static int module_update(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "progress", &opt.progress, N_("force cloning progress")), OPT_BOOL(0, "require-init", &opt.require_init, - N_("disallow cloning into non-empty directory")), + N_("disallow cloning into non-empty directory, implies --init")), OPT_BOOL(0, "single-branch", &opt.single_branch, N_("clone only one branch, HEAD or --branch")), OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_END() }; - const char *const git_submodule_helper_usage[] = { N_("git submodule [--quiet] update" " [--init [--filter=<filter-spec>]] [--remote]" @@ -2638,25 +2705,26 @@ static int module_update(int argc, const char **argv, const char *prefix) update_clone_config_from_gitmodules(&opt.max_jobs); git_config(git_update_clone_config, &opt.max_jobs); - memset(&filter_options, 0, sizeof(filter_options)); argc = parse_options(argc, argv, prefix, module_update_options, git_submodule_helper_usage, 0); + if (opt.require_init) + opt.init = 1; + if (filter_options.choice && !opt.init) { usage_with_options(git_submodule_helper_usage, module_update_options); } opt.filter_options = &filter_options; + opt.prefix = prefix; if (opt.update_default) - if (parse_submodule_update_strategy(opt.update_default, - &opt.update_strategy) < 0) - die(_("bad value for update parameter")); + opt.update_strategy.type = opt.update_default; - if (module_list_compute(argc, argv, prefix, &pathspec, &opt.list) < 0) { - list_objects_filter_release(&filter_options); - return 1; + if (module_list_compute(argv, prefix, &pathspec, &opt.list) < 0) { + ret = 1; + goto cleanup; } if (pathspec.nr) @@ -2666,9 +2734,12 @@ static int module_update(int argc, const char **argv, const char *prefix) struct module_list list = MODULE_LIST_INIT; struct init_cb info = INIT_CB_INIT; - if (module_list_compute(argc, argv, opt.prefix, - &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, opt.prefix, + &pathspec2, &list) < 0) { + module_list_release(&list); + ret = 1; + goto cleanup; + } /* * If there are no path args and submodule.active is set then, @@ -2678,15 +2749,20 @@ static int module_update(int argc, const char **argv, const char *prefix) module_list_active(&list); info.prefix = opt.prefix; - info.superprefix = opt.recursive_prefix; + info.super_prefix = opt.super_prefix; if (opt.quiet) info.flags |= OPT_QUIET; for_each_listed_submodule(&list, init_submodule_cb, &info); + module_list_release(&list); } ret = update_submodules(&opt); +cleanup: + update_data_release(&opt); list_objects_filter_release(&filter_options); + clear_pathspec(&pathspec); + clear_pathspec(&pathspec2); return ret; } @@ -2770,111 +2846,34 @@ static int push_check(int argc, const char **argv, const char *prefix) static int absorb_git_dirs(int argc, const char **argv, const char *prefix) { int i; - struct pathspec pathspec; + struct pathspec pathspec = { 0 }; struct module_list list = MODULE_LIST_INIT; - unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES; - + const char *super_prefix = NULL; struct option embed_gitdir_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("path into the working tree")), - OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"), - ABSORB_GITDIR_RECURSE_SUBMODULES), + OPT__SUPER_PREFIX(&super_prefix), OPT_END() }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"), + N_("git submodule absorbgitdirs [<options>] [<path>...]"), NULL }; + int ret = 1; argc = parse_options(argc, argv, prefix, embed_gitdir_options, git_submodule_helper_usage, 0); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; + if (module_list_compute(argv, prefix, &pathspec, &list) < 0) + goto cleanup; for (i = 0; i < list.nr; i++) - absorb_git_dir_into_superproject(list.entries[i]->name, flags); + absorb_git_dir_into_superproject(list.entries[i]->name, + super_prefix); - return 0; -} - -static int is_active(int argc, const char **argv, const char *prefix) -{ - if (argc != 2) - die("submodule--helper is-active takes exactly 1 argument"); - - return !is_submodule_active(the_repository, argv[1]); -} - -/* - * Exit non-zero if any of the submodule names given on the command line is - * invalid. If no names are given, filter stdin to print only valid names - * (which is primarily intended for testing). - */ -static int check_name(int argc, const char **argv, const char *prefix) -{ - if (argc > 1) { - while (*++argv) { - if (check_submodule_name(*argv) < 0) - return 1; - } - } else { - struct strbuf buf = STRBUF_INIT; - while (strbuf_getline(&buf, stdin) != EOF) { - if (!check_submodule_name(buf.buf)) - printf("%s\n", buf.buf); - } - strbuf_release(&buf); - } - return 0; -} - -static int module_config(int argc, const char **argv, const char *prefix) -{ - enum { - CHECK_WRITEABLE = 1, - DO_UNSET = 2 - } command = 0; - - struct option module_config_options[] = { - OPT_CMDMODE(0, "check-writeable", &command, - N_("check if it is safe to write to the .gitmodules file"), - CHECK_WRITEABLE), - OPT_CMDMODE(0, "unset", &command, - N_("unset the config in the .gitmodules file"), - DO_UNSET), - OPT_END() - }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper config <name> [<value>]"), - N_("git submodule--helper config --unset <name>"), - "git submodule--helper config --check-writeable", - NULL - }; - - argc = parse_options(argc, argv, prefix, module_config_options, - git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0); - - if (argc == 1 && command == CHECK_WRITEABLE) - return is_writing_gitmodules_ok() ? 0 : -1; - - /* Equivalent to ACTION_GET in builtin/config.c */ - if (argc == 2 && command != DO_UNSET) - return print_config_from_gitmodules(the_repository, argv[1]); - - /* Equivalent to ACTION_SET in builtin/config.c */ - if (argc == 3 || (argc == 2 && command == DO_UNSET)) { - const char *value = (argc == 3) ? argv[2] : NULL; - - if (!is_writing_gitmodules_ok()) - die(_("please make sure that the .gitmodules file is in the working tree")); - - return config_set_in_gitmodules_file_gently(argv[1], value); - } - - usage_with_options(git_submodule_helper_usage, module_config_options); + ret = 0; +cleanup: + clear_pathspec(&pathspec); + module_list_release(&list); + return ret; } static int module_set_url(int argc, const char **argv, const char *prefix) @@ -2883,13 +2882,12 @@ static int module_set_url(int argc, const char **argv, const char *prefix) const char *newurl; const char *path; char *config_name; - struct option options[] = { OPT__QUIET(&quiet, N_("suppress output for setting url of a submodule")), OPT_END() }; const char *const usage[] = { - N_("git submodule--helper set-url [--quiet] <path> <newurl>"), + N_("git submodule set-url [--quiet] <path> <newurl>"), NULL }; @@ -2901,7 +2899,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix) config_name = xstrfmt("submodule.%s.url", path); config_set_in_gitmodules_file_gently(config_name, newurl); - sync_submodule(path, prefix, quiet ? OPT_QUIET : 0); + sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0); free(config_name); @@ -2914,13 +2912,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) const char *opt_branch = NULL; const char *path; char *config_name; - - /* - * We accept the `quiet` option for uniformity across subcommands, - * though there is nothing to make less verbose in this subcommand. - */ struct option options[] = { + /* + * We accept the `quiet` option for uniformity across subcommands, + * though there is nothing to make less verbose in this subcommand. + */ OPT_NOOP_NOARG('q', "quiet"), + OPT_BOOL('d', "default", &opt_default, N_("set the default tracking branch to master")), OPT_STRING('b', "branch", &opt_branch, N_("branch"), @@ -2928,8 +2926,8 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) OPT_END() }; const char *const usage[] = { - N_("git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"), - N_("git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"), + N_("git submodule set-branch [-q|--quiet] (-d|--default) <path>"), + N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"), NULL }; @@ -2955,7 +2953,6 @@ static int module_create_branch(int argc, const char **argv, const char *prefix) { enum branch_track track; int quiet = 0, force = 0, reflog = 0, dry_run = 0; - struct option options[] = { OPT__QUIET(&quiet, N_("print only error messages")), OPT__FORCE(&force, N_("force creation"), 0), @@ -3018,8 +3015,10 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path) if (!capture_command(&cp_remote, &sb_remote_out, 0)) { char *next_line; char *line = sb_remote_out.buf; + while ((next_line = strchr(line, '\n')) != NULL) { size_t len = next_line - line; + if (strip_suffix_mem(line, &len, " (fetch)")) strbuf_addf(msg, " %.*s\n", (int)len, line); line = next_line + 1; @@ -3033,6 +3032,8 @@ static int add_submodule(const struct add_data *add_data) { char *submod_gitdir_path; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; + struct string_list reference = STRING_LIST_INIT_NODUP; + int ret = -1; /* perhaps the path already exists and is already a git repo, else clone it */ if (is_directory(add_data->sm_path)) { @@ -3049,6 +3050,7 @@ static int add_submodule(const struct add_data *add_data) free(submod_gitdir_path); } else { struct child_process cp = CHILD_PROCESS_INIT; + submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name); if (is_directory(submod_gitdir_path)) { @@ -3087,15 +3089,17 @@ static int add_submodule(const struct add_data *add_data) clone_data.url = add_data->realrepo; clone_data.quiet = add_data->quiet; clone_data.progress = add_data->progress; - if (add_data->reference_path) - string_list_append(&clone_data.reference, - xstrdup(add_data->reference_path)); + if (add_data->reference_path) { + char *p = xstrdup(add_data->reference_path); + + string_list_append(&reference, p)->util = p; + } clone_data.dissociate = add_data->dissociate; if (add_data->depth >= 0) clone_data.depth = xstrfmt("%d", add_data->depth); - if (clone_submodule(&clone_data)) - return -1; + if (clone_submodule(&clone_data, &reference)) + goto cleanup; prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; @@ -3114,7 +3118,10 @@ static int add_submodule(const struct add_data *add_data) if (run_command(&cp)) die(_("unable to checkout submodule '%s'"), add_data->sm_path); } - return 0; + ret = 0; +cleanup: + string_list_clear(&reference, 1); + return ret; } static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value) @@ -3135,7 +3142,7 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con static void configure_added_submodule(struct add_data *add_data) { char *key; - char *val = NULL; + const char *val; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; @@ -3180,7 +3187,7 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!git_config_get_string("submodule.active", &val) && val) { + if (!git_config_get_string_tmp("submodule.active", &val)) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag @@ -3203,7 +3210,7 @@ static void die_on_index_match(const char *path, int force) const char *args[] = { path, NULL }; parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args); - if (read_cache_preload(NULL) < 0) + if (repo_read_index_preload(the_repository, NULL, 0) < 0) die(_("index file corrupt")); if (ps.nr) { @@ -3214,19 +3221,18 @@ static void die_on_index_match(const char *path, int force) ensure_full_index(&the_index); /* - * Since there is only one pathspec, we just need - * need to check ps_matched[0] to know if a cache - * entry matched. + * 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 < active_nr; i++) { - ce_path_match(&the_index, active_cache[i], &ps, + for (i = 0; i < the_index.cache_nr; i++) { + ce_path_match(&the_index, the_index.cache[i], &ps, ps_matched); if (ps_matched[0]) { if (!force) die(_("'%s' already exists in the index"), path); - if (!S_ISGITLINK(active_cache[i]->ce_mode)) + if (!S_ISGITLINK(the_index.cache[i]->ce_mode)) die(_("'%s' already exists in the index " "and is not a submodule"), path); break; @@ -3254,7 +3260,6 @@ static int module_add(int argc, const char **argv, const char *prefix) int force = 0, quiet = 0, progress = 0, dissociate = 0; struct add_data add_data = ADD_DATA_INIT; char *to_free = NULL; - struct option options[] = { OPT_STRING('b', "branch", &add_data.branch, N_("branch"), N_("branch of repository to add as submodule")), @@ -3271,11 +3276,12 @@ static int module_add(int argc, const char **argv, const char *prefix) OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")), OPT_END() }; - const char *const usage[] = { - N_("git submodule--helper add [<options>] [--] <repository> [<path>]"), + N_("git submodule add [<options>] [--] <repository> [<path>]"), NULL }; + struct strbuf sb = STRBUF_INIT; + int ret = 1; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -3295,8 +3301,12 @@ static int module_add(int argc, const char **argv, const char *prefix) else add_data.sm_path = xstrdup(argv[1]); - if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) - add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path); + if (prefix && *prefix && !is_absolute_path(add_data.sm_path)) { + char *sm_path = add_data.sm_path; + + add_data.sm_path = xstrfmt("%s%s", prefix, sm_path); + free(sm_path); + } if (starts_with_dot_dot_slash(add_data.repo) || starts_with_dot_slash(add_data.repo)) { @@ -3325,20 +3335,17 @@ static int module_add(int argc, const char **argv, const char *prefix) die_on_repo_without_commits(add_data.sm_path); if (!force) { - int exit_code = -1; - struct strbuf sb = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; + cp.git_cmd = 1; cp.no_stdout = 1; strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing", "--no-warn-embedded-repo", add_data.sm_path, NULL); - if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { + if ((ret = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) { strbuf_complete_line(&sb); fputs(sb.buf, stderr); - free(add_data.sm_path); - return exit_code; + goto cleanup; } - strbuf_release(&sb); } if(!add_data.sm_name) @@ -3353,64 +3360,44 @@ static int module_add(int argc, const char **argv, const char *prefix) add_data.progress = !!progress; add_data.dissociate = !!dissociate; - if (add_submodule(&add_data)) { - free(add_data.sm_path); - return 1; - } + if (add_submodule(&add_data)) + goto cleanup; configure_added_submodule(&add_data); + + ret = 0; +cleanup: free(add_data.sm_path); free(to_free); + strbuf_release(&sb); - return 0; + return ret; } -#define SUPPORT_SUPER_PREFIX (1<<0) - -struct cmd_struct { - const char *cmd; - int (*fn)(int, const char **, const char *); - unsigned option; -}; - -static struct cmd_struct commands[] = { - {"list", module_list, 0}, - {"name", module_name, 0}, - {"clone", module_clone, 0}, - {"add", module_add, SUPPORT_SUPER_PREFIX}, - {"update", module_update, 0}, - {"resolve-relative-url-test", resolve_relative_url_test, 0}, - {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, - {"init", module_init, SUPPORT_SUPER_PREFIX}, - {"status", module_status, SUPPORT_SUPER_PREFIX}, - {"sync", module_sync, SUPPORT_SUPER_PREFIX}, - {"deinit", module_deinit, 0}, - {"summary", module_summary, SUPPORT_SUPER_PREFIX}, - {"push-check", push_check, 0}, - {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, - {"is-active", is_active, 0}, - {"check-name", check_name, 0}, - {"config", module_config, 0}, - {"set-url", module_set_url, 0}, - {"set-branch", module_set_branch, 0}, - {"create-branch", module_create_branch, 0}, -}; - int cmd_submodule__helper(int argc, const char **argv, const char *prefix) { - int i; - if (argc < 2 || !strcmp(argv[1], "-h")) - usage("git submodule--helper <command>"); - - for (i = 0; i < ARRAY_SIZE(commands); i++) { - if (!strcmp(argv[1], commands[i].cmd)) { - if (get_super_prefix() && - !(commands[i].option & SUPPORT_SUPER_PREFIX)) - die(_("%s doesn't support --super-prefix"), - commands[i].cmd); - return commands[i].fn(argc - 1, argv + 1, prefix); - } - } + parse_opt_subcommand_fn *fn = NULL; + const char *const usage[] = { + N_("git submodule--helper <command>"), + NULL + }; + struct option options[] = { + OPT_SUBCOMMAND("clone", &fn, module_clone), + OPT_SUBCOMMAND("add", &fn, module_add), + OPT_SUBCOMMAND("update", &fn, module_update), + OPT_SUBCOMMAND("foreach", &fn, module_foreach), + OPT_SUBCOMMAND("init", &fn, module_init), + OPT_SUBCOMMAND("status", &fn, module_status), + OPT_SUBCOMMAND("sync", &fn, module_sync), + OPT_SUBCOMMAND("deinit", &fn, module_deinit), + OPT_SUBCOMMAND("summary", &fn, module_summary), + OPT_SUBCOMMAND("push-check", &fn, push_check), + OPT_SUBCOMMAND("absorbgitdirs", &fn, absorb_git_dirs), + OPT_SUBCOMMAND("set-url", &fn, module_set_url), + OPT_SUBCOMMAND("set-branch", &fn, module_set_branch), + OPT_SUBCOMMAND("create-branch", &fn, module_create_branch), + OPT_END() + }; + argc = parse_options(argc, argv, prefix, options, usage, 0); - die(_("'%s' is not a valid submodule--helper " - "subcommand"), argv[1]); + return fn(argc, argv, prefix); } diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index e547a08d6c..e00768a8b7 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -5,15 +5,19 @@ #include "parse-options.h" static const char * const git_symbolic_ref_usage[] = { - N_("git symbolic-ref [<options>] <name> [<ref>]"), - N_("git symbolic-ref -d [-q] <name>"), + N_("git symbolic-ref [-m <reason>] <name> <ref>"), + N_("git symbolic-ref [-q] [--short] [--no-recurse] <name>"), + N_("git symbolic-ref --delete [-q] <name>"), NULL }; -static int check_symref(const char *HEAD, int quiet, int shorten, int print) +static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print) { - int flag; - const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag); + int resolve_flags, flag; + const char *refname; + + resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE); + refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag); if (!refname) die("No such ref: %s", HEAD); @@ -35,13 +39,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print) int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { - int quiet = 0, delete = 0, shorten = 0, ret = 0; + int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0; const char *msg = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("suppress error message for non-symbolic (detached) refs")), OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")), OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), + OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")), OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_END(), }; @@ -55,7 +60,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) if (delete) { if (argc != 1) usage_with_options(git_symbolic_ref_usage, options); - ret = check_symref(argv[0], 1, 0, 0); + ret = check_symref(argv[0], 1, 0, 0, 0); if (ret) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) @@ -65,12 +70,14 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) switch (argc) { case 1: - ret = check_symref(argv[0], quiet, shorten, 1); + ret = check_symref(argv[0], quiet, shorten, recurse, 1); break; case 2: if (!strcmp(argv[0], "HEAD") && !starts_with(argv[1], "refs/")) 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); break; default: diff --git a/builtin/tag.c b/builtin/tag.c index 75dece0e4f..adcaa547b0 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -9,6 +9,7 @@ #include "cache.h" #include "config.h" #include "builtin.h" +#include "hex.h" #include "refs.h" #include "object-store.h" #include "tag.h" @@ -23,11 +24,13 @@ #include "date.h" static const char * const git_tag_usage[] = { - N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n" - " <tagname> [<head>]"), + N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n" + " <tagname> [<commit> | <object>]"), N_("git tag -d <tagname>..."), - N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n" - " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"), + N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n" + " [--points-at <object>] [--column[=<options>] | --no-column]\n" + " [--create-reflog] [--sort=<key>] [--format=<format>]\n" + " [--merged <commit>] [--no-merged <commit>] [<pattern>...]"), N_("git tag -v [--format=<format>] <tagname>..."), NULL }; @@ -178,8 +181,6 @@ static const char tag_template_nocleanup[] = static int git_tag_config(const char *var, const char *value, void *cb) { - int status; - if (!strcmp(var, "tag.gpgsign")) { config_sign_tag = git_config_bool(var, value); return 0; @@ -192,9 +193,6 @@ static int git_tag_config(const char *var, const char *value, void *cb) return 0; } - status = git_gpg_config(var, value, cb); - if (status) - return status; if (!strcmp(var, "tag.forcesignannotated")) { force_sign_annotate = git_config_bool(var, value); return 0; @@ -431,7 +429,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) int create_reflog = 0; int annotate = 0, force = 0; int cmdmode = 0, create_tag_object = 0; - const char *msgfile = NULL, *keyid = NULL; + char *msgfile = NULL; + const char *keyid = NULL; struct msg_arg msg = { .buf = STRBUF_INIT }; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; @@ -641,5 +640,6 @@ cleanup: strbuf_release(&reflog_msg); strbuf_release(&msg.buf); strbuf_release(&err); + free(msgfile); return ret; } diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c index 58652229f2..e9b105a539 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "hex.h" #include "object-store.h" static char *create_temp_file(struct object_id *oid) @@ -19,6 +20,7 @@ static char *create_temp_file(struct object_id *oid) if (write_in_full(fd, buf, size) < 0) die_errno("unable to write temp-file"); close(fd); + free(buf); return path; } @@ -27,7 +29,7 @@ int cmd_unpack_file(int argc, const char **argv, const char *prefix) struct object_id oid; if (argc != 2 || !strcmp(argv[1], "-h")) - usage("git unpack-file <sha1>"); + usage("git unpack-file <blob>"); if (get_oid(argv[1], &oid)) die("Not a valid object name %s", argv[1]); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 56d05e2725..1908dcfcff 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -2,12 +2,14 @@ #include "cache.h" #include "bulk-checkin.h" #include "config.h" +#include "hex.h" #include "object-store.h" #include "object.h" #include "delta.h" #include "pack.h" #include "blob.h" #include "commit.h" +#include "replace-object.h" #include "tag.h" #include "tree.h" #include "tree-walk.h" @@ -97,15 +99,27 @@ static void use(int bytes) display_throughput(progress, consumed_bytes); } +/* + * Decompress zstream from the standard input into a newly + * allocated buffer of specified size and return the buffer. + * The caller is responsible to free the returned buffer. + * + * But for dry_run mode, "get_data()" is only used to check the + * integrity of data, and the returned buffer is not used at all. + * Therefore, in dry_run mode, "get_data()" will release the small + * allocated buffer which is reused to hold temporary zstream output + * and return NULL instead of returning garbage data. + */ static void *get_data(unsigned long size) { git_zstream stream; - void *buf = xmallocz(size); + unsigned long bufsize = dry_run && size > 8192 ? 8192 : size; + void *buf = xmallocz(bufsize); memset(&stream, 0, sizeof(stream)); stream.next_out = buf; - stream.avail_out = size; + stream.avail_out = bufsize; stream.next_in = fill(1); stream.avail_in = len; git_inflate_init(&stream); @@ -125,8 +139,17 @@ static void *get_data(unsigned long size) } stream.next_in = fill(1); stream.avail_in = len; + if (dry_run) { + /* reuse the buffer in dry_run mode */ + stream.next_out = buf; + stream.avail_out = bufsize > size - stream.total_out ? + size - stream.total_out : + bufsize; + } } git_inflate_end(&stream); + if (dry_run) + FREE_AND_NULL(buf); return buf; } @@ -326,10 +349,70 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size, { void *buf = get_data(size); - if (!dry_run && buf) + if (buf) write_object(nr, type, buf, size); - else - free(buf); +} + +struct input_zstream_data { + git_zstream *zstream; + unsigned char buf[8192]; + int status; +}; + +static const void *feed_input_zstream(struct input_stream *in_stream, + unsigned long *readlen) +{ + struct input_zstream_data *data = in_stream->data; + git_zstream *zstream = data->zstream; + void *in = fill(1); + + if (in_stream->is_finished) { + *readlen = 0; + return NULL; + } + + zstream->next_out = data->buf; + zstream->avail_out = sizeof(data->buf); + zstream->next_in = in; + zstream->avail_in = len; + + data->status = git_inflate(zstream, 0); + + in_stream->is_finished = data->status != Z_OK; + use(len - zstream->avail_in); + *readlen = sizeof(data->buf) - zstream->avail_out; + + return data->buf; +} + +static void stream_blob(unsigned long size, unsigned nr) +{ + git_zstream zstream = { 0 }; + struct input_zstream_data data = { 0 }; + struct input_stream in_stream = { + .read = feed_input_zstream, + .data = &data, + }; + struct obj_info *info = &obj_list[nr]; + + data.zstream = &zstream; + git_inflate_init(&zstream); + + if (stream_loose_object(&in_stream, size, &info->oid)) + die(_("failed to write object in stream")); + + if (data.status != Z_STREAM_END) + die(_("inflate returned (%d)"), data.status); + git_inflate_end(&zstream); + + if (strict) { + struct blob *blob = lookup_blob(the_repository, &info->oid); + + if (!blob) + die(_("invalid blob object from stream")); + blob->object.flags |= FLAG_WRITTEN; + } + info->obj = NULL; } static int resolve_against_held(unsigned nr, const struct object_id *base, @@ -359,10 +442,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, oidread(&base_oid, fill(the_hash_algo->rawsz)); use(the_hash_algo->rawsz); delta_data = get_data(delta_size); - if (dry_run || !delta_data) { - free(delta_data); + if (!delta_data) return; - } if (has_object_file(&base_oid)) ; /* Ok we have this one */ else if (resolve_against_held(nr, &base_oid, @@ -398,10 +479,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, die("offset value out of bound for delta base object"); delta_data = get_data(delta_size); - if (dry_run || !delta_data) { - free(delta_data); + if (!delta_data) return; - } lo = 0; hi = nr; while (lo < hi) { @@ -468,9 +547,14 @@ static void unpack_one(unsigned nr) } switch (type) { + case OBJ_BLOB: + if (!dry_run && size > big_file_threshold) { + stream_blob(size, nr); + return; + } + /* fallthrough */ case OBJ_COMMIT: case OBJ_TREE: - case OBJ_BLOB: case OBJ_TAG: unpack_non_delta_entry(type, size, nr); return; diff --git a/builtin/update-index.c b/builtin/update-index.c index b62249905f..11dc135271 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -3,10 +3,11 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "cache.h" #include "bulk-checkin.h" #include "config.h" +#include "hex.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h" @@ -237,16 +238,16 @@ done: static int mark_ce_flags(const char *path, int flag, int mark) { int namelen = strlen(path); - int pos = cache_name_pos(path, namelen); + int pos = index_name_pos(&the_index, path, namelen); if (0 <= pos) { - mark_fsmonitor_invalid(&the_index, active_cache[pos]); + mark_fsmonitor_invalid(&the_index, the_index.cache[pos]); if (mark) - active_cache[pos]->ce_flags |= flag; + the_index.cache[pos]->ce_flags |= flag; else - active_cache[pos]->ce_flags &= ~flag; - active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE; + the_index.cache[pos]->ce_flags &= ~flag; + the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE; cache_tree_invalidate_path(&the_index, path); - active_cache_changed |= CE_ENTRY_CHANGED; + the_index.cache_changed |= CE_ENTRY_CHANGED; return 0; } return -1; @@ -256,7 +257,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_cache(path)) + if (remove_file_from_index(&the_index, path)) return error("%s: cannot remove from the index", path); return 0; } @@ -281,7 +282,7 @@ 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) && !ce_match_stat(old, st, 0)) + if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0)) return 0; ce = make_empty_cache_entry(&the_index, len); @@ -298,7 +299,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len } option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_cache_entry(ce, option)) { + if (add_index_entry(&the_index, ce, option)) { discard_cache_entry(ce); return error("%s: cannot add to the index - missing --add option?", path); } @@ -331,11 +332,11 @@ 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 = cache_name_pos(path, len); + int pos = index_name_pos(&the_index, path, len); /* Exact match: file or existing gitlink */ if (pos >= 0) { - const struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = the_index.cache[pos]; if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ @@ -350,8 +351,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 < active_nr) { - const struct cache_entry *ce = active_cache[pos++]; + while (pos < the_index.cache_nr) { + const struct cache_entry *ce = the_index.cache[pos++]; if (strncmp(ce->name, path, len)) break; @@ -381,8 +382,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 = cache_name_pos(path, len); - ce = pos < 0 ? NULL : active_cache[pos]; + pos = index_name_pos(&the_index, path, len); + ce = pos < 0 ? NULL : the_index.cache[pos]; if (ce && ce_skip_worktree(ce)) { /* * working directory version is assumed "good" @@ -390,7 +391,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_cache(path)) + remove_file_from_index(&the_index, path)) return error("%s: cannot remove from the index", path); return 0; } @@ -429,7 +430,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_cache_entry(ce, option)) + if (add_index_entry(&the_index, ce, option)) return error("%s: cannot add to the index - missing --add option?", path); report("add '%s'", path); @@ -441,11 +442,11 @@ static void chmod_path(char flip, const char *path) int pos; struct cache_entry *ce; - pos = cache_name_pos(path, strlen(path)); + pos = index_name_pos(&the_index, path, strlen(path)); if (pos < 0) goto fail; - ce = active_cache[pos]; - if (chmod_cache_entry(ce, flip) < 0) + ce = the_index.cache[pos]; + if (chmod_index_entry(&the_index, ce, flip) < 0) goto fail; report("chmod %cx '%s'", flip, path); @@ -488,7 +489,7 @@ static void update_one(const char *path) } if (force_remove) { - if (remove_file_from_cache(path)) + if (remove_file_from_index(&the_index, path)) die("git update-index: unable to remove %s", path); report("remove '%s'", path); return; @@ -571,7 +572,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_cache(path_name)) + if (remove_file_from_index(&the_index, path_name)) die("git update-index: unable to remove %s", ptr); } @@ -638,12 +639,12 @@ static int unresolve_one(const char *path) struct cache_entry *ce_2 = NULL, *ce_3 = NULL; /* See if there is such entry in the index. */ - pos = cache_name_pos(path, namelen); + pos = index_name_pos(&the_index, path, namelen); if (0 <= pos) { /* already merged */ - pos = unmerge_cache_entry_at(pos); - if (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos]; + pos = unmerge_index_entry_at(&the_index, pos); + if (pos < the_index.cache_nr) { + const struct cache_entry *ce = the_index.cache[pos]; if (ce_stage(ce) && ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) @@ -656,8 +657,8 @@ static int unresolve_one(const char *path) * want to do anything in the former case. */ pos = -pos-1; - if (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos]; + if (pos < the_index.cache_nr) { + const struct cache_entry *ce = the_index.cache[pos]; if (ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) { fprintf(stderr, @@ -686,13 +687,13 @@ static int unresolve_one(const char *path) goto free_return; } - remove_file_from_cache(path); - if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { + remove_file_from_index(&the_index, path); + if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) { error("%s: cannot add our version to the index.", path); ret = -1; goto free_return; } - if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) + if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD)) return 0; error("%s: cannot add their version to the index.", path); ret = -1; @@ -732,7 +733,7 @@ static int do_unresolve(int ac, const char **av, return err; } -static int do_reupdate(int ac, const char **av, +static int do_reupdate(const char **paths, const char *prefix) { /* Read HEAD and run update-index on paths that are @@ -744,7 +745,7 @@ static int do_reupdate(int ac, const char **av, parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, - prefix, av + 1); + prefix, paths); if (read_ref("HEAD", &head_oid)) /* If there is no HEAD, that means it is an initial @@ -752,8 +753,8 @@ static int do_reupdate(int ac, const char **av, */ has_head = 0; redo: - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; + for (pos = 0; pos < the_index.cache_nr; pos++) { + const struct cache_entry *ce = the_index.cache[pos]; struct cache_entry *old = NULL; int save_nr; char *path; @@ -782,12 +783,12 @@ static int do_reupdate(int ac, const char **av, * path anymore, in which case, under 'allow_remove', * or worse yet 'allow_replace', active_nr may decrease. */ - save_nr = active_nr; + save_nr = the_index.cache_nr; path = xstrdup(ce->name); update_one(path); free(path); discard_cache_entry(old); - if (save_nr != active_nr) + if (save_nr != the_index.cache_nr) goto redo; } clear_pathspec(&pathspec); @@ -802,18 +803,19 @@ struct refresh_params { static int refresh(struct refresh_params *o, unsigned int flag) { setup_work_tree(); - read_cache(); - *o->has_errors |= refresh_cache(o->flags | flag); + repo_read_index(the_repository); + *o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL, + NULL, NULL); if (has_racy_timestamp(&the_index)) { /* * Even if nothing else has changed, updating the file * increases the chance that racy timestamps become * non-racy, helping future run-time performance. * We do that even in case of "errors" returned by - * refresh_cache() as these are no actual errors. + * refresh_index() as these are no actual errors. * cmd_status() does the same. */ - active_cache_changed |= SOMETHING_CHANGED; + the_index.cache_changed |= SOMETHING_CHANGED; } return 0; } @@ -850,7 +852,7 @@ static int resolve_undo_clear_callback(const struct option *opt, { BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - resolve_undo_clear(); + resolve_undo_clear_index(&the_index); return 0; } @@ -951,7 +953,7 @@ static enum parse_opt_result unresolve_callback( *has_errors = do_unresolve(ctx->argc, ctx->argv, prefix, prefix ? strlen(prefix) : 0); if (*has_errors) - active_cache_changed = 0; + the_index.cache_changed = 0; ctx->argv += ctx->argc - 1; ctx->argc = 1; @@ -970,9 +972,9 @@ static enum parse_opt_result reupdate_callback( /* consume remaining arguments. */ setup_work_tree(); - *has_errors = do_reupdate(ctx->argc, ctx->argv, prefix); + *has_errors = do_reupdate(ctx->argv + 1, prefix); if (*has_errors) - active_cache_changed = 0; + the_index.cache_changed = 0; ctx->argv += ctx->argc - 1; ctx->argc = 1; @@ -1109,11 +1111,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) the_repository->settings.command_requires_full_index = 0; /* we will diagnose later if it turns out that we need to update it */ - newfd = hold_locked_index(&lock_file, 0); + newfd = repo_hold_locked_index(the_repository, &lock_file, 0); if (newfd < 0) lock_error = errno; - entries = read_cache(); + entries = repo_read_index(the_repository); if (entries < 0) die("cache corrupted"); @@ -1178,7 +1180,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) INDEX_FORMAT_LB, INDEX_FORMAT_UB); if (the_index.version != preferred_index_format) - active_cache_changed |= SOMETHING_CHANGED; + the_index.cache_changed |= SOMETHING_CHANGED; the_index.version = preferred_index_format; } @@ -1290,7 +1292,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) report(_("fsmonitor disabled")); } - if (active_cache_changed || force_write) { + if (the_index.cache_changed || force_write) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index 880fffec58..d2239c9ef4 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -4,7 +4,7 @@ #include "parse-options.h" static const char * const update_server_info_usage[] = { - "git update-server-info [--force]", + "git update-server-info [-f | --force]", NULL }; diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index 98d028dae6..945ee2b412 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -10,7 +10,7 @@ #include "strvec.h" static const char upload_archive_usage[] = - "git upload-archive <repo>"; + "git upload-archive <repository>"; static const char deadchild[] = "git upload-archive: archiver died with error"; diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 125af53885..7a3c68720f 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -4,11 +4,13 @@ #include "pkt-line.h" #include "parse-options.h" #include "protocol.h" +#include "replace-object.h" #include "upload-pack.h" #include "serve.h" static const char * const upload_pack_usage[] = { - N_("git upload-pack [<options>] <dir>"), + N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" + " [--advertise-refs] <directory>"), NULL }; diff --git a/builtin/var.c b/builtin/var.c index 491db27429..d9943be9af 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -5,18 +5,19 @@ */ #include "builtin.h" #include "config.h" +#include "ident.h" #include "refs.h" static const char var_usage[] = "git var (-l | <variable>)"; static const char *editor(int flag) { - const char *pgm = git_editor(); - - if (!pgm && flag & IDENT_STRICT) - die("Terminal is dumb, but EDITOR unset"); + return git_editor(); +} - return pgm; +static const char *sequence_editor(int flag) +{ + return git_sequence_editor(); } static const char *pager(int flag) @@ -41,6 +42,7 @@ static struct git_var git_vars[] = { { "GIT_COMMITTER_IDENT", git_committer_info }, { "GIT_AUTHOR_IDENT", git_author_info }, { "GIT_EDITOR", editor }, + { "GIT_SEQUENCE_EDITOR", sequence_editor }, { "GIT_PAGER", pager }, { "GIT_DEFAULT_BRANCH", default_branch }, { "", NULL }, @@ -56,18 +58,15 @@ static void list_vars(void) printf("%s=%s\n", ptr->name, val); } -static const char *read_var(const char *var) +static const struct git_var *get_git_var(const char *var) { struct git_var *ptr; - const char *val; - val = NULL; for (ptr = git_vars; ptr->read; ptr++) { if (strcmp(var, ptr->name) == 0) { - val = ptr->read(IDENT_STRICT); - break; + return ptr; } } - return val; + return NULL; } static int show_config(const char *var, const char *value, void *cb) @@ -81,7 +80,9 @@ static int show_config(const char *var, const char *value, void *cb) int cmd_var(int argc, const char **argv, const char *prefix) { - const char *val = NULL; + const struct git_var *git_var; + const char *val; + if (argc != 2) usage(var_usage); @@ -91,10 +92,15 @@ int cmd_var(int argc, const char **argv, const char *prefix) return 0; } git_config(git_default_config, NULL); - val = read_var(argv[1]); - if (!val) + + git_var = get_git_var(argv[1]); + if (!git_var) usage(var_usage); + val = git_var->read(IDENT_STRICT); + if (!val) + return 1; + printf("%s\n", val); return 0; diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 40c69a0bed..7aedf10e85 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -16,7 +16,7 @@ #include "gpg-interface.h" static const char * const verify_commit_usage[] = { - N_("git verify-commit [-v | --verbose] <commit>..."), + N_("git verify-commit [-v | --verbose] [--raw] <commit>..."), NULL }; @@ -52,14 +52,6 @@ static int verify_commit(const char *name, unsigned flags) return run_gpg_verify((struct commit *)obj, flags); } -static int git_verify_commit_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - int cmd_verify_commit(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; @@ -70,7 +62,7 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_verify_commit_config, NULL); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, verify_commit_options, verify_commit_usage, PARSE_OPT_KEEP_ARGV0); diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index 05c5213594..27d6f75fd8 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -56,7 +56,7 @@ static int verify_one_pack(const char *path, unsigned int flags, const char *has } static const char * const verify_pack_usage[] = { - N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."), + N_("git verify-pack [-v | --verbose] [-s | --stat-only] [--] <pack>.idx..."), NULL }; diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index f45136a06b..5c00b0b0f7 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -15,18 +15,10 @@ #include "ref-filter.h" static const char * const verify_tag_usage[] = { - N_("git verify-tag [-v | --verbose] [--format=<format>] <tag>..."), + N_("git verify-tag [-v | --verbose] [--format=<format>] [--raw] <tag>..."), NULL }; -static int git_verify_tag_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; @@ -39,7 +31,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_verify_tag_config, NULL); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, verify_tag_options, verify_tag_usage, PARSE_OPT_KEEP_ARGV0); diff --git a/builtin/worktree.c b/builtin/worktree.c index cd62eef240..80d05e246d 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -3,6 +3,7 @@ #include "config.h" #include "builtin.h" #include "dir.h" +#include "hex.h" #include "parse-options.h" #include "strvec.h" #include "branch.h" @@ -15,15 +16,73 @@ #include "worktree.h" #include "quote.h" -static const char * const worktree_usage[] = { - N_("git worktree add [<options>] <path> [<commit-ish>]"), - N_("git worktree list [<options>]"), - N_("git worktree lock [<options>] <path>"), - N_("git worktree move <worktree> <new-path>"), - N_("git worktree prune [<options>]"), - N_("git worktree remove [<options>] <worktree>"), - N_("git worktree repair [<path>...]"), - N_("git worktree unlock <path>"), +#define BUILTIN_WORKTREE_ADD_USAGE \ + N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \ + " [-b <new-branch>] <path> [<commit-ish>]") +#define BUILTIN_WORKTREE_LIST_USAGE \ + N_("git worktree list [-v | --porcelain [-z]]") +#define BUILTIN_WORKTREE_LOCK_USAGE \ + N_("git worktree lock [--reason <string>] <worktree>") +#define BUILTIN_WORKTREE_MOVE_USAGE \ + N_("git worktree move <worktree> <new-path>") +#define BUILTIN_WORKTREE_PRUNE_USAGE \ + N_("git worktree prune [-n] [-v] [--expire <expire>]") +#define BUILTIN_WORKTREE_REMOVE_USAGE \ + N_("git worktree remove [-f] <worktree>") +#define BUILTIN_WORKTREE_REPAIR_USAGE \ + N_("git worktree repair [<path>...]") +#define BUILTIN_WORKTREE_UNLOCK_USAGE \ + N_("git worktree unlock <worktree>") + +static const char * const git_worktree_usage[] = { + BUILTIN_WORKTREE_ADD_USAGE, + BUILTIN_WORKTREE_LIST_USAGE, + BUILTIN_WORKTREE_LOCK_USAGE, + BUILTIN_WORKTREE_MOVE_USAGE, + BUILTIN_WORKTREE_PRUNE_USAGE, + BUILTIN_WORKTREE_REMOVE_USAGE, + BUILTIN_WORKTREE_REPAIR_USAGE, + BUILTIN_WORKTREE_UNLOCK_USAGE, + NULL +}; + +static const char * const git_worktree_add_usage[] = { + BUILTIN_WORKTREE_ADD_USAGE, + NULL, +}; + +static const char * const git_worktree_list_usage[] = { + BUILTIN_WORKTREE_LIST_USAGE, + NULL +}; + +static const char * const git_worktree_lock_usage[] = { + BUILTIN_WORKTREE_LOCK_USAGE, + NULL +}; + +static const char * const git_worktree_move_usage[] = { + BUILTIN_WORKTREE_MOVE_USAGE, + NULL +}; + +static const char * const git_worktree_prune_usage[] = { + BUILTIN_WORKTREE_PRUNE_USAGE, + NULL +}; + +static const char * const git_worktree_remove_usage[] = { + BUILTIN_WORKTREE_REMOVE_USAGE, + NULL +}; + +static const char * const git_worktree_repair_usage[] = { + BUILTIN_WORKTREE_REPAIR_USAGE, + NULL +}; + +static const char * const git_worktree_unlock_usage[] = { + BUILTIN_WORKTREE_UNLOCK_USAGE, NULL }; @@ -115,7 +174,7 @@ static void prune_worktrees(void) { struct strbuf reason = STRBUF_INIT; struct strbuf main_path = STRBUF_INIT; - struct string_list kept = STRING_LIST_INIT_NODUP; + struct string_list kept = STRING_LIST_INIT_DUP; DIR *dir = opendir(git_path("worktrees")); struct dirent *d; if (!dir) @@ -126,14 +185,14 @@ static void prune_worktrees(void) if (should_prune_worktree(d->d_name, &reason, &path, expire)) prune_worktree(d->d_name, reason.buf); else if (path) - string_list_append(&kept, path)->util = xstrdup(d->d_name); + string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name); } closedir(dir); strbuf_add_absolute_path(&main_path, get_git_common_dir()); /* massage main worktree absolute path to match 'gitdir' content */ strbuf_strip_suffix(&main_path, "/."); - string_list_append(&kept, strbuf_detach(&main_path, NULL)); + string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL)); prune_dups(&kept); string_list_clear(&kept, 1); @@ -153,9 +212,10 @@ static int prune(int ac, const char **av, const char *prefix) }; expire = TIME_MAX; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_prune_usage, + 0); if (ac) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_prune_usage, options); prune_worktrees(); return 0; } @@ -570,10 +630,11 @@ static int add(int ac, const char **av, const char *prefix) N_("try to match the new branch name with a remote-tracking branch")), OPT_END() }; + int ret; memset(&opts, 0, sizeof(opts)); opts.checkout = 1; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0); if (!!opts.detach + !!new_branch + !!new_branch_force > 1) die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach"); if (lock_reason && !keep_locked) @@ -584,7 +645,7 @@ static int add(int ac, const char **av, const char *prefix) opts.keep_locked = _("added with --lock"); if (ac < 1 || ac > 2) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_add_usage, options); path = prefix_filename(prefix, av[0]); branch = ac < 2 ? "HEAD" : av[1]; @@ -646,9 +707,9 @@ static int add(int ac, const char **av, const char *prefix) die(_("--[no-]track can only be used if a new branch is created")); } - UNLEAK(path); - UNLEAK(opts); - return add_worktree(path, branch, &opts); + ret = add_worktree(path, branch, &opts); + free(path); + return ret; } static void show_worktree_porcelain(struct worktree *wt, int line_terminator) @@ -772,9 +833,9 @@ static int list(int ac, const char **av, const char *prefix) }; expire = TIME_MAX; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_list_usage, 0); if (ac) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_list_usage, options); else if (verbose && porcelain) die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain"); else if (!line_terminator && !porcelain) @@ -811,9 +872,9 @@ static int lock_worktree(int ac, const char **av, const char *prefix) }; struct worktree **worktrees, *wt; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0); if (ac != 1) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_lock_usage, options); worktrees = get_worktrees(); wt = find_worktree(worktrees, prefix, av[0]); @@ -844,9 +905,9 @@ static int unlock_worktree(int ac, const char **av, const char *prefix) struct worktree **worktrees, *wt; int ret; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0); if (ac != 1) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_unlock_usage, options); worktrees = get_worktrees(); wt = find_worktree(worktrees, prefix, av[0]); @@ -863,7 +924,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix) static void validate_no_submodules(const struct worktree *wt) { - struct index_state istate = { NULL }; + struct index_state istate = INDEX_STATE_INIT(the_repository); struct strbuf path = STRBUF_INIT; int i, found_submodules = 0; @@ -914,9 +975,10 @@ static int move_worktree(int ac, const char **av, const char *prefix) const char *reason = NULL; char *path; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_move_usage, + 0); if (ac != 2) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_move_usage, options); path = prefix_filename(prefix, av[1]); strbuf_addstr(&dst, path); @@ -1042,9 +1104,9 @@ static int remove_worktree(int ac, const char **av, const char *prefix) const char *reason = NULL; int ret = 0; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_remove_usage, 0); if (ac != 1) - usage_with_options(worktree_usage, options); + usage_with_options(git_worktree_remove_usage, options); worktrees = get_worktrees(); wt = find_worktree(worktrees, prefix, av[0]); @@ -1102,7 +1164,7 @@ static int repair(int ac, const char **av, const char *prefix) }; int rc = 0; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0); p = ac > 0 ? av : self; for (; *p; p++) repair_worktree_at_path(*p, report_repair, &rc); @@ -1112,31 +1174,24 @@ static int repair(int ac, const char **av, const char *prefix) int cmd_worktree(int ac, const char **av, const char *prefix) { + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { + OPT_SUBCOMMAND("add", &fn, add), + OPT_SUBCOMMAND("prune", &fn, prune), + OPT_SUBCOMMAND("list", &fn, list), + OPT_SUBCOMMAND("lock", &fn, lock_worktree), + OPT_SUBCOMMAND("unlock", &fn, unlock_worktree), + OPT_SUBCOMMAND("move", &fn, move_worktree), + OPT_SUBCOMMAND("remove", &fn, remove_worktree), + OPT_SUBCOMMAND("repair", &fn, repair), OPT_END() }; git_config(git_worktree_config, NULL); - if (ac < 2) - usage_with_options(worktree_usage, options); if (!prefix) prefix = ""; - if (!strcmp(av[1], "add")) - return add(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "prune")) - return prune(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "list")) - return list(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "lock")) - return lock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "unlock")) - return unlock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "move")) - return move_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "remove")) - return remove_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "repair")) - return repair(ac - 1, av + 1, prefix); - usage_with_options(worktree_usage, options); + + ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0); + return fn(ac, av, prefix); } diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 45d61707e7..7ad0d05945 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -3,10 +3,11 @@ * * Copyright (C) Linus Torvalds, 2005 */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS +#define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "cache.h" #include "config.h" +#include "hex.h" #include "tree.h" #include "cache-tree.h" #include "parse-options.h" @@ -38,7 +39,8 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix) argc = parse_options(argc, argv, cmd_prefix, write_tree_options, write_tree_usage, 0); - ret = write_cache_as_tree(&oid, flags, tree_prefix); + ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags, + tree_prefix); switch (ret) { case 0: printf("%s\n", oid_to_hex(&oid)); |
