diff options
Diffstat (limited to 'builtin')
86 files changed, 3038 insertions, 1689 deletions
diff --git a/builtin/add.c b/builtin/add.c index 0057392f98..393c10cbcf 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -12,14 +12,11 @@ #include "dir.h" #include "gettext.h" #include "pathspec.h" -#include "exec-cmd.h" -#include "cache-tree.h" #include "run-command.h" #include "parse-options.h" #include "path.h" #include "preload-index.h" #include "diff.h" -#include "diffcore.h" #include "read-cache.h" #include "repository.h" #include "revision.h" @@ -118,7 +115,7 @@ static int refresh(int verbose, const struct pathspec *pathspec) int i, ret = 0; char *skip_worktree_seen = NULL; struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP; - int flags = REFRESH_IGNORE_SKIP_WORKTREE | + unsigned int flags = REFRESH_IGNORE_SKIP_WORKTREE | (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET); seen = xcalloc(pathspec->nr, 1); @@ -231,6 +228,8 @@ static char *chmod_arg; static int ignore_removal_cb(const struct option *opt, const char *arg, int unset) { + BUG_ON_OPT_ARG(arg); + /* if we are told to ignore, we are not adding removals */ *(int *)opt->value = !unset ? 0 : 1; return 0; @@ -422,7 +421,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ - parse_pathspec(&pathspec, PATHSPEC_ATTR, + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); @@ -431,7 +430,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (pathspec.nr) die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file"); - parse_pathspec_file(&pathspec, PATHSPEC_ATTR, + parse_pathspec_file(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, pathspec_from_file, pathspec_file_nul); @@ -502,7 +501,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | - PATHSPEC_EXCLUDE); + PATHSPEC_EXCLUDE | + PATHSPEC_ATTR); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; diff --git a/builtin/am.c b/builtin/am.c index 6655059a57..e8fb27a8ef 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -10,7 +10,6 @@ #include "config.h" #include "editor.h" #include "environment.h" -#include "exec-cmd.h" #include "gettext.h" #include "hex.h" #include "parse-options.h" @@ -24,7 +23,6 @@ #include "refs.h" #include "commit.h" #include "diff.h" -#include "diffcore.h" #include "unpack-trees.h" #include "branch.h" #include "object-name.h" @@ -35,11 +33,9 @@ #include "log-tree.h" #include "notes-utils.h" #include "rerere.h" -#include "prompt.h" #include "mailinfo.h" #include "apply.h" #include "string-list.h" -#include "packfile.h" #include "pager.h" #include "path.h" #include "repository.h" @@ -92,9 +88,16 @@ enum signoff_type { SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ }; -enum show_patch_type { - SHOW_PATCH_RAW = 0, - SHOW_PATCH_DIFF = 1, +enum resume_type { + RESUME_FALSE = 0, + RESUME_APPLY, + RESUME_RESOLVED, + RESUME_SKIP, + RESUME_ABORT, + RESUME_QUIT, + RESUME_SHOW_PATCH_RAW, + RESUME_SHOW_PATCH_DIFF, + RESUME_ALLOW_EMPTY, }; enum empty_action { @@ -1991,8 +1994,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0; opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */ opts.fn = twoway_merge; - init_tree_desc(&t[0], head->buffer, head->size); - init_tree_desc(&t[1], remote->buffer, remote->size); + init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size); + init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size); if (unpack_trees(2, t, &opts)) { rollback_lock_file(&lock_file); @@ -2026,7 +2029,7 @@ static int merge_tree(struct tree *tree) opts.dst_index = &the_index; opts.merge = 1; opts.fn = oneway_merge; - init_tree_desc(&t[0], tree->buffer, tree->size); + init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, t, &opts)) { rollback_lock_file(&lock_file); @@ -2191,7 +2194,7 @@ static void am_abort(struct am_state *state) am_destroy(state); } -static int show_patch(struct am_state *state, enum show_patch_type sub_mode) +static int show_patch(struct am_state *state, enum resume_type resume_mode) { struct strbuf sb = STRBUF_INIT; const char *patch_path; @@ -2206,11 +2209,11 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode) return run_command(&cmd); } - switch (sub_mode) { - case SHOW_PATCH_RAW: + switch (resume_mode) { + case RESUME_SHOW_PATCH_RAW: patch_path = am_path(state, msgnum(state)); break; - case SHOW_PATCH_DIFF: + case RESUME_SHOW_PATCH_DIFF: patch_path = am_path(state, "patch"); break; default: @@ -2257,57 +2260,25 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int return 0; } -enum resume_type { - RESUME_FALSE = 0, - RESUME_APPLY, - RESUME_RESOLVED, - RESUME_SKIP, - RESUME_ABORT, - RESUME_QUIT, - RESUME_SHOW_PATCH, - RESUME_ALLOW_EMPTY, -}; - -struct resume_mode { - enum resume_type mode; - enum show_patch_type sub_mode; -}; - static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset) { int *opt_value = opt->value; - struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode); + BUG_ON_OPT_NEG(unset); + + if (!arg) + *opt_value = opt->defval; + else if (!strcmp(arg, "raw")) + *opt_value = RESUME_SHOW_PATCH_RAW; + else if (!strcmp(arg, "diff")) + *opt_value = RESUME_SHOW_PATCH_DIFF; /* * Please update $__git_showcurrentpatch in git-completion.bash * when you add new options */ - const char *valid_modes[] = { - [SHOW_PATCH_DIFF] = "diff", - [SHOW_PATCH_RAW] = "raw" - }; - int new_value = SHOW_PATCH_RAW; - - BUG_ON_OPT_NEG(unset); - - if (arg) { - for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) { - if (!strcmp(arg, valid_modes[new_value])) - break; - } - if (new_value >= ARRAY_SIZE(valid_modes)) - return error(_("invalid value for '%s': '%s'"), - "--show-current-patch", arg); - } - - if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode) - return error(_("options '%s=%s' and '%s=%s' " - "cannot be used together"), - "--show-current-patch", arg, - "--show-current-patch", valid_modes[resume->sub_mode]); - - resume->mode = RESUME_SHOW_PATCH; - resume->sub_mode = new_value; + else + return error(_("invalid value for '%s': '%s'"), + "--show-current-patch", arg); return 0; } @@ -2317,7 +2288,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) int binary = -1; int keep_cr = -1; int patch_format = PATCH_FORMAT_UNKNOWN; - struct resume_mode resume = { .mode = RESUME_FALSE }; + enum resume_type resume_mode = RESUME_FALSE; int in_progress; int ret = 0; @@ -2388,27 +2359,27 @@ int cmd_am(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG), OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL, N_("override error message when patch failure occurs")), - OPT_CMDMODE(0, "continue", &resume.mode, + OPT_CMDMODE(0, "continue", &resume_mode, N_("continue applying patches after resolving a conflict"), RESUME_RESOLVED), - OPT_CMDMODE('r', "resolved", &resume.mode, + OPT_CMDMODE('r', "resolved", &resume_mode, N_("synonyms for --continue"), RESUME_RESOLVED), - OPT_CMDMODE(0, "skip", &resume.mode, + OPT_CMDMODE(0, "skip", &resume_mode, N_("skip the current patch"), RESUME_SKIP), - OPT_CMDMODE(0, "abort", &resume.mode, + OPT_CMDMODE(0, "abort", &resume_mode, N_("restore the original branch and abort the patching operation"), RESUME_ABORT), - OPT_CMDMODE(0, "quit", &resume.mode, + OPT_CMDMODE(0, "quit", &resume_mode, N_("abort the patching operation but keep HEAD where it is"), RESUME_QUIT), - { OPTION_CALLBACK, 0, "show-current-patch", &resume.mode, + { OPTION_CALLBACK, 0, "show-current-patch", &resume_mode, "(diff|raw)", N_("show the patch being applied"), PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, - parse_opt_show_current_patch, RESUME_SHOW_PATCH }, - OPT_CMDMODE(0, "allow-empty", &resume.mode, + parse_opt_show_current_patch, RESUME_SHOW_PATCH_RAW }, + OPT_CMDMODE(0, "allow-empty", &resume_mode, N_("record the empty patch as an empty commit"), RESUME_ALLOW_EMPTY), OPT_BOOL(0, "committer-date-is-author-date", @@ -2420,7 +2391,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}", + OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)", N_("how to handle empty patches"), PARSE_OPT_NONEG, am_option_parse_empty), OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing, @@ -2463,12 +2434,12 @@ int cmd_am(int argc, const char **argv, const char *prefix) * intend to feed us a patch but wanted to continue * unattended. */ - if (argc || (resume.mode == RESUME_FALSE && !isatty(0))) + if (argc || (resume_mode == RESUME_FALSE && !isatty(0))) die(_("previous rebase directory %s still exists but mbox given."), state.dir); - if (resume.mode == RESUME_FALSE) - resume.mode = RESUME_APPLY; + if (resume_mode == RESUME_FALSE) + resume_mode = RESUME_APPLY; if (state.signoff == SIGNOFF_EXPLICIT) am_append_signoff(&state); @@ -2482,7 +2453,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) * stray directories. */ if (file_exists(state.dir) && !state.rebasing) { - if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) { + if (resume_mode == RESUME_ABORT || resume_mode == RESUME_QUIT) { am_destroy(&state); am_state_release(&state); return 0; @@ -2493,7 +2464,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) state.dir); } - if (resume.mode) + if (resume_mode) die(_("Resolve operation not in progress, we are not resuming.")); for (i = 0; i < argc; i++) { @@ -2511,7 +2482,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) strvec_clear(&paths); } - switch (resume.mode) { + switch (resume_mode) { case RESUME_FALSE: am_run(&state, 0); break; @@ -2520,7 +2491,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) break; case RESUME_RESOLVED: case RESUME_ALLOW_EMPTY: - am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0); + am_resolve(&state, resume_mode == RESUME_ALLOW_EMPTY ? 1 : 0); break; case RESUME_SKIP: am_skip(&state); @@ -2532,8 +2503,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) am_rerere_clear(); am_destroy(&state); break; - case RESUME_SHOW_PATCH: - ret = show_patch(&state, resume.sub_mode); + case RESUME_SHOW_PATCH_RAW: + case RESUME_SHOW_PATCH_DIFF: + ret = show_patch(&state, resume_mode); break; default: BUG("invalid resume value"); diff --git a/builtin/apply.c b/builtin/apply.c index c18b7ea5d3..861a01910c 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1,6 +1,5 @@ #include "builtin.h" #include "gettext.h" -#include "parse-options.h" #include "repository.h" #include "apply.h" diff --git a/builtin/archive.c b/builtin/archive.c index 90761fdfee..15ee1ec7bb 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -9,7 +9,6 @@ #include "parse-options.h" #include "pkt-line.h" #include "repository.h" -#include "sideband.h" static void create_output_file(const char *output_file) { diff --git a/builtin/bisect.c b/builtin/bisect.c index 65478ef40f..f69c3f7e43 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c @@ -7,7 +7,6 @@ #include "parse-options.h" #include "bisect.h" #include "refs.h" -#include "dir.h" #include "strvec.h" #include "run-command.h" #include "oid-array.h" @@ -17,7 +16,6 @@ #include "revision.h" static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") -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") @@ -26,7 +24,7 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") #define BUILTIN_GIT_BISECT_START_USAGE \ - N_("git bisect start [--term-{new,bad}=<term> --term-{old,good}=<term>]" \ + 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 \ @@ -46,7 +44,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") #define BUILTIN_GIT_BISECT_LOG_USAGE \ "git bisect log" #define BUILTIN_GIT_BISECT_RUN_USAGE \ - N_("git bisect run <cmd>...") + N_("git bisect run <cmd> [<arg>...]") static const char * const git_bisect_usage[] = { BUILTIN_GIT_BISECT_START_USAGE, @@ -233,11 +231,10 @@ static int bisect_reset(const char *commit) struct strbuf branch = STRBUF_INIT; if (!commit) { - if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) { + if (!strbuf_read_file(&branch, git_path_bisect_start(), 0)) printf(_("We are not bisecting.\n")); - return 0; - } - strbuf_rtrim(&branch); + else + strbuf_rtrim(&branch); } else { struct object_id oid; @@ -246,7 +243,7 @@ static int bisect_reset(const char *commit) strbuf_addstr(&branch, commit); } - if (!ref_exists("BISECT_HEAD")) { + if (branch.len && !ref_exists("BISECT_HEAD")) { struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; @@ -921,7 +918,6 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, const char *state; int i, verify_expected = 1; struct object_id oid, expected; - struct strbuf buf = STRBUF_INIT; struct oid_array revs = OID_ARRAY_INIT; if (!argc) @@ -976,10 +972,8 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, oid_array_append(&revs, &commit->object.oid); } - if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz || - get_oid_hex(buf.buf, &expected) < 0) + if (read_ref("BISECT_EXPECTED_REV", &expected)) verify_expected = 0; /* Ignore invalid file contents */ - strbuf_release(&buf); for (i = 0; i < revs.nr; i++) { if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) { @@ -988,7 +982,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc, } if (verify_expected && !oideq(&revs.oid[i], &expected)) { unlink_or_warn(git_path_bisect_ancestors_ok()); - unlink_or_warn(git_path_bisect_expected_rev()); + delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF); verify_expected = 0; } } diff --git a/builtin/blame.c b/builtin/blame.c index 9c987d6567..db1f56de61 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -25,7 +25,6 @@ #include "userdiff.h" #include "line-range.h" #include "line-log.h" -#include "dir.h" #include "progress.h" #include "object-name.h" #include "object-store-ll.h" @@ -748,6 +747,8 @@ static int git_blame_config(const char *var, const char *value, } if (!strcmp(var, "blame.coloring")) { + if (!value) + return config_error_nonbool(var); if (!strcmp(value, "repeatedLines")) { coloring_mode |= OUTPUT_COLOR_LINE; } else if (!strcmp(value, "highlightRecent")) { diff --git a/builtin/branch.c b/builtin/branch.c index 08da650516..8c2305ad2c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -17,16 +17,14 @@ #include "remote.h" #include "parse-options.h" #include "branch.h" -#include "diff.h" #include "path.h" -#include "revision.h" #include "string-list.h" #include "column.h" #include "utf8.h" -#include "wt-status.h" #include "ref-filter.h" #include "worktree.h" #include "help.h" +#include "advice.h" #include "commit-reach.h" static const char * const builtin_branch_usage[] = { @@ -45,7 +43,6 @@ static const char *head; static struct object_id head_oid; static int recurse_submodules = 0; static int submodule_propagate_branches = 0; -static int omit_empty = 0; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { @@ -161,6 +158,8 @@ static int branch_merged(int kind, const char *name, merged = reference_rev ? repo_in_merge_bases(the_repository, rev, reference_rev) : 0; + if (merged < 0) + exit(128); /* * After the safety valve is fully redefined to "check with @@ -169,15 +168,19 @@ static int branch_merged(int kind, const char *name, * any of the following code, but during the transition period, * a gentle reminder is in order. */ - if ((head_rev != reference_rev) && - (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) { - if (merged) + if (head_rev != reference_rev) { + int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0; + if (expect < 0) + exit(128); + if (expect == merged) + ; /* okay */ + else if (merged) warning(_("deleting branch '%s' that has been merged to\n" - " '%s', but not yet merged to HEAD."), + " '%s', but not yet merged to HEAD"), name, reference_name); else warning(_("not deleting branch '%s' that is not yet merged to\n" - " '%s', even though it is merged to HEAD."), + " '%s', even though it is merged to HEAD"), name, reference_name); } free(reference_name_to_free); @@ -190,13 +193,14 @@ static int check_branch_commit(const char *branchname, const char *refname, { struct commit *rev = lookup_commit_reference(the_repository, oid); if (!force && !rev) { - error(_("Couldn't look up commit object for '%s'"), refname); + error(_("couldn't look up commit object for '%s'"), refname); return -1; } if (!force && !branch_merged(kinds, branchname, rev, head_rev)) { - error(_("The branch '%s' is not fully merged.\n" - "If you are sure you want to delete it, " - "run 'git branch -D %s'."), branchname, branchname); + error(_("the branch '%s' is not fully merged"), branchname); + advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH, + _("If you are sure you want to delete it, " + "run 'git branch -D %s'"), branchname); return -1; } return 0; @@ -207,7 +211,7 @@ static void delete_branch_config(const char *branchname) struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "branch.%s", branchname); if (git_config_rename_section(buf.buf, NULL) < 0) - warning(_("Update of config-file failed")); + warning(_("update of config-file failed")); strbuf_release(&buf); } @@ -260,8 +264,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, if (kinds == FILTER_REFS_BRANCHES) { const char *path; if ((path = branch_checked_out(name))) { - error(_("Cannot delete branch '%s' " - "checked out at '%s'"), + error(_("cannot delete branch '%s' " + "used by worktree at '%s'"), bname.buf, path); ret = 1; continue; @@ -275,7 +279,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, &oid, &flags); if (!target) { if (remote_branch) { - error(_("remote-tracking branch '%s' not found."), bname.buf); + error(_("remote-tracking branch '%s' not found"), bname.buf); } else { char *virtual_name = mkpathdup(fmt_remotes, bname.buf); char *virtual_target = resolve_refdup(virtual_name, @@ -290,7 +294,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, "Did you forget --remote?"), bname.buf); else - error(_("branch '%s' not found."), bname.buf); + error(_("branch '%s' not found"), bname.buf); FREE_AND_NULL(virtual_target); } ret = 1; @@ -438,8 +442,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin { int i; struct ref_array array; - struct strbuf out = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; int maxwidth = 0; const char *remote_prefix = ""; char *to_free = NULL; @@ -469,24 +471,27 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin filter_ahead_behind(the_repository, format, &array); ref_array_sort(sorting, &array); - for (i = 0; i < array.nr; i++) { - strbuf_reset(&err); - strbuf_reset(&out); - if (format_ref_array_item(array.items[i], format, &out, &err)) - die("%s", err.buf); - if (column_active(colopts)) { - assert(!filter->verbose && "--column and --verbose are incompatible"); - /* format to a string_list to let print_columns() do its job */ + if (column_active(colopts)) { + struct strbuf out = STRBUF_INIT, err = STRBUF_INIT; + + assert(!filter->verbose && "--column and --verbose are incompatible"); + + for (i = 0; i < array.nr; i++) { + strbuf_reset(&err); + strbuf_reset(&out); + if (format_ref_array_item(array.items[i], format, &out, &err)) + die("%s", err.buf); + + /* format to a string_list to let print_columns() do its job */ string_list_append(output, out.buf); - } else { - fwrite(out.buf, 1, out.len, stdout); - if (out.len || !omit_empty) - putchar('\n'); } + + strbuf_release(&err); + strbuf_release(&out); + } else { + print_formatted_ref_array(&array, format); } - strbuf_release(&err); - strbuf_release(&out); ref_array_clear(&array); free(to_free); } @@ -518,11 +523,11 @@ static void reject_rebase_or_bisect_branch(struct worktree **worktrees, continue; if (is_worktree_being_rebased(wt, target)) - die(_("Branch %s is being rebased at %s"), + die(_("branch %s is being rebased at %s"), target, wt->path); if (is_worktree_being_bisected(wt, target)) - die(_("Branch %s is being bisected at %s"), + die(_("branch %s is being bisected at %s"), target, wt->path); } } @@ -577,8 +582,12 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int */ if (ref_exists(oldref.buf)) recovery = 1; - else - die(_("Invalid branch name: '%s'"), oldname); + else { + int code = die_message(_("invalid branch name: '%s'"), oldname); + advise_if_enabled(ADVICE_REF_SYNTAX, + _("See `man git check-ref-format`")); + exit(code); + } } for (int i = 0; worktrees[i]; i++) { @@ -594,9 +603,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) { if (oldref_usage & IS_HEAD) - die(_("No commit on branch '%s' yet."), oldname); + die(_("no commit on branch '%s' yet"), oldname); else - die(_("No branch named '%s'."), oldname); + die(_("no branch named '%s'"), oldname); } /* @@ -624,32 +633,32 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if (!copy && !(oldref_usage & IS_ORPHAN) && rename_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch rename failed")); + die(_("branch rename failed")); if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch copy failed")); + die(_("branch copy failed")); if (recovery) { if (copy) - warning(_("Created a copy of a misnamed branch '%s'"), + warning(_("created a copy of a misnamed branch '%s'"), interpreted_oldname); else - warning(_("Renamed a misnamed branch '%s' away"), + warning(_("renamed a misnamed branch '%s' away"), interpreted_oldname); } if (!copy && (oldref_usage & IS_HEAD) && replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch renamed to %s, but HEAD is not updated!"), newname); + die(_("branch renamed to %s, but HEAD is not updated"), newname); strbuf_release(&logmsg); strbuf_addf(&oldsection, "branch.%s", interpreted_oldname); strbuf_addf(&newsection, "branch.%s", interpreted_newname); if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0) - die(_("Branch is renamed, but update of config-file failed")); + die(_("branch is renamed, but update of config-file failed")); 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")); + die(_("branch is copied, but update of config-file failed")); strbuf_release(&oldref); strbuf_release(&newref); strbuf_release(&oldsection); @@ -737,7 +746,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2), - OPT_BOOL(0, "omit-empty", &omit_empty, + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, N_("do not output a newline after empty formatted refs")), OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1), OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2), @@ -767,13 +776,19 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_branch_usage, options); + /* + * Try to set sort keys from config. If config does not set any, + * fall back on default (refname) sorting. + */ git_config(git_branch_config, &sorting_options); + if (!sorting_options.nr) + string_list_append(&sorting_options, "refname"); track = git_branch_track; head = resolve_refdup("HEAD", 0, &head_oid, NULL); if (!head) - die(_("Failed to resolve HEAD as a valid ref.")); + die(_("failed to resolve HEAD as a valid ref")); if (!strcmp(head, "HEAD")) filter.detached = 1; else if (!skip_prefix(head, "refs/heads/", &head)) @@ -866,7 +881,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!argc) { if (filter.detached) - die(_("Cannot give description to detached HEAD")); + die(_("cannot give description to detached HEAD")); branch_name = head; } else if (argc == 1) { strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL); @@ -878,8 +893,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); if (!ref_exists(branch_ref.buf)) error((!argc || branch_checked_out(branch_ref.buf)) - ? _("No commit on branch '%s' yet.") - : _("No branch named '%s'."), + ? _("no commit on branch '%s' yet") + : _("no branch named '%s'"), branch_name); else if (!edit_branch_description(branch_name)) ret = 0; /* happy */ @@ -892,8 +907,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) 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.")); + 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], copy, copy + rename > 1); else if (argc == 2) @@ -916,14 +931,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch) { if (!argc || !strcmp(argv[0], "HEAD")) die(_("could not set upstream of HEAD to %s when " - "it does not point to any branch."), + "it does not point to any branch"), new_upstream); die(_("no such branch '%s'"), argv[0]); } if (!ref_exists(branch->refname)) { if (!argc || branch_checked_out(branch->refname)) - die(_("No commit on branch '%s' yet."), branch->name); + die(_("no commit on branch '%s' yet"), branch->name); die(_("branch '%s' does not exist"), branch->name); } @@ -946,12 +961,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch) { if (!argc || !strcmp(argv[0], "HEAD")) die(_("could not unset upstream of HEAD when " - "it does not point to any branch.")); + "it does not point to any branch")); die(_("no such branch '%s'"), argv[0]); } if (!branch_has_merge_config(branch)) - die(_("Branch '%s' has no upstream information"), branch->name); + die(_("branch '%s' has no upstream information"), branch->name); strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", branch->name); @@ -965,11 +980,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) const char *start_name = argc == 2 ? argv[1] : head; if (filter.kind != FILTER_REFS_BRANCHES) - die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n" + die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n" "Did you mean to use: -a|-r --list <pattern>?")); if (track == BRANCH_TRACK_OVERRIDE) - die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); + die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead")); if (recurse_submodules) { create_branches_recursively(the_repository, branch_name, diff --git a/builtin/bugreport.c b/builtin/bugreport.c index d2ae5c305d..25f860a0d9 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -64,7 +64,8 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit) } static const char * const bugreport_usage[] = { - N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n" + N_("git bugreport [(-o | --output-directory) <path>]\n" + " [(-s | --suffix) <format> | --no-suffix]\n" " [--diagnose[=<mode>]]"), NULL }; @@ -126,6 +127,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, bugreport_options, bugreport_usage, 0); + if (argc) { + error(_("unknown argument `%s'"), argv[0]); + usage(bugreport_usage[0]); + } + /* Prepare the path to put the result */ prefixed_filename = prefix_filename(prefix, option_output ? option_output : ""); @@ -133,8 +139,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix) 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); + strbuf_addstr(&report_path, "git-bugreport"); + if (option_suffix) { + strbuf_addch(&report_path, '-'); + strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0); + } strbuf_addstr(&report_path, ".txt"); switch (safe_create_leading_directories(report_path.buf)) { diff --git a/builtin/cat-file.c b/builtin/cat-file.c index ea8ad601ec..3f5ce7d34c 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -15,7 +15,6 @@ #include "parse-options.h" #include "userdiff.h" #include "streaming.h" -#include "tree-walk.h" #include "oid-array.h" #include "packfile.h" #include "object-file.h" @@ -107,7 +106,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, struct object_info oi = OBJECT_INFO_INIT; struct strbuf sb = STRBUF_INIT; unsigned flags = OBJECT_INFO_LOOKUP_REPLACE; - unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE; + unsigned get_oid_flags = + GET_OID_RECORD_PATH | + GET_OID_ONLY_TO_DIE | + GET_OID_HASH_ANY; const char *path = force_path; const int opt_cw = (opt == 'c' || opt == 'w'); if (!path && opt_cw) @@ -222,8 +224,13 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, &type, &size); const char *target; + + if (!buffer) + die(_("unable to read %s"), oid_to_hex(&oid)); + if (!skip_prefix(buffer, "object ", &target) || - get_oid_hex(target, &blob_oid)) + get_oid_hex_algop(target, &blob_oid, + &hash_algos[oid.algo])) die("%s not a valid tag", oid_to_hex(&oid)); free(buffer); } else @@ -417,6 +424,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d contents = repo_read_object_file(the_repository, oid, &type, &size); + if (!contents) + die("object %s disappeared", oid_to_hex(oid)); if (use_mailmap) { size_t s = size; @@ -424,8 +433,6 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d 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 && !use_mailmap) @@ -482,6 +489,8 @@ static void batch_object_write(const char *obj_name, buf = repo_read_object_file(the_repository, &data->oid, &data->type, &data->size); + if (!buf) + die(_("unable to read %s"), oid_to_hex(&data->oid)); buf = replace_idents_using_mailmap(buf, &s); data->size = cast_size_t_to_ulong(s); @@ -512,7 +521,9 @@ static void batch_one_object(const char *obj_name, struct expand_data *data) { struct object_context ctx; - int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0; + int flags = + GET_OID_HASH_ANY | + (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0); enum get_oid_result result; result = get_oid_with_context(the_repository, obj_name, diff --git a/builtin/check-attr.c b/builtin/check-attr.c index b22ff748c3..c1da1d184e 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -122,6 +122,9 @@ 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); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index f62f13f2b5..2e086a204d 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -7,7 +7,6 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "config.h" -#include "dir.h" #include "gettext.h" #include "lockfile.h" #include "quote.h" @@ -24,7 +23,7 @@ static int nul_term_line; static int checkout_stage; /* default to checkout stage0 */ static int ignore_skip_worktree; /* default to 0 */ -static int to_tempfile; +static int to_tempfile = -1; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static struct checkout state = CHECKOUT_INIT; @@ -193,15 +192,16 @@ static const char * const builtin_checkout_index_usage[] = { static int option_parse_stage(const struct option *opt, const char *arg, int unset) { + int *stage = opt->value; + BUG_ON_OPT_NEG(unset); if (!strcmp(arg, "all")) { - to_tempfile = 1; - checkout_stage = CHECKOUT_ALL; + *stage = CHECKOUT_ALL; } else { int ch = arg[0]; if ('1' <= ch && ch <= '3') - checkout_stage = arg[0] - '0'; + *stage = arg[0] - '0'; else die(_("stage should be between 1 and 3 or all")); } @@ -239,7 +239,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) N_("write the content to temporary files")), OPT_STRING(0, "prefix", &state.base_dir, N_("string"), N_("when creating files, prepend <string>")), - OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)", + OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)", N_("copy out the files from named stage"), PARSE_OPT_NONEG, option_parse_stage), OPT_END() @@ -269,6 +269,12 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) state.base_dir = ""; state.base_dir_len = strlen(state.base_dir); + if (to_tempfile < 0) + to_tempfile = (checkout_stage == CHECKOUT_ALL); + if (!to_tempfile && checkout_stage == CHECKOUT_ALL) + die(_("options '%s' and '%s' cannot be used together"), + "--stage=all", "--no-temp"); + /* * when --prefix is specified we do not want to update cache. */ diff --git a/builtin/checkout.c b/builtin/checkout.c index f53612f468..2b6166c284 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,7 +1,6 @@ #define USE_THE_INDEX_VARIABLE #include "builtin.h" #include "advice.h" -#include "blob.h" #include "branch.h" #include "cache-tree.h" #include "checkout.h" @@ -27,10 +26,8 @@ #include "remote.h" #include "resolve-undo.h" #include "revision.h" -#include "run-command.h" #include "setup.h" #include "submodule.h" -#include "submodule-config.h" #include "symlinks.h" #include "trace2.h" #include "tree.h" @@ -94,7 +91,7 @@ struct checkout_opts { int new_branch_log; enum branch_track track; struct diff_options diff_options; - char *conflict_style; + int conflict_style; int branch_exists; const char *prefix; @@ -103,6 +100,8 @@ struct checkout_opts { struct tree *source_tree; }; +#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } + struct branch_info { char *name; /* The short name used */ char *path; /* The full name of a real branch */ @@ -254,7 +253,8 @@ 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) + int *nr_checkouts, struct mem_pool *ce_mem_pool, + int conflict_style) { struct cache_entry *ce = the_index.cache[pos]; const char *path = ce->name; @@ -265,7 +265,7 @@ static int checkout_merged(int pos, const struct checkout *state, mmbuffer_t result_buf; struct object_id threeway[3]; unsigned mode = 0; - struct ll_merge_options ll_opts; + struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; int renormalize = 0; memset(threeway, 0, sizeof(threeway)); @@ -287,9 +287,9 @@ static int checkout_merged(int pos, const struct checkout *state, read_mmblob(&ours, &threeway[1]); read_mmblob(&theirs, &threeway[2]); - memset(&ll_opts, 0, sizeof(ll_opts)); git_config_get_bool("merge.renormalize", &renormalize); ll_opts.renormalize = renormalize; + ll_opts.conflict_style = conflict_style; merge_status = ll_merge(&result_buf, path, &ancestor, "base", &ours, "ours", &theirs, "theirs", state->istate, &ll_opts); @@ -420,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts, else if (opts->merge) errs |= checkout_merged(pos, &state, &nr_unmerged, - &ce_mem_pool); + &ce_mem_pool, + opts->conflict_style); pos = skip_same_name(ce, pos) - 1; } } @@ -523,6 +524,15 @@ static int checkout_paths(const struct checkout_opts *opts, "--merge", "--conflict", "--staged"); } + /* + * recreating unmerged index entries and writing out data from + * unmerged index entries would make no sense when checking out + * of a tree-ish. + */ + if ((opts->merge || opts->writeout_stage) && opts->source_tree) + die(_("'%s', '%s', or '%s' cannot be used when checking out of a tree"), + "--merge", "--ours", "--theirs"); + if (opts->patch_mode) { enum add_p_mode patch_mode; const char *rev = new_branch_info->name; @@ -560,6 +570,8 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->source_tree) read_tree_some(opts->source_tree, &opts->pathspec); + if (opts->merge) + unmerge_index(&the_index, &opts->pathspec, CE_MATCHED); ps_matched = xcalloc(opts->pathspec.nr, 1); @@ -583,10 +595,6 @@ static int checkout_paths(const struct checkout_opts *opts, } free(ps_matched); - /* "checkout -m path" to recreate conflicted state */ - if (opts->merge) - unmerge_marked_index(&the_index); - /* Any unmerged paths? */ for (pos = 0; pos < the_index.cache_nr; pos++) { const struct cache_entry *ce = the_index.cache[pos]; @@ -700,8 +708,9 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o, init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(), NULL); - parse_tree(tree); - init_tree_desc(&tree_desc, tree->buffer, tree->size); + if (parse_tree(tree) < 0) + return 128; + init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { case -2: *writeout_error = 1; @@ -779,9 +788,15 @@ static int merge_working_tree(const struct checkout_opts *opts, if (new_branch_info->commit) BUG("'switch --orphan' should never accept a commit as starting point"); new_tree = parse_tree_indirect(the_hash_algo->empty_tree); - } else + if (!new_tree) + BUG("unable to read empty tree"); + } else { new_tree = repo_get_commit_tree(the_repository, new_branch_info->commit); + if (!new_tree) + return error(_("unable to read tree (%s)"), + oid_to_hex(&new_branch_info->commit->object.oid)); + } if (opts->discard_changes) { ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info); if (ret) @@ -815,10 +830,13 @@ static int merge_working_tree(const struct checkout_opts *opts, die(_("unable to parse commit %s"), oid_to_hex(old_commit_oid)); - init_tree_desc(&trees[0], tree->buffer, tree->size); - parse_tree(new_tree); + init_tree_desc(&trees[0], &tree->object.oid, + tree->buffer, tree->size); + if (parse_tree(new_tree) < 0) + exit(128); tree = new_tree; - init_tree_desc(&trees[1], tree->buffer, tree->size); + init_tree_desc(&trees[1], &tree->object.oid, + tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); clear_unpack_trees_porcelain(&topts); @@ -883,6 +901,7 @@ static int merge_working_tree(const struct checkout_opts *opts, } o.branch1 = new_branch_info->name; o.branch2 = "local"; + o.conflict_style = opts->conflict_style; ret = merge_trees(&o, new_tree, work, @@ -1195,6 +1214,8 @@ static int git_checkout_config(const char *var, const char *value, struct checkout_opts *opts = cb; if (!strcmp(var, "diff.ignoresubmodules")) { + if (!value) + return config_error_nonbool(var); handle_ignore_submodules_arg(&opts->diff_options, value); return 0; } @@ -1218,7 +1239,9 @@ static void setup_new_branch_info_and_source_tree( struct tree **source_tree = &opts->source_tree; struct object_id branch_rev; - new_branch_info->name = xstrdup(arg); + /* treat '@' as a shortcut for 'HEAD' */ + new_branch_info->name = !strcmp(arg, "@") ? xstrdup("HEAD") : + xstrdup(arg); setup_branch_path(new_branch_info); if (!check_refname_format(new_branch_info->path, 0) && @@ -1232,10 +1255,15 @@ static void setup_new_branch_info_and_source_tree( if (!new_branch_info->commit) { /* not a commit */ *source_tree = parse_tree_indirect(rev); + if (!*source_tree) + die(_("unable to read tree (%s)"), oid_to_hex(rev)); } else { parse_commit_or_die(new_branch_info->commit); *source_tree = repo_get_commit_tree(the_repository, new_branch_info->commit); + if (!*source_tree) + die(_("unable to read tree (%s)"), + oid_to_hex(&new_branch_info->commit->object.oid)); } } @@ -1509,6 +1537,26 @@ static void die_if_some_operation_in_progress(void) wt_status_state_free_buffers(&state); } +/* + * die if attempting to checkout an existing branch that is in use + * in another worktree, unless ignore-other-wortrees option is given. + * The check is bypassed when the branch is already the current one, + * as it will not make things any worse. + */ +static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, + const char *full_ref) +{ + int flags; + char *head_ref; + + if (opts->ignore_other_worktrees) + return; + head_ref = resolve_refdup("HEAD", 0, NULL, &flags); + if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref))) + die_if_checked_out(full_ref, 1); + free(head_ref); +} + static int checkout_branch(struct checkout_opts *opts, struct branch_info *new_branch_info) { @@ -1569,14 +1617,15 @@ static int checkout_branch(struct checkout_opts *opts, if (!opts->can_switch_when_in_progress) die_if_some_operation_in_progress(); - if (new_branch_info->path && !opts->force_detach && !opts->new_branch && - !opts->ignore_other_worktrees) { - int flag; - char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag); - if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path))) - die_if_checked_out(new_branch_info->path, 1); - free(head_ref); + /* "git checkout <branch>" */ + if (new_branch_info->path && !opts->force_detach && !opts->new_branch) + die_if_switching_to_a_branch_in_use(opts, new_branch_info->path); + + /* "git checkout -B <branch>" */ + if (opts->new_branch_force) { + char *full_ref = xstrfmt("refs/heads/%s", opts->new_branch); + die_if_switching_to_a_branch_in_use(opts, full_ref); + free(full_ref); } if (!new_branch_info->commit && opts->new_branch) { @@ -1590,6 +1639,24 @@ static int checkout_branch(struct checkout_opts *opts, return switch_branches(opts, new_branch_info); } +static int parse_opt_conflict(const struct option *o, const char *arg, int unset) +{ + struct checkout_opts *opts = o->value; + + if (unset) { + opts->conflict_style = -1; + return 0; + } + opts->conflict_style = parse_conflict_style_name(arg); + if (opts->conflict_style < 0) + return error(_("unknown conflict style '%s'"), arg); + /* --conflict overrides a previous --no-merge */ + if (!opts->merge) + opts->merge = -1; + + return 0; +} + static struct option *add_common_options(struct checkout_opts *opts, struct option *prevopts) { @@ -1600,8 +1667,9 @@ static struct option *add_common_options(struct checkout_opts *opts, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")), OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")), - OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"), - N_("conflict style (merge, diff3, or zdiff3)")), + OPT_CALLBACK(0, "conflict", opts, N_("style"), + N_("conflict style (merge, diff3, or zdiff3)"), + parse_opt_conflict), OPT_END() }; struct option *newopts = parse_options_concat(prevopts, options); @@ -1620,7 +1688,7 @@ static struct option *add_common_switch_branch_options( parse_opt_tracking_mode), OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"), PARSE_OPT_NOCOMPLETE), - OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")), + OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unborn branch")), OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore, N_("update ignored files (default)"), PARSE_OPT_NOCOMPLETE), @@ -1660,10 +1728,11 @@ static char cb_option = 'b'; static int checkout_main(int argc, const char **argv, const char *prefix, struct checkout_opts *opts, struct option *options, - const char * const usagestr[], - struct branch_info *new_branch_info) + const char * const usagestr[]) { int parseopt_flags = 0; + struct branch_info new_branch_info = { 0 }; + int ret; opts->overwrite_ignore = 1; opts->prefix = prefix; @@ -1692,15 +1761,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix, opts->show_progress = isatty(2); } - if (opts->conflict_style) { - struct key_value_info kvi = KVI_INIT; - struct config_context ctx = { - .kvi = &kvi, - }; - opts->merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", opts->conflict_style, - &ctx, NULL); - } + /* --conflicts implies --merge */ + if (opts->merge == -1) + opts->merge = opts->conflict_style >= 0; + if (opts->force) { opts->discard_changes = 1; opts->ignore_unmerged_opt = "--force"; @@ -1779,7 +1843,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, opts->track == BRANCH_TRACK_UNSPECIFIED && !opts->new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, - new_branch_info, opts, &rev); + &new_branch_info, opts, &rev); argv += n; argc -= n; } else if (!opts->accept_ref && opts->from_treeish) { @@ -1788,7 +1852,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev)) die(_("could not resolve %s"), opts->from_treeish); - setup_new_branch_info_and_source_tree(new_branch_info, + setup_new_branch_info_and_source_tree(&new_branch_info, opts, &rev, opts->from_treeish); @@ -1808,7 +1872,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, * Try to give more helpful suggestion. * new_branch && argc > 1 will be caught later. */ - if (opts->new_branch && argc == 1 && !new_branch_info->commit) + if (opts->new_branch && argc == 1 && !new_branch_info.commit) die(_("'%s' is not a commit and a branch '%s' cannot be created from it"), argv[0], opts->new_branch); @@ -1858,14 +1922,21 @@ static int checkout_main(int argc, const char **argv, const char *prefix, } if (opts->patch_mode || opts->pathspec.nr) - return checkout_paths(opts, new_branch_info); + ret = checkout_paths(opts, &new_branch_info); else - return checkout_branch(opts, new_branch_info); + ret = checkout_branch(opts, &new_branch_info); + + branch_info_release(&new_branch_info); + clear_pathspec(&opts->pathspec); + free(opts->pathspec_from_file); + free(options); + + return ret; } int cmd_checkout(int argc, const char **argv, const char *prefix) { - struct checkout_opts opts; + struct checkout_opts opts = CHECKOUT_OPTS_INIT; struct option *options; struct option checkout_options[] = { OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), @@ -1878,10 +1949,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")), OPT_END() }; - int ret; - struct branch_info new_branch_info = { 0 }; - memset(&opts, 0, sizeof(opts)); opts.dwim_new_local_branch = 1; opts.switch_branch_doing_nothing_is_ok = 1; opts.only_merge_on_switching_branches = 0; @@ -1909,18 +1977,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) options = add_common_switch_branch_options(&opts, options); options = add_checkout_path_options(&opts, options); - ret = checkout_main(argc, argv, prefix, &opts, - 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; + return checkout_main(argc, argv, prefix, &opts, options, + checkout_usage); } int cmd_switch(int argc, const char **argv, const char *prefix) { - struct checkout_opts opts; + struct checkout_opts opts = CHECKOUT_OPTS_INIT; struct option *options = NULL; struct option switch_options[] = { OPT_STRING('c', "create", &opts.new_branch, N_("branch"), @@ -1933,10 +1996,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix) N_("throw away local modifications")), OPT_END() }; - int ret; - struct branch_info new_branch_info = { 0 }; - memset(&opts, 0, sizeof(opts)); opts.dwim_new_local_branch = 1; opts.accept_ref = 1; opts.accept_pathspec = 0; @@ -1953,16 +2013,13 @@ int cmd_switch(int argc, const char **argv, const char *prefix) cb_option = 'c'; - ret = checkout_main(argc, argv, prefix, &opts, - options, switch_branch_usage, &new_branch_info); - branch_info_release(&new_branch_info); - FREE_AND_NULL(options); - return ret; + return checkout_main(argc, argv, prefix, &opts, options, + switch_branch_usage); } int cmd_restore(int argc, const char **argv, const char *prefix) { - struct checkout_opts opts; + struct checkout_opts opts = CHECKOUT_OPTS_INIT; struct option *options; struct option restore_options[] = { OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>", @@ -1976,10 +2033,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")), OPT_END() }; - int ret; - struct branch_info new_branch_info = { 0 }; - memset(&opts, 0, sizeof(opts)); opts.accept_ref = 0; opts.accept_pathspec = 1; opts.empty_pathspec_ok = 0; @@ -1992,9 +2046,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix) options = add_common_options(&opts, options); options = add_checkout_path_options(&opts, options); - ret = checkout_main(argc, argv, prefix, &opts, - options, restore_usage, &new_branch_info); - branch_info_release(&new_branch_info); - FREE_AND_NULL(options); - return ret; + return checkout_main(argc, argv, prefix, &opts, options, + restore_usage); } diff --git a/builtin/clean.c b/builtin/clean.c index 49c224e626..29efe84153 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -25,7 +25,7 @@ #include "help.h" #include "prompt.h" -static int force = -1; /* unset */ +static int require_force = -1; /* unset */ static int interactive; static struct string_list del_list = STRING_LIST_INIT_DUP; static unsigned int colopts; @@ -128,7 +128,7 @@ static int git_clean_config(const char *var, const char *value, } if (!strcmp(var, "clean.requireforce")) { - force = !git_config_bool(var, value); + require_force = git_config_bool(var, value); return 0; } @@ -920,7 +920,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) { int i, res; int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0; - int ignored_only = 0, config_set = 0, errors = 0, gone = 1; + int ignored_only = 0, force = 0, errors = 0, gone = 1; int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf abs_path = STRBUF_INIT; struct dir_struct dir = DIR_INIT; @@ -946,22 +946,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix) }; git_config(git_clean_config, NULL); - if (force < 0) - force = 0; - else - config_set = 1; argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, 0); - if (!interactive && !dry_run && !force) { - if (config_set) - die(_("clean.requireForce set to true and neither -i, -n, nor -f given; " - "refusing to clean")); - else - die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;" - " refusing to clean")); - } + if (require_force != 0 && !force && !interactive && !dry_run) + die(_("clean.requireForce is true and -f not given: refusing to clean")); if (force > 1) rm_flags = 0; @@ -971,7 +961,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (ignored && ignored_only) - die(_("-x and -X cannot be used together")); + die(_("options '%s' and '%s' cannot be used together"), "-x", "-X"); if (!ignored) setup_standard_excludes(&dir); if (ignored_only) diff --git a/builtin/clone.c b/builtin/clone.c index c6357af949..74ec14542e 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -19,7 +19,6 @@ #include "hex.h" #include "lockfile.h" #include "parse-options.h" -#include "fetch-pack.h" #include "refs.h" #include "refspec.h" #include "object-file.h" @@ -72,6 +71,7 @@ static char *remote_name = NULL; static char *option_branch = NULL; static struct string_list option_not = STRING_LIST_INIT_NODUP; static const char *real_git_dir; +static const char *ref_format; static char *option_upload_pack = "git-upload-pack"; static int option_verbosity; static int option_progress = -1; @@ -116,7 +116,7 @@ static struct option builtin_clone_options[] = { OPT_HIDDEN_BOOL(0, "naked", &option_bare, N_("create a bare repository")), OPT_BOOL(0, "mirror", &option_mirror, - N_("create a mirror repository (implies bare)")), + N_("create a mirror repository (implies --bare)")), OPT_BOOL('l', "local", &option_local, N_("to clone from a local repository")), OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks, @@ -157,6 +157,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will be shallow")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), N_("separate git dir from working tree")), + OPT_STRING(0, "ref-format", &ref_format, N_("format"), + N_("specify the reference format to use")), OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), N_("set config inside the new repository")), OPT_STRING_LIST(0, "server-option", &server_options, @@ -736,8 +738,9 @@ static int checkout(int submodule_progress, int filter_submodules) tree = parse_tree_indirect(&oid); if (!tree) die(_("unable to parse commit %s"), oid_to_hex(&oid)); - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); + if (parse_tree(tree) < 0) + exit(128); + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) die(_("unable to checkout working tree")); @@ -791,6 +794,8 @@ static int git_clone_config(const char *k, const char *v, const struct config_context *ctx, void *cb) { if (!strcmp(k, "clone.defaultremotename")) { + if (!v) + return config_error_nonbool(k); free(remote_name); remote_name = xstrdup(v); } @@ -922,6 +927,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct ref *mapped_refs = NULL; const struct ref *ref; struct strbuf key = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; @@ -930,6 +936,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int submodule_progress; int filter_submodules = 0; int hash_algo; + unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; const int do_not_override_repo_unix_permissions = -1; struct transport_ls_refs_options transport_ls_refs_options = @@ -955,6 +962,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_single_branch == -1) option_single_branch = deepen ? 1 : 0; + if (ref_format) { + ref_storage_format = ref_storage_format_by_name(ref_format); + if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format '%s'"), ref_format); + } + if (option_mirror) option_bare = 1; @@ -965,7 +978,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } if (bundle_uri && deepen) - die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude")); + die(_("options '%s' and '%s' cannot be used together"), + "--bundle-uri", + "--depth/--shallow-since/--shallow-exclude"); repo_name = argv[0]; @@ -1097,8 +1112,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } } - init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL, - do_not_override_repo_unix_permissions, INIT_DB_QUIET); + /* + * Initialize the repository, but skip initializing the reference + * database. We do not yet know about the object format of the + * repository, and reference backends may persist that information into + * their on-disk data structures. + */ + init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, + ref_storage_format, NULL, + do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB); if (real_git_dir) { free((char *)git_dir); @@ -1106,6 +1128,50 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } /* + * We have a chicken-and-egg situation between initializing the refdb + * and spawning transport helpers: + * + * - Initializing the refdb requires us to know about the object + * format. We thus have to spawn the transport helper to learn + * about it. + * + * - The transport helper may want to access the Git repository. But + * because the refdb has not been initialized, we don't have "HEAD" + * or "refs/". Thus, the helper cannot find the Git repository. + * + * Ideally, we would have structured the helper protocol such that it's + * mandatory for the helper to first announce its capabilities without + * yet assuming a fully initialized repository. Like that, we could + * have added a "lazy-refdb-init" capability that announces whether the + * helper is ready to handle not-yet-initialized refdbs. If any helper + * didn't support them, we would have fully initialized the refdb with + * the SHA1 object format, but later on bailed out if we found out that + * the remote repository used a different object format. + * + * But we didn't, and thus we use the following workaround to partially + * initialize the repository's refdb such that it can be discovered by + * Git commands. To do so, we: + * + * - Create an invalid HEAD ref pointing at "refs/heads/.invalid". + * + * - Create the "refs/" directory. + * + * - Set up the ref storage format and repository version as + * required. + * + * This is sufficient for Git commands to discover the Git directory. + */ + initialize_repository_version(GIT_HASH_UNKNOWN, + the_repository->ref_storage_format, 1); + + strbuf_addf(&buf, "%s/HEAD", git_dir); + write_file(buf.buf, "ref: refs/heads/.invalid"); + + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/refs", git_dir); + safe_create_dir(buf.buf, 1); + + /* * additional config can be injected with -c, make sure it's included * after init_db, which clears the entire config environment. */ @@ -1185,10 +1251,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); - if (option_sparse_checkout && git_sparse_checkout_init(dir)) - return 1; - - remote = remote_get(remote_name); + remote = remote_get_early(remote_name); refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, branch_top.buf); @@ -1266,6 +1329,27 @@ 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; + strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); + refspec_ref_prefixes(&remote->fetch, + &transport_ls_refs_options.ref_prefixes); + if (option_branch) + expand_ref_prefix(&transport_ls_refs_options.ref_prefixes, + option_branch); + if (!option_no_tags) + strvec_push(&transport_ls_refs_options.ref_prefixes, + "refs/tags/"); + + refs = transport_get_remote_refs(transport, &transport_ls_refs_options); + + /* + * Now that we know what algorithm the remote side is using, let's set + * ours to the same thing. + */ + hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); + initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1); + repo_set_hash_algo(the_repository, hash_algo); + create_reference_database(the_repository->ref_storage_format, NULL, 1); + /* * Before fetching from the remote, download and install bundle * data from the --bundle-uri option. @@ -1281,24 +1365,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) 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, - &transport_ls_refs_options.ref_prefixes); - if (option_branch) - expand_ref_prefix(&transport_ls_refs_options.ref_prefixes, - option_branch); - if (!option_no_tags) - strvec_push(&transport_ls_refs_options.ref_prefixes, - "refs/tags/"); - - refs = transport_get_remote_refs(transport, &transport_ls_refs_options); - - if (refs) - mapped_refs = wanted_peer_refs(refs, &remote->fetch); - - if (!bundle_uri) { + } else { /* * Populate transport->got_remote_bundle_uri and * transport->bundle_uri. We might get nothing. @@ -1319,13 +1386,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } } - /* - * Now that we know what algorithm the remote side is using, - * let's set ours to the same thing. - */ - hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); - initialize_repository_version(hash_algo, 1); - repo_set_hash_algo(the_repository, hash_algo); + if (refs) + mapped_refs = wanted_peer_refs(refs, &remote->fetch); if (mapped_refs) { /* @@ -1428,12 +1490,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix) dissociate_from_references(); } + if (option_sparse_checkout && git_sparse_checkout_init(dir)) + return 1; + junk_mode = JUNK_LEAVE_REPO; err = checkout(submodule_progress, filter_submodules); free(remote_name); strbuf_release(&reflog_msg); strbuf_release(&branch_top); + strbuf_release(&buf); strbuf_release(&key); free_refs(mapped_refs); free_refs(remote_head_points_at); diff --git a/builtin/column.c b/builtin/column.c index a83be8bc99..10ff7e0166 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -45,6 +45,8 @@ int cmd_column(int argc, const char **argv, const char *prefix) memset(&copts, 0, sizeof(copts)); copts.padding = 1; argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0); + if (copts.padding < 0) + die(_("%s must be non-negative"), "--padding"); if (argc) usage_with_options(builtin_column_usage, options); if (real_command || command) { @@ -56,5 +58,7 @@ int cmd_column(int argc, const char **argv, const char *prefix) string_list_append(&list, sb.buf); print_columns(&list, colopts, &copts); + strbuf_release(&sb); + string_list_clear(&list, 0); return 0; } diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index c88389df24..7102ee90a0 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -1,17 +1,16 @@ #include "builtin.h" #include "commit.h" #include "config.h" -#include "dir.h" #include "environment.h" #include "gettext.h" #include "hex.h" -#include "lockfile.h" #include "parse-options.h" #include "repository.h" #include "commit-graph.h" #include "object-store-ll.h" #include "progress.h" #include "replace-object.h" +#include "strbuf.h" #include "tag.h" #include "trace2.h" @@ -22,7 +21,7 @@ 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>") + " <split-options>") static const char * builtin_commit_graph_verify_usage[] = { BUILTIN_COMMIT_GRAPH_VERIFY_USAGE, @@ -69,10 +68,12 @@ static int graph_verify(int argc, const char **argv, const char *prefix) struct commit_graph *graph = NULL; struct object_directory *odb = NULL; char *graph_name; - int open_ok; + char *chain_name; + enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE; int fd; struct stat st; int flags = 0; + int incomplete_chain = 0; int ret; static struct option builtin_commit_graph_verify_options[] = { @@ -102,24 +103,39 @@ static int graph_verify(int argc, const char **argv, const char *prefix) odb = find_odb(the_repository, opts.obj_dir); graph_name = get_commit_graph_filename(odb); - open_ok = open_commit_graph(graph_name, &fd, &st); - if (!open_ok && errno != ENOENT) + chain_name = get_commit_graph_chain_filename(odb); + if (open_commit_graph(graph_name, &fd, &st)) + opened = OPENED_GRAPH; + else if (errno != ENOENT) die_errno(_("Could not open commit-graph '%s'"), graph_name); + else if (open_commit_graph_chain(chain_name, &fd, &st)) + opened = OPENED_CHAIN; + else if (errno != ENOENT) + die_errno(_("could not open commit-graph chain '%s'"), chain_name); FREE_AND_NULL(graph_name); + FREE_AND_NULL(chain_name); FREE_AND_NULL(options); - if (open_ok) + if (opened == OPENED_NONE) + return 0; + else if (opened == OPENED_GRAPH) graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); else - graph = read_commit_graph_one(the_repository, odb); + graph = load_commit_graph_chain_fd_st(the_repository, fd, &st, + &incomplete_chain); - /* Return failure if open_ok predicted success */ if (!graph) - return !!open_ok; + return 1; ret = verify_commit_graph(the_repository, graph, flags); free_commit_graph(graph); + + if (incomplete_chain) { + error("one or more commit-graph chain files could not be loaded"); + ret |= 1; + } + return ret; } @@ -311,6 +327,7 @@ cleanup: FREE_AND_NULL(options); string_list_clear(&pack_indexes, 0); strbuf_release(&buf); + oidset_clear(&commits); return result; } diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 02625e7176..1bb7819839 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -11,9 +11,6 @@ #include "object-store-ll.h" #include "repository.h" #include "commit.h" -#include "tree.h" -#include "utf8.h" -#include "gpg-interface.h" #include "parse-options.h" static const char * const commit_tree_usage[] = { diff --git a/builtin/commit.c b/builtin/commit.c index 781af2e206..b27b56c8be 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -16,17 +16,12 @@ #include "editor.h" #include "environment.h" #include "diff.h" -#include "diffcore.h" #include "commit.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" #include "run-command.h" -#include "hook.h" -#include "refs.h" -#include "log-tree.h" #include "strbuf.h" -#include "utf8.h" #include "object-name.h" #include "parse-options.h" #include "path.h" @@ -35,9 +30,6 @@ #include "string-list.h" #include "rerere.h" #include "unpack-trees.h" -#include "quote.h" -#include "submodule.h" -#include "gpg-interface.h" #include "column.h" #include "sequencer.h" #include "sparse-index.h" @@ -339,8 +331,9 @@ static void create_base_index(const struct commit *current_head) tree = parse_tree_indirect(¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); + if (parse_tree(tree) < 0) + exit(128); + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) exit(128); /* We've already reported the error, finish dying */ } @@ -745,7 +738,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix, const char *hook_arg2 = NULL; int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE); int old_display_comment_prefix; - int merge_contains_scissors = 0; int invoked_hook; /* This checks and barfs if author is badly specified */ @@ -849,7 +841,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start) - merge_contains_scissors = 1; + s->added_cut_line = 1; } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) { if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0) die_errno(_("could not read SQUASH_MSG")); @@ -900,7 +892,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, strbuf_stripspace(&sb, '\0'); if (signoff) - append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0); + append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0); if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); @@ -932,9 +924,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, " yourself if you want to.\n" "An empty message aborts the commit.\n"); if (whence != FROM_COMMIT) { - if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && - !merge_contains_scissors) - wt_status_add_cut_line(s->fp); + if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) + wt_status_add_cut_line(s); status_printf_ln( s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? @@ -954,8 +945,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char); else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { - if (whence == FROM_COMMIT && !merge_contains_scissors) - wt_status_add_cut_line(s->fp); + if (whence == FROM_COMMIT) + wt_status_add_cut_line(s); } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char); @@ -1166,22 +1157,45 @@ static void handle_ignored_arg(struct wt_status *s) die(_("Invalid ignored mode '%s'"), ignored_arg); } -static void handle_untracked_files_arg(struct wt_status *s) +static enum untracked_status_type parse_untracked_setting_name(const char *u) { - if (!untracked_files_arg) - ; /* default already initialized */ - else if (!strcmp(untracked_files_arg, "no")) - s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "normal")) - s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "all")) - s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; /* * Please update $__git_untracked_file_modes in * git-completion.bash when you add new options */ + switch (git_parse_maybe_bool(u)) { + case 0: + u = "no"; + break; + case 1: + u = "normal"; + break; + default: + break; + } + + if (!strcmp(u, "no")) + return SHOW_NO_UNTRACKED_FILES; + else if (!strcmp(u, "normal")) + return SHOW_NORMAL_UNTRACKED_FILES; + else if (!strcmp(u, "all")) + return SHOW_ALL_UNTRACKED_FILES; else - die(_("Invalid untracked files mode '%s'"), untracked_files_arg); + return SHOW_UNTRACKED_FILES_ERROR; +} + +static void handle_untracked_files_arg(struct wt_status *s) +{ + enum untracked_status_type u; + + if (!untracked_files_arg) + return; /* default already initialized */ + + u = parse_untracked_setting_name(untracked_files_arg); + if (u == SHOW_UNTRACKED_FILES_ERROR) + die(_("Invalid untracked files mode '%s'"), + untracked_files_arg); + s->show_untracked_files = u; } static const char *read_commit_message(const char *name) @@ -1464,16 +1478,12 @@ static int git_status_config(const char *k, const char *v, return 0; } if (!strcmp(k, "status.showuntrackedfiles")) { - if (!v) - return config_error_nonbool(k); - else if (!strcmp(v, "no")) - s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(v, "normal")) - s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(v, "all")) - s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; - else + enum untracked_status_type u; + + u = parse_untracked_setting_name(v); + if (u == SHOW_UNTRACKED_FILES_ERROR) return error(_("Invalid untracked files mode '%s'"), v); + s->show_untracked_files = u; return 0; } if (!strcmp(k, "diff.renamelimit")) { @@ -1885,7 +1895,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) &oid, flags); } - apply_autostash(git_path_merge_autostash(the_repository)); + apply_autostash_ref(the_repository, "MERGE_AUTOSTASH"); cleanup: strbuf_release(&author_ident); diff --git a/builtin/config.c b/builtin/config.c index 11a4d4ef14..b55bfae7d6 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -708,10 +708,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) } if (use_global_config) { - char *user_config, *xdg_config; - - git_global_config(&user_config, &xdg_config); - if (!user_config) + given_config_source.file = git_global_config(); + if (!given_config_source.file) /* * It is unknown if HOME/.gitconfig exists, so * we do not know if we should write to XDG @@ -719,19 +717,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) * is set and points at a sane location. */ die(_("$HOME not set")); - given_config_source.scope = CONFIG_SCOPE_GLOBAL; - - if (access_or_warn(user_config, R_OK, 0) && - xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { - given_config_source.file = xdg_config; - free(user_config); - } else { - given_config_source.file = user_config; - free(xdg_config); - } - } - else if (use_system_config) { + } else if (use_system_config) { given_config_source.file = git_system_config(); given_config_source.scope = CONFIG_SCOPE_SYSTEM; } else if (use_local_config) { @@ -760,7 +747,6 @@ int cmd_config(int argc, const char **argv, const char *prefix) given_config_source.scope = CONFIG_SCOPE_COMMAND; } - if (respect_includes_opt == -1) config_options.respect_includes = !given_config_source.file; else diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 43b9d0e5b1..bba96d4ffd 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -7,8 +7,6 @@ #ifndef NO_UNIX_SOCKETS -#include "credential.h" -#include "string-list.h" #include "unix-socket.h" #include "run-command.h" diff --git a/builtin/describe.c b/builtin/describe.c index a9e375882b..d6c77a714f 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -7,9 +7,7 @@ #include "lockfile.h" #include "commit.h" #include "tag.h" -#include "blob.h" #include "refs.h" -#include "exec-cmd.h" #include "object-name.h" #include "parse-options.h" #include "read-cache-ll.h" @@ -561,9 +559,11 @@ static void describe(const char *arg, int last_one) static int option_parse_exact_match(const struct option *opt, const char *arg, int unset) { + int *val = opt->value; + BUG_ON_OPT_ARG(arg); - max_candidates = unset ? DEFAULT_CANDIDATES : 0; + *val = unset ? DEFAULT_CANDIDATES : 0; return 0; } @@ -578,7 +578,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "long", &longformat, N_("always use long format")), OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")), OPT__ABBREV(&abbrev), - OPT_CALLBACK_F(0, "exact-match", NULL, NULL, + OPT_CALLBACK_F(0, "exact-match", &max_candidates, NULL, N_("only output exact matches"), PARSE_OPT_NOARG, option_parse_exact_match), OPT_INTEGER(0, "candidates", &max_candidates, diff --git a/builtin/diff-files.c b/builtin/diff-files.c index f38912cd40..018011f29e 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -11,7 +11,6 @@ #include "preload-index.h" #include "repository.h" #include "revision.h" -#include "submodule.h" static const char diff_files_usage[] = "git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]" diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 220f341ffa..3e05260ac0 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -7,8 +7,6 @@ #include "repository.h" #include "revision.h" #include "setup.h" -#include "sparse-index.h" -#include "submodule.h" static const char diff_cache_usage[] = "git diff-index [-m] [--cached] [--merge-base] " diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 86be634286..a8e68ce8ef 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -6,7 +6,6 @@ #include "gettext.h" #include "hex.h" #include "log-tree.h" -#include "submodule.h" #include "read-cache-ll.h" #include "repository.h" #include "revision.h" diff --git a/builtin/diff.c b/builtin/diff.c index 0b313549c7..6e196e0c7d 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -10,7 +10,6 @@ #include "lockfile.h" #include "color.h" #include "commit.h" -#include "blob.h" #include "gettext.h" #include "tag.h" #include "diff.h" @@ -21,7 +20,6 @@ #include "revision.h" #include "log-tree.h" #include "setup.h" -#include "submodule.h" #include "oid-array.h" #include "tree.h" @@ -474,8 +472,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) repo_init_revisions(the_repository, &rev, prefix); /* Set up defaults that will apply to both no-index and regular diffs. */ - rev.diffopt.stat_width = -1; - rev.diffopt.stat_graph_width = -1; + init_diffstat_widths(&rev.diffopt); rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; diff --git a/builtin/difftool.c b/builtin/difftool.c index 0f5eae9cd4..a3c72b8258 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -18,7 +18,6 @@ #include "copy.h" #include "run-command.h" #include "environment.h" -#include "exec-cmd.h" #include "gettext.h" #include "hex.h" #include "parse-options.h" diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 56dc69fac1..4693d18cc9 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -25,7 +25,6 @@ #include "quote.h" #include "remote.h" #include "blob.h" -#include "commit-slab.h" static const char *fast_export_usage[] = { N_("git fast-export [<rev-list-opts>]"), @@ -33,9 +32,9 @@ static const char *fast_export_usage[] = { }; static int progress; -static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; -static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; -static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; +static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; +static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; +static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; static int fake_missing_tagger; static int use_done_feature; static int no_data; @@ -53,16 +52,18 @@ static struct revision_sources revision_sources; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) { + enum signed_tag_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - signed_tag_mode = SIGNED_TAG_ABORT; + *val = SIGNED_TAG_ABORT; else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - signed_tag_mode = VERBATIM; + *val = VERBATIM; else if (!strcmp(arg, "warn")) - signed_tag_mode = WARN; + *val = WARN; else if (!strcmp(arg, "warn-strip")) - signed_tag_mode = WARN_STRIP; + *val = WARN_STRIP; else if (!strcmp(arg, "strip")) - signed_tag_mode = STRIP; + *val = STRIP; else return error("Unknown signed-tags mode: %s", arg); return 0; @@ -71,12 +72,14 @@ static int parse_opt_signed_tag_mode(const struct option *opt, static int parse_opt_tag_of_filtered_mode(const struct option *opt, const char *arg, int unset) { + enum tag_of_filtered_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - tag_of_filtered_mode = TAG_FILTERING_ABORT; + *val = TAG_FILTERING_ABORT; else if (!strcmp(arg, "drop")) - tag_of_filtered_mode = DROP; + *val = DROP; else if (!strcmp(arg, "rewrite")) - tag_of_filtered_mode = REWRITE; + *val = REWRITE; else return error("Unknown tag-of-filtered mode: %s", arg); return 0; @@ -85,21 +88,23 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt, static int parse_opt_reencode_mode(const struct option *opt, const char *arg, int unset) { + enum reencode_mode *val = opt->value; + if (unset) { - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; return 0; } switch (git_parse_maybe_bool(arg)) { case 0: - reencode_mode = REENCODE_NO; + *val = REENCODE_NO; break; case 1: - reencode_mode = REENCODE_YES; + *val = REENCODE_YES; break; default: if (!strcasecmp(arg, "abort")) - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; else return error("Unknown reencoding mode: %s", arg); } @@ -131,8 +136,7 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED, a = container_of(eptr, const struct anonymized_entry, hash); if (keydata) { const struct anonymized_entry_key *key = keydata; - int equal = !strncmp(a->orig, key->orig, key->orig_len) && - !a->orig[key->orig_len]; + int equal = !xstrncmpz(a->orig, key->orig, key->orig_len); return !equal; } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 4dbb10aff3..782bda007c 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1102,6 +1102,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size) cycle_packfile(); + the_hash_algo->init_fn(&checkpoint.ctx); hashfile_checkpoint(pack_file, &checkpoint); offset = checkpoint.offset; @@ -1235,20 +1236,6 @@ static void *gfi_unpack_entry( return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep); } -static const char *get_mode(const char *str, uint16_t *modep) -{ - unsigned char c; - uint16_t mode = 0; - - while ((c = *str++) != ' ') { - if (c < '0' || c > '7') - return NULL; - mode = (mode << 3) + (c - '0'); - } - *modep = mode; - return str; -} - static void load_tree(struct tree_entry *root) { struct object_id *oid = &root->versions[1].oid; @@ -1286,7 +1273,7 @@ static void load_tree(struct tree_entry *root) t->entries[t->entry_count++] = e; e->tree = NULL; - c = get_mode(c, &e->versions[1].mode); + c = parse_mode(c, &e->versions[1].mode); if (!c) die("Corrupt mode in %s", oid_to_hex(oid)); e->versions[0].mode = e->versions[1].mode; @@ -1624,6 +1611,7 @@ static int update_branch(struct branch *b) oidclr(&old_oid); if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; + int ret; old_cmit = lookup_commit_reference_gently(the_repository, &old_oid, 0); @@ -1632,7 +1620,10 @@ static int update_branch(struct branch *b) if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); - if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) { + ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit); + if (ret < 0) + exit(128); + if (!ret) { warning("Not updating %s" " (new tip %s does not contain %s)", b->name, oid_to_hex(&b->oid), @@ -2275,7 +2266,7 @@ static void file_change_m(const char *p, struct branch *b) struct object_id oid; uint16_t mode, inline_data = 0; - p = get_mode(p, &mode); + p = parse_mode(p, &mode); if (!p) die("Corrupt mode: %s", command_buf.buf); switch (mode) { @@ -2808,8 +2799,7 @@ static void parse_new_tag(const char *arg) enum object_type type; const char *v; - t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag)); - memset(t, 0, sizeof(struct tag)); + t = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct tag)); t->name = mem_pool_strdup(&fi_mem_pool, arg); if (last_tag) last_tag->next_tag = t; diff --git a/builtin/fetch.c b/builtin/fetch.c index eed4a7cdb6..46a793411a 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -26,7 +26,6 @@ #include "connected.h" #include "strvec.h" #include "utf8.h" -#include "packfile.h" #include "pager.h" #include "path.h" #include "pkt-line.h" @@ -38,7 +37,6 @@ #include "shallow.h" #include "trace.h" #include "trace2.h" -#include "worktree.h" #include "bundle-uri.h" #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000) @@ -102,6 +100,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; struct fetch_config { enum display_format display_format; + int all; int prune; int prune_tags; int show_forced_updates; @@ -115,6 +114,11 @@ static int git_fetch_config(const char *k, const char *v, { struct fetch_config *fetch_config = cb; + if (!strcmp(k, "fetch.all")) { + fetch_config->all = git_config_bool(k, v); + return 0; + } + if (!strcmp(k, "fetch.prune")) { fetch_config->prune = git_config_bool(k, v); return 0; @@ -176,7 +180,7 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset * "git fetch --refmap='' origin foo" * can be used to tell the command not to store anywhere */ - refspec_append(&refmap, arg); + refspec_append(opt->value, arg); return 0; } @@ -308,7 +312,7 @@ static void clear_item(struct refname_hash_entry *item) static void add_already_queued_tags(const char *refname, - const struct object_id *old_oid, + const struct object_id *old_oid UNUSED, const struct object_id *new_oid, void *cb_data) { @@ -444,9 +448,8 @@ static void filter_prefetch_refspec(struct refspec *rs) continue; if (!rs->items[i].dst || (rs->items[i].src && - !strncmp(rs->items[i].src, - ref_namespace[NAMESPACE_TAGS].ref, - strlen(ref_namespace[NAMESPACE_TAGS].ref)))) { + starts_with(rs->items[i].src, + ref_namespace[NAMESPACE_TAGS].ref))) { int j; free(rs->items[i].src); @@ -978,6 +981,8 @@ static int update_local_ref(struct ref *ref, uint64_t t_before = getnanotime(); fast_forward = repo_in_merge_bases(the_repository, current, updated); + if (fast_forward < 0) + exit(128); forced_updates_ms += (getnanotime() - t_before) / 1000000; } else { fast_forward = 1; @@ -1651,7 +1656,7 @@ static int do_fetch(struct transport *transport, if (atomic_fetch) { transaction = ref_transaction_begin(&err); if (!transaction) { - retcode = error("%s", err.buf); + retcode = -1; goto cleanup; } } @@ -1711,7 +1716,6 @@ static int do_fetch(struct transport *transport, retcode = ref_transaction_commit(transaction, &err); if (retcode) { - error("%s", err.buf); ref_transaction_free(transaction); transaction = NULL; goto cleanup; @@ -1775,9 +1779,14 @@ static int do_fetch(struct transport *transport, } cleanup: - if (retcode && transaction) { - ref_transaction_abort(transaction, &err); - error("%s", err.buf); + if (retcode) { + if (err.len) { + error("%s", err.buf); + strbuf_reset(&err); + } + if (transaction && ref_transaction_abort(transaction, &err) && + err.len) + error("%s", err.buf); } display_state_release(&display_state); @@ -2128,7 +2137,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) const char *bundle_uri; struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote = NULL; - int all = 0, multiple = 0; + int all = -1, multiple = 0; int result = 0; int prune_tags_ok = 1; int enable_auto_gc = 1; @@ -2204,7 +2213,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), OPT_BOOL(0, "update-shallow", &update_shallow, N_("accept refs that update .git/shallow")), - OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), + OPT_CALLBACK_F(0, "refmap", &refmap, N_("refmap"), N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), OPT_IPVERSION(&family), @@ -2333,11 +2342,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) fetch_bundle_uri(the_repository, bundle_uri, NULL)) warning(_("failed to fetch bundles from '%s'"), bundle_uri); + if (all < 0) { + /* + * no --[no-]all given; + * only use config option if no remote was explicitly specified + */ + all = (!argc) ? config.all : 0; + } + if (all) { if (argc == 1) die(_("fetch --all does not take a repository argument")); else if (argc > 1) die(_("fetch --all does not make sense with refspecs")); + (void) for_each_remote(get_one_remote_for_fetch, &list); /* do not do fetch_multiple() of one */ diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 350bfa6e81..919282e12a 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -1,13 +1,12 @@ #include "builtin.h" +#include "commit.h" #include "config.h" #include "gettext.h" -#include "refs.h" #include "object.h" #include "parse-options.h" #include "ref-filter.h" #include "strbuf.h" #include "strvec.h" -#include "commit-reach.h" static char const * const for_each_ref_usage[] = { N_("git for-each-ref [<options>] [<pattern>]"), @@ -19,16 +18,12 @@ static char const * const for_each_ref_usage[] = { int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { - int i; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; - int maxcount = 0, icase = 0, omit_empty = 0; - struct ref_array array; + int icase = 0, include_root_refs = 0, from_stdin = 0; struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; - int from_stdin = 0; + unsigned int flags = FILTER_REFS_REGULAR; struct strvec vec = STRVEC_INIT; struct option opts[] = { @@ -40,11 +35,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) N_("quote placeholders suitably for python"), QUOTE_PYTHON), OPT_BIT(0 , "tcl", &format.quote_style, N_("quote placeholders suitably for Tcl"), QUOTE_TCL), - OPT_BOOL(0, "omit-empty", &omit_empty, + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, N_("do not output a newline after empty formatted refs")), OPT_GROUP(""), - OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), + OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), OPT__COLOR(&format.use_color, N_("respect format colors")), OPT_REF_FILTER_EXCLUDE(&filter), @@ -58,18 +53,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")), OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")), OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")), + OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")), OPT_END(), }; - memset(&array, 0, sizeof(array)); - format.format = "%(objectname) %(objecttype)\t%(refname)"; git_config(git_default_config, NULL); + /* Set default (refname) sorting */ + string_list_append(&sorting_options, "refname"); + parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); - if (maxcount < 0) { - error("invalid --count argument: `%d'", maxcount); + if (format.array_opts.max_count < 0) { + error("invalid --count argument: `%d'", format.array_opts.max_count); usage_with_options(for_each_ref_usage, opts); } if (HAS_MULTI_BITS(format.quote_style)) { @@ -100,27 +97,12 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) filter.name_patterns = argv; } + if (include_root_refs) + flags |= FILTER_REFS_ROOT_REFS; + filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL); - filter_ahead_behind(the_repository, &format, &array); - - ref_array_sort(sorting, &array); - - if (!maxcount || array.nr < maxcount) - maxcount = array.nr; - for (i = 0; i < maxcount; i++) { - strbuf_reset(&err); - strbuf_reset(&output); - if (format_ref_array_item(array.items[i], &format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - if (output.len || !omit_empty) - putchar('\n'); - } + filter_and_format_refs(&filter, flags, sorting, &format); - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); ref_filter_clear(&filter); ref_sorting_release(sorting); strvec_clear(&vec); diff --git a/builtin/fsck.c b/builtin/fsck.c index 611925905e..f892487c9b 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -10,13 +10,10 @@ #include "refs.h" #include "pack.h" #include "cache-tree.h" -#include "tree-walk.h" #include "fsck.h" #include "parse-options.h" -#include "dir.h" #include "progress.h" #include "streaming.h" -#include "decorate.h" #include "packfile.h" #include "object-file.h" #include "object-name.h" @@ -512,9 +509,7 @@ 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 UNUSED, - int flag UNUSED, void *cb_data) +static int fsck_handle_reflog(const char *logname, void *cb_data) { struct strbuf refname = STRBUF_INIT; diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 7e99c4d61b..1593713f4c 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -1,19 +1,20 @@ #include "builtin.h" #include "abspath.h" #include "config.h" +#include "dir.h" #include "environment.h" #include "gettext.h" #include "parse-options.h" #include "fsmonitor-ll.h" #include "fsmonitor-ipc.h" -#include "fsmonitor-path-utils.h" #include "fsmonitor-settings.h" #include "compat/fsmonitor/fsm-health.h" #include "compat/fsmonitor/fsm-listen.h" #include "fsmonitor--daemon.h" +#include "repository.h" #include "simple-ipc.h" #include "khash.h" -#include "pkt-line.h" +#include "run-command.h" #include "trace.h" #include "trace2.h" @@ -129,8 +130,9 @@ struct fsmonitor_cookie_item { enum fsmonitor_cookie_item_result result; }; -static int cookies_cmp(const void *data, const struct hashmap_entry *he1, - const struct hashmap_entry *he2, const void *keydata) +static int cookies_cmp(const void *data UNUSED, + const struct hashmap_entry *he1, + const struct hashmap_entry *he2, const void *keydata) { const struct fsmonitor_cookie_item *a = container_of(he1, const struct fsmonitor_cookie_item, entry); @@ -1412,7 +1414,7 @@ done: return err; } -static int try_to_run_foreground_daemon(int detach_console) +static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED) { /* * Technically, we don't need to probe for an existing daemon @@ -1442,7 +1444,8 @@ static int try_to_run_foreground_daemon(int detach_console) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { enum ipc_active_state s = fsmonitor_ipc__get_state(); diff --git a/builtin/gc.c b/builtin/gc.c index 719cae9a88..cb80ced6cb 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -52,6 +52,7 @@ static const char * const builtin_gc_usage[] = { static int pack_refs = 1; static int prune_reflogs = 1; static int cruft_packs = 1; +static unsigned long max_cruft_size; static int aggressive_depth = 50; static int aggressive_window = 250; static int gc_auto_threshold = 6700; @@ -61,6 +62,8 @@ static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; +static char *repack_filter; +static char *repack_filter_to; static unsigned long big_pack_threshold; static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; @@ -163,6 +166,7 @@ static void gc_config(void) git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); git_config_get_bool("gc.cruftpacks", &cruft_packs); + git_config_get_ulong("gc.maxcruftsize", &max_cruft_size); git_config_get_expiry("gc.pruneexpire", &prune_expire); git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); git_config_get_expiry("gc.logexpiry", &gc_log_expire); @@ -170,6 +174,9 @@ static void gc_config(void) git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold); git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size); + git_config_get_string("gc.repackfilter", &repack_filter); + git_config_get_string("gc.repackfilterto", &repack_filter_to); + git_config(git_default_config, NULL); } @@ -347,6 +354,9 @@ static void add_repack_all_option(struct string_list *keep_pack) strvec_push(&repack, "--cruft"); if (prune_expire) strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire); + if (max_cruft_size) + strvec_pushf(&repack, "--max-cruft-size=%lu", + max_cruft_size); } else { strvec_push(&repack, "-A"); if (prune_expire) @@ -355,6 +365,11 @@ static void add_repack_all_option(struct string_list *keep_pack) if (keep_pack) for_each_string_list(keep_pack, keep_one_pack, NULL); + + if (repack_filter && *repack_filter) + strvec_pushf(&repack, "--filter=%s", repack_filter); + if (repack_filter_to && *repack_filter_to) + strvec_pushf(&repack, "--filter-to=%s", repack_filter_to); } static void add_repack_incremental_option(void) @@ -575,6 +590,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")), + OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), @@ -1403,7 +1420,7 @@ static void initialize_task_config(int schedule) strbuf_release(&config_name); } -static int task_option_parse(const struct option *opt, +static int task_option_parse(const struct option *opt UNUSED, const char *arg, int unset) { int i, num_selected = 0; @@ -1526,19 +1543,18 @@ static int maintenance_register(int argc, const char **argv, const char *prefix) if (!found) { int rc; - char *user_config = NULL, *xdg_config = NULL; + char *global_config_file = NULL; if (!config_file) { - git_global_config(&user_config, &xdg_config); - config_file = user_config; - if (!user_config) - die(_("$HOME not set")); + global_config_file = git_global_config(); + config_file = global_config_file; } + if (!config_file) + 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); + free(global_config_file); if (rc) die(_("unable to add '%s' value of '%s'"), @@ -1595,18 +1611,18 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi if (found) { int rc; - char *user_config = NULL, *xdg_config = NULL; + char *global_config_file = NULL; + if (!config_file) { - git_global_config(&user_config, &xdg_config); - config_file = user_config; - if (!user_config) - die(_("$HOME not set")); + global_config_file = git_global_config(); + config_file = global_config_file; } + if (!config_file) + 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); + free(global_config_file); if (rc && (!force || rc == CONFIG_NOTHING_SET)) @@ -1708,6 +1724,15 @@ static int get_schedule_cmd(const char **cmd, int *is_available) return 1; } +static int get_random_minute(void) +{ + /* Use a static value when under tests. */ + if (getenv("GIT_TEST_MAINT_SCHEDULER")) + return 13; + + return git_rand() % 60; +} + static int is_launchctl_available(void) { const char *cmd = "launchctl"; @@ -1820,6 +1845,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT; struct stat st; const char *cmd = "launchctl"; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); preamble = "<?xml version=\"1.0\"?>\n" @@ -1845,29 +1871,30 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit case SCHEDULE_HOURLY: repeat = "<dict>\n" "<key>Hour</key><integer>%d</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 23; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_DAILY: repeat = "<dict>\n" "<key>Day</key><integer>%d</integer>\n" "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 6; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_WEEKLY: - strbuf_addstr(&plist, - "<dict>\n" - "<key>Day</key><integer>0</integer>\n" - "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" - "</dict>\n"); + strbuf_addf(&plist, + "<dict>\n" + "<key>Day</key><integer>0</integer>\n" + "<key>Hour</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" + "</dict>\n", + minute); break; default: @@ -1923,7 +1950,7 @@ static int launchctl_add_plists(void) launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY); } -static int launchctl_update_schedule(int run_maintenance, int fd) +static int launchctl_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return launchctl_add_plists(); @@ -1984,6 +2011,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); struct strbuf tfilename = STRBUF_INIT; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); @@ -2004,7 +2032,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority switch (schedule) { case SCHEDULE_HOURLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T01:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByDay>\n" "<DaysInterval>1</DaysInterval>\n" @@ -2013,12 +2041,13 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Interval>PT1H</Interval>\n" "<Duration>PT23H</Duration>\n" "<StopAtDurationEnd>false</StopAtDurationEnd>\n" - "</Repetition>\n"); + "</Repetition>\n", + minute); break; case SCHEDULE_DAILY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" @@ -2030,19 +2059,21 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Saturday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; case SCHEDULE_WEEKLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" "<Sunday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; default: @@ -2100,7 +2131,7 @@ static int schtasks_schedule_tasks(void) schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY); } -static int schtasks_update_schedule(int run_maintenance, int fd) +static int schtasks_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return schtasks_schedule_tasks(); @@ -2159,6 +2190,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) FILE *cron_list, *cron_in; struct strbuf line = STRBUF_INIT; struct tempfile *tmpedit = NULL; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); strvec_split(&crontab_list.args, cmd); @@ -2213,11 +2245,11 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", exec_path, exec_path); - fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly"); - fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily"); - fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly"); + fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); + fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); + fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly"); strbuf_release(&line_format); fprintf(cron_in, "\n%s\n", END_LINE); @@ -2276,77 +2308,54 @@ static char *xdg_config_home_systemd(const char *filename) return xdg_config_home_for("systemd/user", filename); } -static int systemd_timer_enable_unit(int enable, - enum schedule_priority schedule) -{ - const char *cmd = "systemctl"; - struct child_process child = CHILD_PROCESS_INIT; - const char *frequency = get_frequency(schedule); +#define SYSTEMD_UNIT_FORMAT "git-maintenance@%s.%s" - /* - * Disabling the systemd unit while it is already disabled makes - * systemctl print an error. - * Let's ignore it since it means we already are in the expected state: - * the unit is disabled. - * - * On the other hand, enabling a systemd unit which is already enabled - * produces no error. - */ - if (!enable) - child.no_stderr = 1; - - get_schedule_cmd(&cmd, NULL); - strvec_split(&child.args, cmd); - strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", - "--now", NULL); - strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency); - - if (start_command(&child)) - return error(_("failed to start systemctl")); - if (finish_command(&child)) - /* - * Disabling an already disabled systemd unit makes - * systemctl fail. - * Let's ignore this failure. - * - * Enabling an enabled systemd unit doesn't fail. - */ - if (enable) - return error(_("failed to run systemctl")); - return 0; -} - -static int systemd_timer_delete_unit_templates(void) +static int systemd_timer_delete_timer_file(enum schedule_priority priority) { int ret = 0; - char *filename = xdg_config_home_systemd("git-maintenance@.timer"); - if (unlink(filename) && !is_missing_file_error(errno)) - ret = error_errno(_("failed to delete '%s'"), filename); - FREE_AND_NULL(filename); + const char *frequency = get_frequency(priority); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + char *filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.service"); if (unlink(filename) && !is_missing_file_error(errno)) ret = error_errno(_("failed to delete '%s'"), filename); free(filename); + free(local_timer_name); return ret; } -static int systemd_timer_delete_units(void) +static int systemd_timer_delete_service_template(void) { - return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(0, SCHEDULE_DAILY) || - systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) || - systemd_timer_delete_unit_templates(); + int ret = 0; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); + char *filename = xdg_config_home_systemd(local_service_name); + if (unlink(filename) && !is_missing_file_error(errno)) + ret = error_errno(_("failed to delete '%s'"), filename); + + free(filename); + free(local_service_name); + return ret; } -static int systemd_timer_write_unit_templates(const char *exec_path) +/* + * Write the schedule information into a git-maintenance@<schedule>.timer + * file using a custom minute. This timer file cannot use the templating + * system, so we generate a specific file for each. + */ +static int systemd_timer_write_timer_file(enum schedule_priority schedule, + int minute) { + int res = -1; char *filename; FILE *file; const char *unit; + char *schedule_pattern = NULL; + const char *frequency = get_frequency(schedule); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.timer"); if (safe_create_leading_directories(filename)) { error(_("failed to create directories for '%s'"), filename); goto error; @@ -2355,6 +2364,23 @@ static int systemd_timer_write_unit_templates(const char *exec_path) if (!file) goto error; + switch (schedule) { + case SCHEDULE_HOURLY: + schedule_pattern = xstrfmt("*-*-* 1..23:%02d:00", minute); + break; + + case SCHEDULE_DAILY: + schedule_pattern = xstrfmt("Tue..Sun *-*-* 0:%02d:00", minute); + break; + + case SCHEDULE_WEEKLY: + schedule_pattern = xstrfmt("Mon 0:%02d:00", minute); + break; + + default: + BUG("Unhandled schedule_priority"); + } + unit = "# This file was created and is maintained by Git.\n" "# Any edits made in this file might be replaced in the future\n" "# by a Git command.\n" @@ -2363,12 +2389,12 @@ static int systemd_timer_write_unit_templates(const char *exec_path) "Description=Optimize Git repositories data\n" "\n" "[Timer]\n" - "OnCalendar=%i\n" + "OnCalendar=%s\n" "Persistent=true\n" "\n" "[Install]\n" "WantedBy=timers.target\n"; - if (fputs(unit, file) == EOF) { + if (fprintf(file, unit, schedule_pattern) < 0) { error(_("failed to write to '%s'"), filename); fclose(file); goto error; @@ -2377,9 +2403,36 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(schedule_pattern); + free(local_timer_name); free(filename); + return res; +} + +/* + * No matter the schedule, we use the same service and can make use of the + * templating system. When installing git-maintenance@<schedule>.timer, + * systemd will notice that git-maintenance@.service exists as a template + * and will use this file and insert the <schedule> into the template at + * the position of "%i". + */ +static int systemd_timer_write_service_template(const char *exec_path) +{ + int res = -1; + char *filename; + FILE *file; + const char *unit; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); - filename = xdg_config_home_systemd("git-maintenance@.service"); + filename = xdg_config_home_systemd(local_service_name); + if (safe_create_leading_directories(filename)) { + error(_("failed to create directories for '%s'"), filename); + goto error; + } file = fopen_or_warn(filename, "w"); if (!file) goto error; @@ -2412,29 +2465,114 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(local_service_name); free(filename); + return res; +} + +static int systemd_timer_enable_unit(int enable, + enum schedule_priority schedule, + int minute) +{ + const char *cmd = "systemctl"; + struct child_process child = CHILD_PROCESS_INIT; + const char *frequency = get_frequency(schedule); + + /* + * Disabling the systemd unit while it is already disabled makes + * systemctl print an error. + * Let's ignore it since it means we already are in the expected state: + * the unit is disabled. + * + * On the other hand, enabling a systemd unit which is already enabled + * produces no error. + */ + if (!enable) + child.no_stderr = 1; + else if (systemd_timer_write_timer_file(schedule, minute)) + return -1; + + get_schedule_cmd(&cmd, NULL); + strvec_split(&child.args, cmd); + strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", + "--now", NULL); + strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + if (start_command(&child)) + return error(_("failed to start systemctl")); + if (finish_command(&child)) + /* + * Disabling an already disabled systemd unit makes + * systemctl fail. + * Let's ignore this failure. + * + * Enabling an enabled systemd unit doesn't fail. + */ + if (enable) + return error(_("failed to run systemctl")); return 0; +} + +/* + * A previous version of Git wrote the timer units as template files. + * Clean these up, if they exist. + */ +static void systemd_timer_delete_stale_timer_templates(void) +{ + char *timer_template_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "timer"); + char *filename = xdg_config_home_systemd(timer_template_name); + + if (unlink(filename) && !is_missing_file_error(errno)) + warning(_("failed to delete '%s'"), filename); -error: free(filename); - systemd_timer_delete_unit_templates(); - return -1; + free(timer_template_name); +} + +static int systemd_timer_delete_unit_files(void) +{ + systemd_timer_delete_stale_timer_templates(); + + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_delete_timer_file(SCHEDULE_HOURLY) | + systemd_timer_delete_timer_file(SCHEDULE_DAILY) | + systemd_timer_delete_timer_file(SCHEDULE_WEEKLY) | + systemd_timer_delete_service_template(); +} + +static int systemd_timer_delete_units(void) +{ + int minute = get_random_minute(); + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_enable_unit(0, SCHEDULE_HOURLY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_DAILY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_WEEKLY, minute) | + systemd_timer_delete_unit_files(); } static int systemd_timer_setup_units(void) { + int minute = get_random_minute(); const char *exec_path = git_exec_path(); - int ret = systemd_timer_write_unit_templates(exec_path) || - systemd_timer_enable_unit(1, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(1, SCHEDULE_DAILY) || - systemd_timer_enable_unit(1, SCHEDULE_WEEKLY); + int ret = systemd_timer_write_service_template(exec_path) || + systemd_timer_enable_unit(1, SCHEDULE_HOURLY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_DAILY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_WEEKLY, minute); + if (ret) systemd_timer_delete_units(); + else + systemd_timer_delete_stale_timer_templates(); + return ret; } -static int systemd_timer_update_schedule(int run_maintenance, int fd) +static int systemd_timer_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return systemd_timer_setup_units(); @@ -2606,9 +2744,12 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) opts.scheduler = resolve_scheduler(opts.scheduler); validate_scheduler(opts.scheduler); + if (update_background_schedule(&opts, 1)) + die(_("failed to set up maintenance schedule")); + 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); + return 0; } static const char *const builtin_maintenance_stop_usage[] = { diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c index 20d0dfe9cf..66a7389f9f 100644 --- a/builtin/get-tar-commit-id.c +++ b/builtin/get-tar-commit-id.c @@ -4,7 +4,6 @@ #include "builtin.h" #include "commit.h" #include "tar.h" -#include "quote.h" static const char builtin_get_tar_commit_id_usage[] = "git get-tar-commit-id"; diff --git a/builtin/grep.c b/builtin/grep.c index b71222330a..5777ba82a9 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -4,19 +4,16 @@ * Copyright (c) 2006 Junio C Hamano */ #include "builtin.h" +#include "abspath.h" #include "gettext.h" #include "hex.h" #include "repository.h" #include "config.h" -#include "blob.h" -#include "tree.h" -#include "commit.h" #include "tag.h" #include "tree-walk.h" #include "parse-options.h" #include "string-list.h" #include "run-command.h" -#include "userdiff.h" #include "grep.h" #include "quote.h" #include "dir.h" @@ -530,7 +527,7 @@ static int grep_submodule(struct grep_opt *opt, strbuf_addstr(&base, filename); strbuf_addch(&base, '/'); - init_tree_desc(&tree, data, size); + init_tree_desc(&tree, oid, data, size); hit = grep_tree(&subopt, pathspec, &tree, &base, base.len, object_type == OBJ_COMMIT); strbuf_release(&base); @@ -574,7 +571,9 @@ static int grep_cache(struct grep_opt *opt, data = repo_read_object_file(the_repository, &ce->oid, &type, &size); - init_tree_desc(&tree, data, size); + if (!data) + die(_("unable to read tree %s"), oid_to_hex(&ce->oid)); + init_tree_desc(&tree, &ce->oid, data, size); hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0); strbuf_setlen(&name, name_base_len); @@ -670,7 +669,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, oid_to_hex(&entry.oid)); strbuf_addch(base, '/'); - init_tree_desc(&sub, data, size); + init_tree_desc(&sub, &entry.oid, data, size); hit |= grep_tree(opt, pathspec, &sub, base, tn_len, check_attr); free(data); @@ -714,7 +713,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_add(&base, name, len); strbuf_addch(&base, ':'); } - init_tree_desc(&tree, data, size); + init_tree_desc(&tree, &obj->oid, data, size); hit = grep_tree(opt, pathspec, &tree, &base, base.len, obj->type == OBJ_COMMIT); strbuf_release(&base); @@ -812,14 +811,20 @@ static int file_callback(const struct option *opt, const char *arg, int unset) { struct grep_opt *grep_opt = opt->value; int from_stdin; + const char *filename = arg; FILE *patterns; int lno = 0; struct strbuf sb = STRBUF_INIT; BUG_ON_OPT_NEG(unset); - from_stdin = !strcmp(arg, "-"); - patterns = from_stdin ? stdin : fopen(arg, "r"); + if (!*filename) + ; /* leave it as-is */ + else + filename = prefix_filename_except_for_dash(grep_prefix, filename); + + from_stdin = !strcmp(filename, "-"); + patterns = from_stdin ? stdin : fopen(filename, "r"); if (!patterns) die_errno(_("cannot open '%s'"), arg); while (strbuf_getline(&sb, patterns) == 0) { @@ -833,6 +838,8 @@ static int file_callback(const struct option *opt, const char *arg, int unset) if (!from_stdin) fclose(patterns); strbuf_release(&sb); + if (filename != arg) + free((void *)filename); return 0; } diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 5ffec99dce..82ca6d2bfd 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -14,7 +14,6 @@ #include "blob.h" #include "quote.h" #include "parse-options.h" -#include "exec-cmd.h" #include "setup.h" #include "strbuf.h" #include "write-or-die.h" diff --git a/builtin/hook.c b/builtin/hook.c index 09b51a6487..5234693a94 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -3,7 +3,6 @@ #include "gettext.h" #include "hook.h" #include "parse-options.h" -#include "strbuf.h" #include "strvec.h" #define BUILTIN_HOOK_RUN_USAGE \ diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 006ffdc9c5..856428fef9 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -8,11 +8,9 @@ #include "csum-file.h" #include "blob.h" #include "commit.h" -#include "tag.h" #include "tree.h" #include "progress.h" #include "fsck.h" -#include "exec-cmd.h" #include "strbuf.h" #include "streaming.h" #include "thread-utils.h" @@ -26,7 +24,7 @@ #include "setup.h" static const char index_pack_usage[] = -"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; +"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; struct object_entry { struct pack_idx_entry idx; @@ -1166,6 +1164,7 @@ static void parse_pack_objects(unsigned char *hash) struct ofs_delta_entry *ofs_delta = ofs_deltas; struct object_id ref_delta_oid; struct stat st; + git_hash_ctx tmp_ctx; if (verbose) progress = start_progress( @@ -1202,7 +1201,9 @@ static void parse_pack_objects(unsigned char *hash) /* Check pack integrity */ flush(); - the_hash_algo->final_fn(hash, &input_ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &input_ctx); + the_hash_algo->final_fn(hash, &tmp_ctx); if (!hasheq(fill(the_hash_algo->rawsz), hash)) die(_("pack is corrupted (SHA1 mismatch)")); use(the_hash_algo->rawsz); @@ -1254,6 +1255,7 @@ static void resolve_deltas(void) base_cache_limit = delta_base_cache_limit * nr_threads; if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) { init_thread(); + work_lock(); for (i = 0; i < nr_threads; i++) { int ret = pthread_create(&thread_data[i].thread, NULL, threaded_second_pass, thread_data + i); @@ -1261,6 +1263,7 @@ static void resolve_deltas(void) die(_("unable to create thread: %s"), strerror(ret)); } + work_unlock(); for (i = 0; i < nr_threads; i++) pthread_join(thread_data[i].thread, NULL); cleanup_thread(); @@ -1521,14 +1524,12 @@ static void final(const char *final_pack_name, const char *curr_pack_name, struct strbuf pack_name = STRBUF_INIT; struct strbuf index_name = STRBUF_INIT; struct strbuf rev_index_name = STRBUF_INIT; - int err; if (!from_stdin) { close(input_fd); } else { fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name); - err = close(output_fd); - if (err) + if (close(output_fd)) die_errno(_("error while closing pack file")); } @@ -1563,17 +1564,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name, write_or_die(1, buf.buf, buf.len); strbuf_release(&buf); - /* - * Let's just mimic git-unpack-objects here and write - * the last part of the input buffer to stdout. - */ - while (input_len) { - err = xwrite(1, input_buffer + input_offset, input_len); - if (err <= 0) - break; - input_len -= err; - input_offset += err; - } + /* Write the last part of the buffer to stdout */ + write_in_full(1, input_buffer + input_offset, input_len); } strbuf_release(&rev_index_name); @@ -1782,8 +1774,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) } else if (!strcmp(arg, "--check-self-contained-and-connected")) { strict = 1; check_self_contained_and_connected = 1; - } else if (!strcmp(arg, "--fsck-objects")) { + } else if (skip_to_optional_arg(arg, "--fsck-objects", &arg)) { do_fsck_object = 1; + fsck_set_msg_types(&fsck_options, arg); } else if (!strcmp(arg, "--verify")) { verify = 1; } else if (!strcmp(arg, "--verify-stat")) { diff --git a/builtin/init-db.c b/builtin/init-db.c index cb727c826f..0170469b84 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -5,12 +5,13 @@ */ #include "builtin.h" #include "abspath.h" -#include "config.h" #include "environment.h" #include "gettext.h" #include "object-file.h" #include "parse-options.h" #include "path.h" +#include "refs.h" +#include "repository.h" #include "setup.h" #include "strbuf.h" @@ -57,6 +58,7 @@ 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>]\n" " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" + " [--ref-format=<format>]\n" " [-b <branch-name> | --initial-branch=<branch-name>]\n" " [--shared[=<permissions>]] [<directory>]"), NULL @@ -76,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *template_dir = NULL; unsigned int flags = 0; const char *object_format = NULL; + const char *ref_format = NULL; const char *initial_branch = NULL; int hash_algo = GIT_HASH_UNKNOWN; + unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; int init_shared_repository = -1; const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"), @@ -95,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) N_("override the name of the initial branch")), OPT_STRING(0, "object-format", &object_format, N_("hash"), N_("specify the hash algorithm to use")), + OPT_STRING(0, "ref-format", &ref_format, N_("format"), + N_("specify the reference format to use")), OPT_END() }; @@ -158,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) die(_("unknown hash algorithm '%s'"), object_format); } + if (ref_format) { + ref_storage_format = ref_storage_format_by_name(ref_format); + if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format '%s'"), ref_format); + } + if (init_shared_repository != -1) set_shared_repository(init_shared_repository); @@ -236,5 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |= INIT_DB_EXIST_OK; return init_db(git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, init_shared_repository, flags); + ref_storage_format, initial_branch, + init_shared_repository, flags); } diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index c5e8345265..8768bfea3c 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -9,12 +9,13 @@ #include "gettext.h" #include "parse-options.h" #include "string-list.h" +#include "tempfile.h" #include "trailer.h" #include "config.h" static const char * const git_interpret_trailers_usage[] = { N_("git interpret-trailers [--in-place] [--trim-empty]\n" - " [(--trailer <token>[(=|:)<value>])...]\n" + " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n" " [--parse] [<file>...]"), NULL }; @@ -24,21 +25,24 @@ static enum trailer_if_exists if_exists; static enum trailer_if_missing if_missing; static int option_parse_where(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_where(&where, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_where(opt->value, arg); } static int option_parse_if_exists(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_exists(&if_exists, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_exists(opt->value, arg); } static int option_parse_if_missing(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_missing(&if_missing, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_missing(opt->value, arg); } static void new_trailers_clear(struct list_head *trailers) @@ -88,6 +92,102 @@ static int parse_opt_parse(const struct option *opt, const char *arg, return 0; } +static struct tempfile *trailers_tempfile; + +static FILE *create_in_place_tempfile(const char *file) +{ + struct stat st; + struct strbuf filename_template = STRBUF_INIT; + const char *tail; + FILE *outfile; + + if (stat(file, &st)) + die_errno(_("could not stat %s"), file); + if (!S_ISREG(st.st_mode)) + die(_("file %s is not a regular file"), file); + if (!(st.st_mode & S_IWUSR)) + die(_("file %s is not writable by user"), file); + + /* Create temporary file in the same directory as the original */ + tail = strrchr(file, '/'); + if (tail) + strbuf_add(&filename_template, file, tail - file + 1); + strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX"); + + trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode); + strbuf_release(&filename_template); + outfile = fdopen_tempfile(trailers_tempfile, "w"); + if (!outfile) + die_errno(_("could not open temporary file")); + + return outfile; +} + +static void read_input_file(struct strbuf *sb, const char *file) +{ + if (file) { + if (strbuf_read_file(sb, file, 0) < 0) + die_errno(_("could not read input file '%s'"), file); + } else { + if (strbuf_read(sb, fileno(stdin), 0) < 0) + die_errno(_("could not read from stdin")); + } +} + +static void interpret_trailers(const struct process_trailer_options *opts, + struct list_head *new_trailer_head, + const char *file) +{ + LIST_HEAD(head); + struct strbuf sb = STRBUF_INIT; + struct strbuf trailer_block = STRBUF_INIT; + struct trailer_info info; + FILE *outfile = stdout; + + trailer_config_init(); + + read_input_file(&sb, file); + + if (opts->in_place) + outfile = create_in_place_tempfile(file); + + parse_trailers(opts, &info, sb.buf, &head); + + /* Print the lines before the trailers */ + if (!opts->only_trailers) + fwrite(sb.buf, 1, info.trailer_block_start, outfile); + + if (!opts->only_trailers && !info.blank_line_before_trailer) + fprintf(outfile, "\n"); + + + if (!opts->only_input) { + LIST_HEAD(config_head); + LIST_HEAD(arg_head); + parse_trailers_from_config(&config_head); + parse_trailers_from_command_line_args(&arg_head, new_trailer_head); + list_splice(&config_head, &arg_head); + process_trailers_lists(&head, &arg_head); + } + + /* Print trailer block. */ + format_trailers(opts, &head, &trailer_block); + free_trailers(&head); + fwrite(trailer_block.buf, 1, trailer_block.len, outfile); + strbuf_release(&trailer_block); + + /* Print the lines after the trailers as is */ + if (!opts->only_trailers) + fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile); + trailer_info_release(&info); + + if (opts->in_place) + if (rename_tempfile(&trailers_tempfile, file)) + die_errno(_("could not rename temporary file to %s"), file); + + strbuf_release(&sb); +} + int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) { struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; @@ -97,19 +197,19 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")), OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")), - OPT_CALLBACK(0, "where", NULL, N_("action"), + OPT_CALLBACK(0, "where", &where, N_("placement"), N_("where to place the new trailer"), option_parse_where), - OPT_CALLBACK(0, "if-exists", NULL, N_("action"), + OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"), N_("action if trailer already exists"), option_parse_if_exists), - OPT_CALLBACK(0, "if-missing", NULL, N_("action"), + OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"), N_("action if trailer is missing"), option_parse_if_missing), OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), - OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")), - OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")), - OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"), + OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")), + OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")), + OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), - OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")), + OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")), OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), N_("trailer(s) to add"), option_parse_trailer), OPT_END() @@ -129,11 +229,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) if (argc) { int i; for (i = 0; i < argc; i++) - process_trailers(argv[i], &opts, &trailers); + interpret_trailers(&opts, &trailers, argv[i]); } else { if (opts.in_place) die(_("no input file given for in-place editing")); - process_trailers(NULL, &opts, &trailers); + interpret_trailers(&opts, &trailers, NULL); } new_trailers_clear(&trailers); diff --git a/builtin/log.c b/builtin/log.c index 5d808c92f4..c0a8bb95e9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -26,7 +26,6 @@ #include "tag.h" #include "reflog-walk.h" #include "patch-ids.h" -#include "run-command.h" #include "shortlog.h" #include "remote.h" #include "string-list.h" @@ -36,7 +35,6 @@ #include "streaming.h" #include "version.h" #include "mailmap.h" -#include "gpg-interface.h" #include "progress.h" #include "commit-slab.h" #include "repository.h" @@ -118,16 +116,19 @@ 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) +static int clear_decorations_callback(const struct option *opt UNUSED, + const char *arg, int unset) { + BUG_ON_OPT_NEG(unset); + BUG_ON_OPT_ARG(arg); 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) +static int decorate_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) decoration_style = 0; @@ -173,16 +174,15 @@ static void cmd_log_init_defaults(struct rev_info *rev) if (default_follow) rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; + init_diffstat_widths(&rev->diffopt); rev->diffopt.flags.recursive = 1; - rev->diffopt.stat_width = -1; /* use full terminal width */ - rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ + rev->diffopt.flags.allow_textconv = 1; rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; rev->patch_name_max = fmt_patch_name_max; rev->show_signature = default_show_signature; rev->encode_email_headers = default_encode_email_headers; - rev->diffopt.flags.allow_textconv = 1; if (default_date_mode) parse_date_format(default_date_mode, &rev->date_mode); @@ -592,8 +592,11 @@ static int git_log_config(const char *var, const char *value, decoration_style = 0; /* maybe warn? */ return 0; } - if (!strcmp(var, "log.diffmerges")) + if (!strcmp(var, "log.diffmerges")) { + if (!value) + return config_error_nonbool(var); return diff_merges_config(value); + } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); return 0; @@ -1253,7 +1256,15 @@ static void show_diffstat(struct rev_info *rev, fprintf(rev->diffopt.file, "\n"); } +static void read_desc_file(struct strbuf *buf, const char *desc_file) +{ + if (strbuf_read_file(buf, desc_file, 0) < 0) + die_errno(_("unable to read branch description file '%s'"), + desc_file); +} + static void prepare_cover_text(struct pretty_print_context *pp, + const char *description_file, const char *branch_name, struct strbuf *sb, const char *encoding, @@ -1267,7 +1278,9 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (cover_from_description_mode == COVER_FROM_NONE) goto do_pp; - if (branch_name && *branch_name) + if (description_file && *description_file) + read_desc_file(&description_sb, description_file); + else if (branch_name && *branch_name) read_branch_desc(&description_sb, branch_name); if (!description_sb.len) goto do_pp; @@ -1284,7 +1297,7 @@ static void prepare_cover_text(struct pretty_print_context *pp, subject = subject_sb.buf; do_pp: - pp_title_line(pp, &subject, sb, encoding, need_8bit_cte); + pp_email_subject(pp, &subject, sb, encoding, need_8bit_cte); pp_remainder(pp, &body, sb, 0); strbuf_release(&description_sb); @@ -1313,6 +1326,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct commit *origin, int nr, struct commit **list, + const char *description_file, const char *branch_name, int quiet) { @@ -1350,11 +1364,13 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.fmt = CMIT_FMT_EMAIL; pp.date_mode.type = DATE_RFC2822; pp.rev = rev; - pp.print_email_subject = 1; + pp.encode_email_headers = rev->encode_email_headers; pp_user_info(&pp, NULL, &sb, committer, encoding); - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte); + prepare_cover_text(&pp, description_file, branch_name, &sb, + encoding, need_8bit_cte); fprintf(rev->diffopt.file, "%s\n", sb.buf); + free(pp.after_subject); strbuf_release(&sb); shortlog_init(&log); @@ -1468,19 +1484,16 @@ static int subject_prefix = 0; static int subject_prefix_callback(const struct option *opt, const char *arg, int unset) { + struct strbuf *sprefix; + BUG_ON_OPT_NEG(unset); + sprefix = opt->value; subject_prefix = 1; - ((struct rev_info *)opt->value)->subject_prefix = arg; + strbuf_reset(sprefix); + strbuf_addstr(sprefix, arg); return 0; } -static int rfc_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return subject_prefix_callback(opt, "RFC PATCH", unset); -} - static int numbered_cmdline_opt = 0; static int numbered_callback(const struct option *opt, const char *arg, @@ -1555,7 +1568,8 @@ static int inline_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int header_callback(const struct option *opt, const char *arg, int unset) +static int header_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) { string_list_clear(&extra_hdr, 0); @@ -1567,24 +1581,6 @@ static int header_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int to_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_to, 0); - else - string_list_append(&extra_to, arg); - return 0; -} - -static int cc_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_cc, 0); - else - string_list_append(&extra_cc, arg); - return 0; -} - static int from_callback(const struct option *opt, const char *arg, int unset) { char **from = opt->value; @@ -1629,7 +1625,7 @@ static struct commit *get_base_commit(const char *base_commit, { struct commit *base = NULL; struct commit **rev; - int i = 0, rev_nr = 0, auto_select, die_on_failure; + int i = 0, rev_nr = 0, auto_select, die_on_failure, ret; switch (auto_base) { case AUTO_BASE_NEVER: @@ -1662,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit, struct branch *curr_branch = branch_get(NULL); const char *upstream = branch_get_upstream(curr_branch, NULL); if (upstream) { - struct commit_list *base_list; + struct commit_list *base_list = NULL; struct commit *commit; struct object_id oid; @@ -1673,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit, return NULL; } commit = lookup_commit_or_die(&oid, "upstream base"); - base_list = repo_get_merge_bases_many(the_repository, - commit, total, - list); - /* There should be one and only one merge base. */ - if (!base_list || base_list->next) { + if (repo_get_merge_bases_many(the_repository, + commit, total, + list, + &base_list) < 0 || + /* There should be one and only one merge base. */ + !base_list || base_list->next) { if (die_on_failure) { die(_("could not find exact merge base")); } else { @@ -1708,11 +1705,11 @@ static struct commit *get_base_commit(const char *base_commit, */ while (rev_nr > 1) { for (i = 0; i < rev_nr / 2; i++) { - struct commit_list *merge_base; - merge_base = repo_get_merge_bases(the_repository, - rev[2 * i], - rev[2 * i + 1]); - if (!merge_base || merge_base->next) { + struct commit_list *merge_base = NULL; + if (repo_get_merge_bases(the_repository, + rev[2 * i], + rev[2 * i + 1], &merge_base) < 0 || + !merge_base || merge_base->next) { if (die_on_failure) { die(_("failed to find exact merge base")); } else { @@ -1729,7 +1726,10 @@ static struct commit *get_base_commit(const char *base_commit, rev_nr = DIV_ROUND_UP(rev_nr, 2); } - if (!repo_in_merge_bases(the_repository, base, rev[0])) { + ret = repo_in_merge_bases(the_repository, base, rev[0]); + if (ret < 0) + exit(128); + if (!ret) { if (die_on_failure) { die(_("base commit should be the ancestor of revision list")); } else { @@ -1893,6 +1893,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int quiet = 0; const char *reroll_count = NULL; char *cover_from_description_arg = NULL; + char *description_file = NULL; char *branch_name = NULL; char *base_commit = NULL; struct base_tree_info bases; @@ -1907,6 +1908,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf rdiff_title = STRBUF_INIT; struct strbuf sprefix = STRBUF_INIT; int creation_factor = -1; + int rfc = 0; const struct option builtin_format_patch_options[] = { OPT_CALLBACK_F('n', "numbered", &numbered, NULL, @@ -1930,13 +1932,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("mark the series as Nth re-roll")), OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max, N_("max length of output filename")), - OPT_CALLBACK_F(0, "rfc", &rev, NULL, - N_("use [RFC PATCH] instead of [PATCH]"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback), + OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")), OPT_STRING(0, "cover-from-description", &cover_from_description_arg, N_("cover-from-description-mode"), N_("generate parts of a cover letter based on a branch's description")), - OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"), + OPT_FILENAME(0, "description-file", &description_file, + N_("use branch description from file")), + OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"), N_("use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), OPT_CALLBACK_F('o', "output-directory", &output_directory, @@ -1957,8 +1959,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_GROUP(N_("Messaging")), OPT_CALLBACK(0, "add-header", NULL, N_("header"), N_("add email header"), header_callback), - OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback), - OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback), + OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")), + OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")), OPT_CALLBACK_F(0, "from", &from, N_("ident"), N_("set From address to <ident> (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback), @@ -2016,11 +2018,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.max_parents = 1; rev.diffopt.flags.recursive = 1; rev.diffopt.no_free = 1; - rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; + strbuf_addstr(&sprefix, fmt_patch_subject_prefix); if (format_no_prefix) diff_set_noprefix(&rev.diffopt); @@ -2048,13 +2050,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (cover_from_description_arg) cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); + if (rfc) + strbuf_insertstr(&sprefix, 0, "RFC "); + if (reroll_count) { - strbuf_addf(&sprefix, "%s v%s", - rev.subject_prefix, reroll_count); + strbuf_addf(&sprefix, " v%s", reroll_count); rev.reroll_count = reroll_count; - rev.subject_prefix = sprefix.buf; } + rev.subject_prefix = sprefix.buf; + for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); @@ -2321,7 +2326,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet); print_bases(&bases, rev.diffopt.file); print_signature(rev.diffopt.file); total++; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a0229c3277..92f94e65bf 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -14,19 +14,15 @@ #include "gettext.h" #include "object-name.h" #include "strbuf.h" -#include "tree.h" -#include "cache-tree.h" #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" #include "path.h" #include "pathspec.h" #include "read-cache.h" -#include "run-command.h" #include "setup.h" #include "sparse-index.h" #include "submodule.h" -#include "submodule-config.h" #include "object-store.h" #include "hex.h" diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index fc76575430..e8d65ebbdc 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -5,7 +5,6 @@ #include "pkt-line.h" #include "ref-filter.h" #include "remote.h" -#include "refs.h" #include "parse-options.h" #include "wildmatch.h" @@ -58,6 +57,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) struct transport *transport; const struct ref *ref; struct ref_array ref_array; + struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct option options[] = { @@ -141,13 +141,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) item->symref = xstrdup_or_null(ref->symref); } - if (sorting_options.nr) { - struct ref_sorting *sorting; - - sorting = ref_sorting_options(&sorting_options); - ref_array_sort(sorting, &ref_array); - ref_sorting_release(sorting); - } + sorting = ref_sorting_options(&sorting_options); + ref_array_sort(sorting, &ref_array); for (i = 0; i < ref_array.nr; i++) { const struct ref_array_item *ref = ref_array.items[i]; @@ -157,6 +152,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) status = 0; /* we found something */ } + ref_sorting_release(sorting); ref_array_clear(&ref_array); if (transport_disconnect(transport)) status = 1; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index f558db5f3b..61a2965c8a 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -9,9 +9,7 @@ #include "hex.h" #include "object-name.h" #include "object-store-ll.h" -#include "blob.h" #include "tree.h" -#include "commit.h" #include "path.h" #include "quote.h" #include "parse-options.h" @@ -241,7 +239,8 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, return recurse; } -static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, +static int show_tree_name_only(const struct object_id *oid UNUSED, + struct strbuf *base, const char *pathname, unsigned mode, void *context) { @@ -376,6 +375,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; + struct object_context obj_context; int ret; git_config(git_default_config, NULL); @@ -407,7 +407,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); - if (repo_get_oid(the_repository, argv[0], &oid)) + if (get_oid_with_context(the_repository, argv[0], + GET_OID_HASH_ANY, &oid, + &obj_context)) die("Not a valid object name %s", argv[0]); /* diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 53b55dd71c..53a22645da 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -6,7 +6,6 @@ #include "abspath.h" #include "environment.h" #include "gettext.h" -#include "utf8.h" #include "strbuf.h" #include "mailinfo.h" #include "parse-options.h" diff --git a/builtin/merge-base.c b/builtin/merge-base.c index e68b7fe45d..5a8e729502 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -3,9 +3,6 @@ #include "commit.h" #include "gettext.h" #include "hex.h" -#include "refs.h" -#include "diff.h" -#include "revision.h" #include "object-name.h" #include "parse-options.h" #include "repository.h" @@ -13,10 +10,13 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { - struct commit_list *result, *r; + struct commit_list *result = NULL, *r; - result = repo_get_merge_bases_many_dirty(the_repository, rev[0], - rev_nr - 1, rev + 1); + if (repo_get_merge_bases_many_dirty(the_repository, rev[0], + rev_nr - 1, rev + 1, &result) < 0) { + free_commit_list(result); + return -1; + } if (!result) return 1; @@ -77,13 +77,17 @@ static int handle_independent(int count, const char **args) static int handle_octopus(int count, const char **args, int show_all) { struct commit_list *revs = NULL; - struct commit_list *result, *rev; + struct commit_list *result = NULL, *rev; int i; for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = get_octopus_merge_bases(revs); + if (get_octopus_merge_bases(revs, &result) < 0) { + free_commit_list(revs); + free_commit_list(result); + return 128; + } free_commit_list(revs); reduce_heads_replace(&result); @@ -103,12 +107,16 @@ static int handle_octopus(int count, const char **args, int show_all) static int handle_is_ancestor(int argc, const char **argv) { struct commit *one, *two; + int ret; if (argc != 2) die("--is-ancestor takes exactly two commits"); one = get_commit_reference(argv[0]); two = get_commit_reference(argv[1]); - if (repo_in_merge_bases(the_repository, one, two)) + ret = repo_in_merge_bases(the_repository, one, two); + if (ret < 0) + exit(128); + if (ret) return 0; else return 1; diff --git a/builtin/merge-file.c b/builtin/merge-file.c index d7eb4c6540..1f987334a3 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -1,5 +1,9 @@ #include "builtin.h" #include "abspath.h" +#include "diff.h" +#include "hex.h" +#include "object-name.h" +#include "object-store.h" #include "config.h" #include "gettext.h" #include "setup.h" @@ -25,16 +29,41 @@ static int label_cb(const struct option *opt, const char *arg, int unset) return 0; } +static int set_diff_algorithm(xpparam_t *xpp, + const char *alg) +{ + long diff_algorithm = parse_algorithm_value(alg); + if (diff_algorithm < 0) + return -1; + xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm; + return 0; +} + +static int diff_algorithm_cb(const struct option *opt, + const char *arg, int unset) +{ + xpparam_t *xpp = opt->value; + + BUG_ON_OPT_NEG(unset); + + if (set_diff_algorithm(xpp, arg)) + return error(_("option diff-algorithm accepts \"myers\", " + "\"minimal\", \"patience\" and \"histogram\"")); + + return 0; +} + int cmd_merge_file(int argc, const char **argv, const char *prefix) { 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 ret = 0, i = 0, to_stdout = 0, object_id = 0; int quiet = 0; struct option options[] = { OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")), + OPT_BOOL(0, "object-id", &object_id, N_("use object IDs instead of filenames")), OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3), OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"), XDL_MERGE_ZEALOUS_DIFF3), @@ -44,6 +73,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) XDL_MERGE_FAVOR_THEIRS), OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"), XDL_MERGE_FAVOR_UNION), + OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"), + N_("choose a diff algorithm"), + PARSE_OPT_NONEG, diff_algorithm_cb), OPT_INTEGER(0, "marker-size", &xmp.marker_size, N_("for conflicts, use this marker size")), OPT__QUIET(&quiet, N_("do not warn about conflicts")), @@ -71,8 +103,12 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) return error_errno("failed to redirect stderr to /dev/null"); } + if (object_id) + setup_git_directory(); + for (i = 0; i < 3; i++) { char *fname; + struct object_id oid; mmfile_t *mmf = mmfs + i; if (!names[i]) @@ -80,12 +116,22 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) fname = prefix_filename(prefix, argv[i]); - if (read_mmfile(mmf, fname)) + if (object_id) { + if (repo_get_oid(the_repository, argv[i], &oid)) + ret = error(_("object '%s' does not exist"), + argv[i]); + else if (!oideq(&oid, the_hash_algo->empty_blob)) + read_mmblob(mmf, &oid); + else + read_mmfile(mmf, "/dev/null"); + } else if (read_mmfile(mmf, fname)) { ret = -1; - else if (mmf->size > MAX_XDIFF_SIZE || - buffer_is_binary(mmf->ptr, mmf->size)) + } + if (ret != -1 && (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) @@ -99,20 +145,32 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result); if (ret >= 0) { - const char *filename = argv[0]; - char *fpath = prefix_filename(prefix, argv[0]); - FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); - - if (!f) - ret = error_errno("Could not open %s for writing", - filename); - else if (result.size && - fwrite(result.ptr, result.size, 1, f) != 1) - ret = error_errno("Could not write to %s", filename); - else if (fclose(f)) - ret = error_errno("Could not close %s", filename); + if (object_id && !to_stdout) { + struct object_id oid; + if (result.size) { + if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0) + ret = error(_("Could not write object file")); + } else { + oidcpy(&oid, the_hash_algo->empty_blob); + } + if (ret >= 0) + printf("%s\n", oid_to_hex(&oid)); + } else { + const char *filename = argv[0]; + char *fpath = prefix_filename(prefix, argv[0]); + FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); + + if (!f) + ret = error_errno("Could not open %s for writing", + filename); + else if (result.size && + fwrite(result.ptr, result.size, 1, f) != 1) + ret = error_errno("Could not write to %s", filename); + else if (fclose(f)) + ret = error_errno("Could not close %s", filename); + free(fpath); + } free(result.ptr); - free(fpath); } if (ret > 127) diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 3366699657..c2ce044a20 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -1,13 +1,10 @@ #include "builtin.h" #include "advice.h" -#include "commit.h" #include "gettext.h" #include "hash.h" -#include "tag.h" #include "merge-recursive.h" #include "object-name.h" #include "repository.h" -#include "xdiff-interface.h" static const char builtin_merge_recursive_usage[] = "git %s <base>... -- <head> <remote> ..."; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 0de42aecf4..05d0cad554 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -13,11 +13,11 @@ #include "parse-options.h" #include "repository.h" #include "blob.h" -#include "exec-cmd.h" #include "merge-blobs.h" #include "quote.h" #include "tree.h" #include "config.h" +#include "strvec.h" static int line_termination = '\n'; @@ -414,6 +414,7 @@ struct merge_tree_options { int show_messages; int name_only; int use_stdin; + struct merge_options merge_options; }; static int real_merge(struct merge_tree_options *o, @@ -423,47 +424,61 @@ static int real_merge(struct merge_tree_options *o, { 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; + struct merge_options opt; - 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); - + copy_merge_options(&opt, &o->merge_options); 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); + /* + * We actually only need the trees because we already + * have a merge base. + */ + struct object_id base_oid, head_oid, merge_oid; + + if (repo_get_oid_treeish(the_repository, merge_base, &base_oid)) + die(_("could not parse as tree '%s'"), merge_base); + base_tree = parse_tree_indirect(&base_oid); + if (!base_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&base_oid)); + if (repo_get_oid_treeish(the_repository, branch1, &head_oid)) + die(_("could not parse as tree '%s'"), branch1); + parent1_tree = parse_tree_indirect(&head_oid); + if (!parent1_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&head_oid)); + if (repo_get_oid_treeish(the_repository, branch2, &merge_oid)) + die(_("could not parse as tree '%s'"), branch2); + parent2_tree = parse_tree_indirect(&merge_oid); + if (!parent2_tree) + die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid)); opt.ancestor = merge_base; - base_tree = repo_get_commit_tree(the_repository, base_commit); - parent1_tree = repo_get_commit_tree(the_repository, parent1); - parent2_tree = repo_get_commit_tree(the_repository, parent2); merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result); } else { + 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")); + /* * Get the merge bases, in reverse order; see comment above * merge_incore_recursive in merge-ort.h */ - merge_bases = repo_get_merge_bases(the_repository, parent1, - parent2); + if (repo_get_merge_bases(the_repository, parent1, + parent2, &merge_bases) < 0) + exit(128); if (!merge_bases && !o->allow_unrelated_histories) die(_("refusing to merge unrelated histories")); merge_bases = reverse_commit_list(merge_bases); @@ -507,12 +522,14 @@ static int real_merge(struct merge_tree_options *o, if (o->use_stdin) putchar(line_termination); merge_finalize(&opt, &result); + clear_merge_options(&opt); 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 }; + struct strvec xopts = STRVEC_INIT; int expected_remaining_argc; int original_argc; const char *merge_base = NULL; @@ -548,14 +565,25 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) &merge_base, N_("commit"), N_("specify a merge-base for the merge")), + OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy")), OPT_END() }; + /* Init merge options */ + init_merge_options(&o.merge_options, the_repository); + /* 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); + if (xopts.nr && o.mode == MODE_TRIVIAL) + die(_("--trivial-merge is incompatible with all other options")); + for (int x = 0; x < xopts.nr; x++) + if (parse_merge_opt(&o.merge_options, xopts.v[x])) + die(_("unknown strategy option: -X%s"), xopts.v[x]); + /* Handle --stdin */ if (o.use_stdin) { struct strbuf buf = STRBUF_INIT; @@ -563,7 +591,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) if (o.mode == MODE_TRIVIAL) die(_("--trivial-merge is incompatible with all other options")); if (merge_base) - die(_("--merge-base is incompatible with --stdin")); + die(_("options '%s' and '%s' cannot be used together"), + "--merge-base", "--stdin"); line_termination = '\0'; while (strbuf_getline_lf(&buf, stdin) != EOF) { struct strbuf **split; diff --git a/builtin/merge.c b/builtin/merge.c index de68910177..1cbd6a899c 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -31,8 +31,6 @@ #include "unpack-trees.h" #include "cache-tree.h" #include "dir.h" -#include "utf8.h" -#include "log-tree.h" #include "color.h" #include "rerere.h" #include "help.h" @@ -42,10 +40,8 @@ #include "resolve-undo.h" #include "remote.h" #include "fmt-merge-msg.h" -#include "gpg-interface.h" #include "sequencer.h" #include "string-list.h" -#include "packfile.h" #include "tag.h" #include "alias.h" #include "branch.h" @@ -79,8 +75,7 @@ static int overwrite_ignore = 1; static struct strbuf merge_msg = STRBUF_INIT; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; -static const char **xopts; -static size_t xopts_nr, xopts_alloc; +static struct strvec xopts = STRVEC_INIT; static const char *branch; static char *branch_mergeoptions; static int verbosity; @@ -197,8 +192,7 @@ static struct strategy *get_strategy(const char *name) int j, found = 0; struct cmdname *ent = main_cmds.names[i]; 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]) + if (!xstrncmpz(all_strategy[j].name, ent->name, ent->len)) found = 1; if (!found) add_cmdname(¬_strategies, ent->name, ent->len); @@ -232,7 +226,7 @@ static void append_strategy(struct strategy *s) use_strategies[use_strategies_nr++] = s; } -static int option_parse_strategy(const struct option *opt, +static int option_parse_strategy(const struct option *opt UNUSED, const char *name, int unset) { if (unset) @@ -242,29 +236,9 @@ static int option_parse_strategy(const struct option *opt, return 0; } -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - return 0; - - ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); - xopts[xopts_nr++] = xstrdup(arg); - return 0; -} - -static int option_parse_n(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_ARG(arg); - show_diffstat = unset; - return 0; -} - static struct option builtin_merge_options[] = { - OPT_CALLBACK_F('n', NULL, NULL, NULL, - N_("do not show a diffstat at the end of the merge"), - PARSE_OPT_NOARG, option_parse_n), + OPT_SET_INT('n', NULL, &show_diffstat, + N_("do not show a diffstat at the end of the merge"), 0), OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), @@ -285,10 +259,10 @@ static struct option builtin_merge_options[] = { OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_BOOL(0, "verify-signatures", &verify_signatures, N_("verify that the named commit has a valid GPG signature")), - OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), + OPT_CALLBACK('s', "strategy", NULL, N_("strategy"), N_("merge strategy to use"), option_parse_strategy), - OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), - N_("option for selected merge strategy"), option_parse_x), + OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy")), OPT_CALLBACK('m', "message", &merge_msg, N_("message"), N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), @@ -487,8 +461,7 @@ static void finish(struct commit *head_commit, if (new_head && show_diffstat) { struct diff_options opts; repo_diff_setup(the_repository, &opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; @@ -502,7 +475,7 @@ static void finish(struct commit *head_commit, run_hooks_l("post-merge", squash ? "1" : "0", NULL); if (new_head) - apply_autostash(git_path_merge_autostash(the_repository)); + apply_autostash_ref(the_repository, "MERGE_AUTOSTASH"); strbuf_release(&reflog_message); } @@ -704,7 +677,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, 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); + init_tree_desc(t+i, &trees[i]->object.oid, + trees[i]->buffer, trees[i]->size); } if (unpack_trees(nr_trees, t, &opts)) return -1; @@ -749,9 +723,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, o.show_rename_progress = show_progress == -1 ? isatty(2) : show_progress; - for (x = 0; x < xopts_nr; x++) - if (parse_merge_opt(&o, xopts[x])) - die(_("unknown strategy option: -X%s"), xopts[x]); + for (x = 0; x < xopts.nr; x++) + if (parse_merge_opt(&o, xopts.v[x])) + die(_("unknown strategy option: -X%s"), xopts.v[x]); o.branch1 = head_arg; o.branch2 = merge_remote_util(remoteheads->item)->name; @@ -777,7 +751,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, return clean ? 0 : 1; } else { return try_merge_command(the_repository, - strategy, xopts_nr, xopts, + strategy, xopts.nr, xopts.v, common, head_arg, remoteheads); } } @@ -891,7 +865,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) _(no_scissors_editor_comment), comment_line_char); } if (signoff) - append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0); + append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0); write_merge_heads(remoteheads); write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len); if (run_commit_hook(0 < option_edit, get_index_file(), NULL, @@ -1341,7 +1315,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; - struct strbuf stash_oid = STRBUF_INIT; + char stash_oid_hex[GIT_MAX_HEXSZ + 1]; + struct object_id stash_oid = {0}; if (orig_argc != 2) usage_msg_opt(_("--abort expects no arguments"), @@ -1350,17 +1325,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!file_exists(git_path_merge_head(the_repository))) die(_("There is no merge to abort (MERGE_HEAD missing).")); - if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository), - READ_ONELINER_SKIP_IF_EMPTY)) - unlink(git_path_merge_autostash(the_repository)); + if (!read_ref("MERGE_AUTOSTASH", &stash_oid)) + delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF); /* Invoke 'git reset --merge' */ ret = cmd_reset(nargc, nargv, prefix); - if (stash_oid.len) - apply_autostash_oid(stash_oid.buf); + if (!is_null_oid(&stash_oid)) { + oid_to_hex_r(stash_oid_hex, &stash_oid); + apply_autostash_oid(stash_oid_hex); + } - strbuf_release(&stash_oid); goto done; } @@ -1539,13 +1514,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!remoteheads) ; /* already up-to-date */ - else if (!remoteheads->next) - common = repo_get_merge_bases(the_repository, head_commit, - remoteheads->item); - else { + else if (!remoteheads->next) { + if (repo_get_merge_bases(the_repository, head_commit, + remoteheads->item, &common) < 0) { + ret = 2; + goto done; + } + } else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); - common = get_octopus_merge_bases(list); + if (get_octopus_merge_bases(list, &common) < 0) { + free(list); + ret = 2; + goto done; + } free(list); } @@ -1589,13 +1571,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } if (autostash) - create_autostash(the_repository, - git_path_merge_autostash(the_repository)); + create_autostash_ref(the_repository, "MERGE_AUTOSTASH"); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid, overwrite_ignore)) { - apply_autostash(git_path_merge_autostash(the_repository)); + apply_autostash_ref(the_repository, "MERGE_AUTOSTASH"); ret = 1; goto done; } @@ -1653,17 +1634,21 @@ int cmd_merge(int argc, const char **argv, const char *prefix) struct commit_list *j; for (j = remoteheads; j; j = j->next) { - struct commit_list *common_one; + struct commit_list *common_one = NULL; + struct commit *common_item; /* * Here we *have* to calculate the individual * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = repo_get_merge_bases(the_repository, - head_commit, - j->item); - if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) { + if (repo_get_merge_bases(the_repository, head_commit, + j->item, &common_one) < 0) + exit(128); + + common_item = common_one->item; + free_commit_list(common_one); + if (!oideq(&common_item->object.oid, &j->item->object.oid)) { up_to_date = 0; break; } @@ -1678,8 +1663,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) die_ff_impossible(); if (autostash) - create_autostash(the_repository, - git_path_merge_autostash(the_repository)); + create_autostash_ref(the_repository, "MERGE_AUTOSTASH"); /* We are going to make a new commit. */ git_committer_info(IDENT_STRICT); @@ -1764,7 +1748,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else fprintf(stderr, _("Merge with strategy %s failed.\n"), use_strategies[0]->name); - apply_autostash(git_path_merge_autostash(the_repository)); + apply_autostash_ref(the_repository, "MERGE_AUTOSTASH"); ret = 2; goto done; } else if (best_strategy == wt_strategy) diff --git a/builtin/mktag.c b/builtin/mktag.c index d8e0b5afc0..4767f1a97e 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -3,7 +3,6 @@ #include "hex.h" #include "parse-options.h" #include "strbuf.h" -#include "tag.h" #include "replace-object.h" #include "object-file.h" #include "object-store-ll.h" diff --git a/builtin/mv.c b/builtin/mv.c index c596515ad0..22e64fc290 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -15,7 +15,6 @@ #include "pathspec.h" #include "lockfile.h" #include "dir.h" -#include "cache-tree.h" #include "string-list.h" #include "parse-options.h" #include "read-cache-ll.h" diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 2dd1807c4e..ad9930c831 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -15,6 +15,7 @@ #include "commit-slab.h" #include "commit-graph.h" #include "wildmatch.h" +#include "mem-pool.h" /* * One day. See the 'name a rev shortly after epoch' test in t6120 when @@ -155,30 +156,25 @@ static struct rev_name *create_or_update_name(struct commit *commit, return name; } -static char *get_parent_name(const struct rev_name *name, int parent_number) +static char *get_parent_name(const struct rev_name *name, int parent_number, + struct mem_pool *string_pool) { - struct strbuf sb = STRBUF_INIT; size_t len; strip_suffix(name->tip_name, "^0", &len); if (name->generation > 0) { - strbuf_grow(&sb, len + - 1 + decimal_width(name->generation) + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name, - name->generation, parent_number); + return mem_pool_strfmt(string_pool, "%.*s~%d^%d", + (int)len, name->tip_name, + name->generation, parent_number); } else { - strbuf_grow(&sb, len + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name, - parent_number); + return mem_pool_strfmt(string_pool, "%.*s^%d", + (int)len, name->tip_name, parent_number); } - return strbuf_detach(&sb, NULL); } static void name_rev(struct commit *start_commit, const char *tip_name, timestamp_t taggerdate, - int from_tag, int deref) + int from_tag, int deref, struct mem_pool *string_pool) { struct prio_queue queue; struct commit *commit; @@ -195,9 +191,10 @@ static void name_rev(struct commit *start_commit, if (!start_name) return; if (deref) - start_name->tip_name = xstrfmt("%s^0", tip_name); + start_name->tip_name = mem_pool_strfmt(string_pool, "%s^0", + tip_name); else - start_name->tip_name = xstrdup(tip_name); + start_name->tip_name = mem_pool_strdup(string_pool, tip_name); memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */ prio_queue_put(&queue, start_commit); @@ -235,7 +232,8 @@ static void name_rev(struct commit *start_commit, if (parent_number > 1) parent_name->tip_name = get_parent_name(name, - parent_number); + parent_number, + string_pool); else parent_name->tip_name = name->tip_name; ALLOC_GROW(parents_to_queue, @@ -415,7 +413,7 @@ static int name_ref(const char *path, const struct object_id *oid, return 0; } -static void name_tips(void) +static void name_tips(struct mem_pool *string_pool) { int i; @@ -428,7 +426,7 @@ static void name_tips(void) struct tip_table_entry *e = &tip_table.table[i]; if (e->commit) { name_rev(e->commit, e->refname, e->taggerdate, - e->from_tag, e->deref); + e->from_tag, e->deref, string_pool); } } } @@ -561,6 +559,7 @@ static void name_rev_line(char *p, struct name_ref_data *data) int cmd_name_rev(int argc, const char **argv, const char *prefix) { + struct mem_pool string_pool; struct object_array revs = OBJECT_ARRAY_INIT; int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP }; @@ -587,6 +586,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_END(), }; + mem_pool_init(&string_pool, 0); init_commit_rev_name(&rev_names); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); @@ -648,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) adjust_cutoff_timestamp_for_slop(); for_each_ref(name_ref, &data); - name_tips(); + name_tips(&string_pool); if (annotate_stdin) { struct strbuf sb = STRBUF_INIT; @@ -676,6 +676,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) always, allow_undefined, data.name_only); } + UNLEAK(string_pool); UNLEAK(revs); return 0; } diff --git a/builtin/notes.c b/builtin/notes.c index 9f38863dd5..caf20fd5bd 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -9,7 +9,6 @@ #include "builtin.h" #include "config.h" -#include "alloc.h" #include "editor.h" #include "environment.h" #include "gettext.h" @@ -19,7 +18,6 @@ #include "object-store-ll.h" #include "path.h" #include "repository.h" -#include "blob.h" #include "pretty.h" #include "refs.h" #include "exec-cmd.h" @@ -718,9 +716,11 @@ static int append_edit(int argc, const char **argv, const char *prefix) struct strbuf buf = STRBUF_INIT; char *prev_buf = repo_read_object_file(the_repository, note, &type, &size); - if (prev_buf && size) + if (!prev_buf) + die(_("unable to read %s"), oid_to_hex(note)); + if (size) strbuf_add(&buf, prev_buf, size); - if (d.buf.len && prev_buf && size) + if (d.buf.len && size) append_separator(&buf); strbuf_insert(&d.buf, 0, buf.buf, buf.len); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d2a162d528..baf0090fc8 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -6,10 +6,8 @@ #include "config.h" #include "attr.h" #include "object.h" -#include "blob.h" #include "commit.h" #include "tag.h" -#include "tree.h" #include "delta.h" #include "pack.h" #include "pack-revindex.h" @@ -18,7 +16,6 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" -#include "list-objects-filter.h" #include "list-objects-filter-options.h" #include "pack-objects.h" #include "progress.h" @@ -221,13 +218,19 @@ static int thin; static int num_preferred_base; static struct progress *progress_state; -static struct packed_git *reuse_packfile; +static struct bitmapped_pack *reuse_packfiles; +static size_t reuse_packfiles_nr; +static size_t reuse_packfiles_used_nr; static uint32_t reuse_packfile_objects; static struct bitmap *reuse_packfile_bitmap; static int use_bitmap_index_default = 1; static int use_bitmap_index = -1; -static int allow_pack_reuse = 1; +static enum { + NO_PACK_REUSE = 0, + SINGLE_PACK_REUSE, + MULTI_PACK_REUSE, +} allow_pack_reuse = SINGLE_PACK_REUSE; static enum { WRITE_BITMAP_FALSE = 0, WRITE_BITMAP_QUIET, @@ -1013,7 +1016,9 @@ static off_t find_reused_offset(off_t where) return reused_chunks[lo-1].difference; } -static void write_reused_pack_one(size_t pos, struct hashfile *out, +static void write_reused_pack_one(struct packed_git *reuse_packfile, + size_t pos, struct hashfile *out, + off_t pack_start, struct pack_window **w_curs) { off_t offset, next, cur; @@ -1023,7 +1028,8 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out, offset = pack_pos_to_offset(reuse_packfile, pos); next = pack_pos_to_offset(reuse_packfile, pos + 1); - record_reused_object(offset, offset - hashfile_total(out)); + record_reused_object(offset, + offset - (hashfile_total(out) - pack_start)); cur = offset; type = unpack_object_header(reuse_packfile, w_curs, &cur, &size); @@ -1091,41 +1097,93 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out, copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset); } -static size_t write_reused_pack_verbatim(struct hashfile *out, +static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile, + struct hashfile *out, + off_t pack_start, struct pack_window **w_curs) { - size_t pos = 0; + size_t pos = reuse_packfile->bitmap_pos; + size_t end; + + if (pos % BITS_IN_EWORD) { + size_t word_pos = (pos / BITS_IN_EWORD); + size_t offset = pos % BITS_IN_EWORD; + size_t last; + eword_t word = reuse_packfile_bitmap->words[word_pos]; + + if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD) + last = offset + reuse_packfile->bitmap_nr; + else + last = BITS_IN_EWORD; + + for (; offset < last; offset++) { + if (word >> offset == 0) + return word_pos; + if (!bitmap_get(reuse_packfile_bitmap, + word_pos * BITS_IN_EWORD + offset)) + return word_pos; + } + + pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD); + } + + /* + * Now we're going to copy as many whole eword_t's as possible. + * "end" is the index of the last whole eword_t we copy, but + * there may be additional bits to process. Those are handled + * individually by write_reused_pack(). + * + * Begin by advancing to the first word boundary in range of the + * bit positions occupied by objects in "reuse_packfile". Then + * pick the last word boundary in the same range. If we have at + * least one word's worth of bits to process, continue on. + */ + end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr; + if (end % BITS_IN_EWORD) + end -= end % BITS_IN_EWORD; + if (pos >= end) + return reuse_packfile->bitmap_pos / BITS_IN_EWORD; + + while (pos < end && + reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0) + pos += BITS_IN_EWORD; - while (pos < reuse_packfile_bitmap->word_alloc && - reuse_packfile_bitmap->words[pos] == (eword_t)~0) - pos++; + if (pos > end) + pos = end; - if (pos) { - off_t to_write; + if (reuse_packfile->bitmap_pos < pos) { + off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0); + off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p, + pos - reuse_packfile->bitmap_pos); - written = (pos * BITS_IN_EWORD); - to_write = pack_pos_to_offset(reuse_packfile, written) - - sizeof(struct pack_header); + written += pos - reuse_packfile->bitmap_pos; /* We're recording one chunk, not one object. */ - record_reused_object(sizeof(struct pack_header), 0); + record_reused_object(pack_start_off, + pack_start_off - (hashfile_total(out) - pack_start)); hashflush(out); - copy_pack_data(out, reuse_packfile, w_curs, - sizeof(struct pack_header), to_write); + copy_pack_data(out, reuse_packfile->p, w_curs, + pack_start_off, pack_end_off - pack_start_off); display_progress(progress_state, written); } - return pos; + if (pos % BITS_IN_EWORD) + BUG("attempted to jump past a word boundary to %"PRIuMAX, + (uintmax_t)pos); + return pos / BITS_IN_EWORD; } -static void write_reused_pack(struct hashfile *f) +static void write_reused_pack(struct bitmapped_pack *reuse_packfile, + struct hashfile *f) { - size_t i = 0; + size_t i = reuse_packfile->bitmap_pos / BITS_IN_EWORD; uint32_t offset; + off_t pack_start = hashfile_total(f) - sizeof(struct pack_header); struct pack_window *w_curs = NULL; if (allow_ofs_delta) - i = write_reused_pack_verbatim(f, &w_curs); + i = write_reused_pack_verbatim(reuse_packfile, f, pack_start, + &w_curs); for (; i < reuse_packfile_bitmap->word_alloc; ++i) { eword_t word = reuse_packfile_bitmap->words[i]; @@ -1136,16 +1194,23 @@ static void write_reused_pack(struct hashfile *f) break; offset += ewah_bit_ctz64(word >> offset); + if (pos + offset < reuse_packfile->bitmap_pos) + continue; + if (pos + offset >= reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr) + goto done; /* * Can use bit positions directly, even for MIDX * bitmaps. See comment in try_partial_reuse() * for why. */ - write_reused_pack_one(pos + offset, f, &w_curs); + write_reused_pack_one(reuse_packfile->p, + pos + offset - reuse_packfile->bitmap_pos, + f, pack_start, &w_curs); display_progress(progress_state, ++written); } } +done: unuse_pack(&w_curs); } @@ -1197,9 +1262,14 @@ static void write_pack_file(void) offset = write_pack_header(f, nr_remaining); - if (reuse_packfile) { + if (reuse_packfiles_nr) { assert(pack_to_stdout); - write_reused_pack(f); + for (j = 0; j < reuse_packfiles_nr; j++) { + reused_chunks_nr = 0; + write_reused_pack(&reuse_packfiles[j], f); + if (reused_chunks_nr) + reuse_packfiles_used_nr++; + } offset = hashfile_total(f); } @@ -1756,7 +1826,8 @@ static void add_pbase_object(struct tree_desc *tree, tree = pbase_tree_get(&entry.oid); if (!tree) return; - init_tree_desc(&sub, tree->tree_data, tree->tree_size); + init_tree_desc(&sub, &tree->oid, + tree->tree_data, tree->tree_size); add_pbase_object(&sub, down, downlen, fullname); pbase_tree_put(tree); @@ -1816,7 +1887,8 @@ static void add_preferred_base_object(const char *name) } else { struct tree_desc tree; - init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size); + init_tree_desc(&tree, &it->pcache.oid, + it->pcache.tree_data, it->pcache.tree_size); add_pbase_object(&tree, name, cmplen, name); } } @@ -3175,7 +3247,19 @@ static int git_pack_config(const char *k, const char *v, return 0; } if (!strcmp(k, "pack.allowpackreuse")) { - allow_pack_reuse = git_config_bool(k, v); + int res = git_parse_maybe_bool_text(v); + if (res < 0) { + if (!strcasecmp(v, "single")) + allow_pack_reuse = SINGLE_PACK_REUSE; + else if (!strcasecmp(v, "multi")) + allow_pack_reuse = MULTI_PACK_REUSE; + else + die(_("invalid pack.allowPackReuse value: '%s'"), v); + } else if (res) { + allow_pack_reuse = SINGLE_PACK_REUSE; + } else { + allow_pack_reuse = NO_PACK_REUSE; + } return 0; } if (!strcmp(k, "pack.threads")) { @@ -3204,7 +3288,7 @@ static int git_pack_config(const char *k, const char *v, return 0; } if (!strcmp(k, "uploadpack.blobpackfileuri")) { - struct configured_exclusion *ex = xmalloc(sizeof(*ex)); + struct configured_exclusion *ex; const char *oid_end, *pack_end; /* * Stores the pack hash. This is not a true object ID, but is @@ -3212,6 +3296,10 @@ static int git_pack_config(const char *k, const char *v, */ struct object_id pack_hash; + if (!v) + return config_error_nonbool(k); + + ex = xmalloc(sizeof(*ex)); if (parse_oid_hex(v, &ex->e.oid, &oid_end) || *oid_end != ' ' || parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) || @@ -3603,7 +3691,6 @@ static void read_cruft_objects(void) string_list_append(&discard_packs, buf.buf + 1); else string_list_append(&fresh_packs, buf.buf); - strbuf_reset(&buf); } string_list_sort(&discard_packs); @@ -3739,7 +3826,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name, show_object(obj, name, data); } -static int option_parse_missing_action(const struct option *opt, +static int option_parse_missing_action(const struct option *opt UNUSED, const char *arg, int unset) { assert(arg); @@ -3931,7 +4018,7 @@ static void loosen_unused_packed_objects(void) */ static int pack_options_allow_reuse(void) { - return allow_pack_reuse && + return allow_pack_reuse != NO_PACK_REUSE && pack_to_stdout && !ignore_packed_keep_on_disk && !ignore_packed_keep_in_core && @@ -3944,13 +4031,18 @@ static int get_object_list_from_bitmap(struct rev_info *revs) if (!(bitmap_git = prepare_bitmap_walk(revs, 0))) return -1; - if (pack_options_allow_reuse() && - !reuse_partial_packfile_from_bitmap( - bitmap_git, - &reuse_packfile, - &reuse_packfile_objects, - &reuse_packfile_bitmap)) { - assert(reuse_packfile_objects); + if (pack_options_allow_reuse()) + reuse_partial_packfile_from_bitmap(bitmap_git, + &reuse_packfiles, + &reuse_packfiles_nr, + &reuse_packfile_bitmap, + allow_pack_reuse == MULTI_PACK_REUSE); + + if (reuse_packfiles) { + reuse_packfile_objects = bitmap_popcount(reuse_packfile_bitmap); + if (!reuse_packfile_objects) + BUG("expected non-empty reuse bitmap"); + nr_result += reuse_packfile_objects; nr_seen += reuse_packfile_objects; display_progress(progress_state, nr_seen); @@ -4120,34 +4212,37 @@ static void add_extra_kept_packs(const struct string_list *names) static int option_parse_quiet(const struct option *opt, const char *arg, int unset) { + int *val = opt->value; + BUG_ON_OPT_ARG(arg); if (!unset) - progress = 0; - else if (!progress) - progress = 1; + *val = 0; + else if (!*val) + *val = 1; return 0; } static int option_parse_index_version(const struct option *opt, const char *arg, int unset) { + struct pack_idx_option *popts = opt->value; char *c; const char *val = arg; BUG_ON_OPT_NEG(unset); - pack_idx_opts.version = strtoul(val, &c, 10); - if (pack_idx_opts.version > 2) + popts->version = strtoul(val, &c, 10); + if (popts->version > 2) die(_("unsupported index version %s"), val); if (*c == ',' && c[1]) - pack_idx_opts.off32_limit = strtoul(c+1, &c, 0); - if (*c || pack_idx_opts.off32_limit & 0x80000000) + popts->off32_limit = strtoul(c+1, &c, 0); + if (*c || popts->off32_limit & 0x80000000) die(_("bad index version '%s'"), val); return 0; } -static int option_parse_unpack_unreachable(const struct option *opt, +static int option_parse_unpack_unreachable(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4162,7 +4257,7 @@ static int option_parse_unpack_unreachable(const struct option *opt, return 0; } -static int option_parse_cruft_expiration(const struct option *opt, +static int option_parse_cruft_expiration(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4190,7 +4285,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) LIST_OBJECTS_FILTER_INIT; struct option pack_objects_options[] = { - OPT_CALLBACK_F('q', "quiet", NULL, NULL, + OPT_CALLBACK_F('q', "quiet", &progress, NULL, N_("do not show progress meter"), PARSE_OPT_NOARG, option_parse_quiet), OPT_SET_INT(0, "progress", &progress, @@ -4200,7 +4295,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "all-progress-implied", &all_progress_implied, N_("similar to --all-progress when progress meter is shown")), - OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"), + OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"), N_("write the pack index file in the specified idx format version"), PARSE_OPT_NONEG, option_parse_index_version), OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, @@ -4303,6 +4398,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); if (sparse < 0) sparse = the_repository->settings.pack_use_sparse; + if (the_repository->settings.pack_use_multi_pack_reuse) + allow_pack_reuse = MULTI_PACK_REUSE; } reset_pack_idx_option(&pack_idx_opts); @@ -4383,7 +4480,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!HAVE_THREADS && delta_search_threads != 1) warning(_("no threads support, ignoring --threads")); - if (!pack_to_stdout && !pack_size_limit && !cruft) + if (!pack_to_stdout && !pack_size_limit) pack_size_limit = pack_size_limit_cfg; if (pack_to_stdout && pack_size_limit) die(_("--max-pack-size cannot be used to build a pack for transfer")); @@ -4400,12 +4497,8 @@ 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 (filter_options.choice) { - if (!pack_to_stdout) - die(_("cannot use --filter without --stdout")); - if (stdin_packs) - die(_("cannot use --filter with --stdin-packs")); - } + if (stdin_packs && filter_options.choice) + die(_("cannot use --filter with --stdin-packs")); if (stdin_packs && use_internal_rev_list) die(_("cannot use internal rev list with --stdin-packs")); @@ -4415,8 +4508,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) die(_("cannot use internal rev list with --cruft")); if (stdin_packs) die(_("cannot use --stdin-packs with --cruft")); - if (pack_size_limit) - die(_("cannot use --max-pack-size with --cruft")); } /* @@ -4521,11 +4612,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) fprintf_ln(stderr, _("Total %"PRIu32" (delta %"PRIu32")," " reused %"PRIu32" (delta %"PRIu32")," - " pack-reused %"PRIu32), + " pack-reused %"PRIu32" (from %"PRIuMAX")"), written, written_delta, reused, reused_delta, - reuse_packfile_objects); + reuse_packfile_objects, + (uintmax_t)reuse_packfiles_used_nr); + + trace2_data_intmax("pack-objects", the_repository, "written", written); + trace2_data_intmax("pack-objects", the_repository, "written/delta", written_delta); + trace2_data_intmax("pack-objects", the_repository, "reused", reused); + trace2_data_intmax("pack-objects", the_repository, "reused/delta", reused_delta); + trace2_data_intmax("pack-objects", the_repository, "pack-reused", reuse_packfile_objects); + trace2_data_intmax("pack-objects", the_repository, "packs-reused", reuse_packfiles_used_nr); cleanup: + clear_packing_data(&to_pack); list_objects_filter_release(&filter_options); strvec_clear(&rp); diff --git a/builtin/pull.c b/builtin/pull.c index be2b2c9ebc..72cbb76d52 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -14,7 +14,6 @@ #include "merge.h" #include "object-name.h" #include "parse-options.h" -#include "exec-cmd.h" #include "run-command.h" #include "oid-array.h" #include "remote.h" @@ -24,15 +23,11 @@ #include "rebase.h" #include "refs.h" #include "refspec.h" -#include "revision.h" #include "submodule.h" #include "submodule-config.h" -#include "tempfile.h" -#include "lockfile.h" #include "wt-status.h" #include "commit-reach.h" #include "sequencer.h" -#include "packfile.h" /** * Parses the value of --rebase. If value is a false value, returns @@ -820,7 +815,7 @@ static int get_octopus_merge_base(struct object_id *merge_base, const struct object_id *merge_head, const struct object_id *fork_point) { - struct commit_list *revs = NULL, *result; + struct commit_list *revs = NULL, *result = NULL; commit_list_insert(lookup_commit_reference(the_repository, curr_head), &revs); @@ -830,7 +825,8 @@ static int get_octopus_merge_base(struct object_id *merge_base, commit_list_insert(lookup_commit_reference(the_repository, fork_point), &revs); - result = get_octopus_merge_bases(revs); + if (get_octopus_merge_bases(revs, &result) < 0) + exit(128); free_commit_list(revs); reduce_heads_replace(&result); @@ -931,6 +927,8 @@ static int get_can_ff(struct object_id *orig_head, merge_head = lookup_commit_reference(the_repository, orig_merge_head); ret = repo_is_descendant_of(the_repository, merge_head, list); free_commit_list(list); + if (ret < 0) + exit(128); return ret; } @@ -955,6 +953,8 @@ static int already_up_to_date(struct object_id *orig_head, commit_list_insert(theirs, &list); ok = repo_is_descendant_of(the_repository, ours, list); free_commit_list(list); + if (ok < 0) + exit(128); if (!ok) return 0; } diff --git a/builtin/push.c b/builtin/push.c index 2e708383c2..2fbb31c3ad 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -7,7 +7,6 @@ #include "config.h" #include "environment.h" #include "gettext.h" -#include "refs.h" #include "refspec.h" #include "run-command.h" #include "remote.h" @@ -392,7 +391,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs, if (!is_empty_cas(&cas)) { if (!transport->smart_options) die("underlying transport does not support --%s option", - CAS_OPT_NAME); + "force-with-lease"); transport->smart_options->cas = &cas; } @@ -526,26 +525,21 @@ static int git_push_config(const char *k, const char *v, *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM; return 0; } else if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER); - break; - case 1: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS); - break; - default: - if (value && !strcasecmp(value, "if-asked")) - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED); - else - return error(_("invalid value for '%s'"), k); - } + switch (git_parse_maybe_bool(v)) { + case 0: + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER); + break; + case 1: + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS); + break; + default: + if (!strcasecmp(v, "if-asked")) + set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED); + else + return error(_("invalid value for '%s'"), k); } } else if (!strcmp(k, "push.recursesubmodules")) { - const char *value; - if (!git_config_get_value("push.recursesubmodules", &value)) - recurse_submodules = parse_push_recurse_submodules_arg(k, value); + recurse_submodules = parse_push_recurse_submodules_arg(k, v); } else if (!strcmp(k, "submodule.recurse")) { int val = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; @@ -604,7 +598,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN), OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE), - OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), + OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"), N_("require old value of ref to be at this value"), PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option), OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags, @@ -639,8 +633,10 @@ int cmd_push(int argc, const char **argv, const char *prefix) : &push_options_config); set_push_cert_flags(&flags, push_cert); - if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) - die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--branches/--mirror/--tags"); + die_for_incompatible_opt4(deleterefs, "--delete", + tags, "--tags", + flags & TRANSPORT_PUSH_ALL, "--all/--branches", + flags & TRANSPORT_PUSH_MIRROR, "--mirror"); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); @@ -677,19 +673,13 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); if (flags & TRANSPORT_PUSH_ALL) { - if (tags) - die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags"); if (argc >= 2) die(_("--all can't be combined with refspecs")); } if (flags & TRANSPORT_PUSH_MIRROR) { - if (tags) - die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags"); if (argc >= 2) die(_("--mirror can't be combined with refspecs")); } - if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) - die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror"); if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES)) cas.use_force_if_includes = 1; diff --git a/builtin/range-diff.c b/builtin/range-diff.c index e455a4795c..f02cbac087 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -5,7 +5,6 @@ #include "range-diff.h" #include "config.h" #include "repository.h" -#include "revision.h" static const char * const builtin_range_diff_usage[] = { N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"), diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 1fec702a04..6f89cec0fb 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -16,14 +16,12 @@ #include "tree-walk.h" #include "cache-tree.h" #include "unpack-trees.h" -#include "dir.h" #include "parse-options.h" #include "repository.h" #include "resolve-undo.h" #include "setup.h" #include "sparse-index.h" #include "submodule.h" -#include "submodule-config.h" static int nr_trees; static int read_empty; @@ -49,7 +47,7 @@ static const char * const read_tree_usage[] = { NULL }; -static int index_output_cb(const struct option *opt, const char *arg, +static int index_output_cb(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); @@ -263,8 +261,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) cache_tree_free(&the_index.cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; - parse_tree(tree); - init_tree_desc(t+i, tree->buffer, tree->size); + if (parse_tree(tree) < 0) + return 128; + init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size); } if (unpack_trees(nr_trees, t, &opts)) return 128; diff --git a/builtin/rebase.c b/builtin/rebase.c index 50cb85751f..e444ab102d 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -11,14 +11,10 @@ #include "gettext.h" #include "hex.h" #include "run-command.h" -#include "exec-cmd.h" #include "strvec.h" #include "dir.h" -#include "packfile.h" #include "refs.h" -#include "quote.h" #include "config.h" -#include "cache-tree.h" #include "unpack-trees.h" #include "lockfile.h" #include "object-file.h" @@ -149,7 +145,6 @@ struct rebase_options { .reapply_cherry_picks = -1, \ .allow_empty_message = 1, \ .autosquash = -1, \ - .config_autosquash = -1, \ .rebase_merges = -1, \ .config_rebase_merges = -1, \ .update_refs = -1, \ @@ -376,20 +371,6 @@ static int run_sequencer_rebase(struct rebase_options *opts) return ret; } -static void imply_merge(struct rebase_options *opts, const char *option); -static int parse_opt_keep_empty(const struct option *opt, const char *arg, - int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_ARG(arg); - - imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); - opts->keep_empty = !unset; - opts->type = REBASE_MERGE; - return 0; -} - static int is_merge(struct rebase_options *opts) { return opts->type == REBASE_MERGE; @@ -534,7 +515,7 @@ static int finish_rebase(struct rebase_options *opts) int ret = 0; delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - unlink(git_path_auto_merge(the_repository)); + delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF); apply_autostash(state_dir_path("autostash", opts)); /* * We ignore errors in 'git maintenance run --auto', since the @@ -586,18 +567,10 @@ static int move_to_original_branch(struct rebase_options *opts) return ret; } -static const char *resolvemsg = -N_("Resolve all conflicts manually, mark them as resolved with\n" -"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n" -"You can instead skip this commit: run \"git rebase --skip\".\n" -"To abort and get back to the state before \"git rebase\", run " -"\"git rebase --abort\"."); - static int run_am(struct rebase_options *opts) { struct child_process am = CHILD_PROCESS_INIT; struct child_process format_patch = CHILD_PROCESS_INIT; - struct strbuf revisions = STRBUF_INIT; int status; char *rebased_patches; @@ -607,7 +580,7 @@ static int run_am(struct rebase_options *opts) opts->reflog_action); if (opts->action == ACTION_CONTINUE) { strvec_push(&am.args, "--resolved"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg); if (opts->gpg_sign_opt) strvec_push(&am.args, opts->gpg_sign_opt); status = run_command(&am); @@ -618,7 +591,7 @@ static int run_am(struct rebase_options *opts) } if (opts->action == ACTION_SKIP) { strvec_push(&am.args, "--skip"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg); status = run_command(&am); if (status) return status; @@ -630,13 +603,6 @@ static int run_am(struct rebase_options *opts) return run_command(&am); } - strbuf_addf(&revisions, "%s...%s", - oid_to_hex(opts->root ? - /* this is now equivalent to !opts->upstream */ - &opts->onto->object.oid : - &opts->upstream->object.oid), - oid_to_hex(&opts->orig_head->object.oid)); - rebased_patches = xstrdup(git_path("rebased-patches")); format_patch.out = open(rebased_patches, O_WRONLY | O_CREAT | O_TRUNC, 0666); @@ -657,7 +623,12 @@ static int run_am(struct rebase_options *opts) if (opts->git_format_patch_opt.len) strvec_split(&format_patch.args, opts->git_format_patch_opt.buf); - strvec_push(&format_patch.args, revisions.buf); + strvec_pushf(&format_patch.args, "%s...%s", + oid_to_hex(opts->root ? + /* this is now equivalent to !opts->upstream */ + &opts->onto->object.oid : + &opts->upstream->object.oid), + oid_to_hex(&opts->orig_head->object.oid)); if (opts->restrict_revision) strvec_pushf(&format_patch.args, "^%s", oid_to_hex(&opts->restrict_revision->object.oid)); @@ -680,10 +651,8 @@ static int run_am(struct rebase_options *opts) "As a result, git cannot rebase them."), opts->revisions); - strbuf_release(&revisions); return status; } - strbuf_release(&revisions); am.in = open(rebased_patches, O_RDONLY); if (am.in < 0) { @@ -696,7 +665,7 @@ static int run_am(struct rebase_options *opts) strvec_pushv(&am.args, opts->git_am_opts.v); strvec_push(&am.args, "--rebasing"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg); strvec_push(&am.args, "--patch-format=mboxrd"); if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) strvec_push(&am.args, "--rerere-autoupdate"); @@ -724,11 +693,8 @@ static int run_specific_rebase(struct rebase_options *opts) if (opts->type == REBASE_MERGE) { /* Run sequencer-based rebase */ - setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1); - if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { + if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) setenv("GIT_SEQUENCE_EDITOR", ":", 1); - opts->autosquash = 0; - } if (opts->gpg_sign_opt) { /* remove the leading "-S" */ char *tmp = xstrdup(opts->gpg_sign_opt + 2); @@ -893,7 +859,8 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream, if (!upstream) goto done; - merge_bases = repo_get_merge_bases(the_repository, upstream, head); + if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0) + exit(128); if (!merge_bases || merge_bases->next) goto done; @@ -912,8 +879,9 @@ static void fill_branch_base(struct rebase_options *options, { struct commit_list *merge_bases = NULL; - merge_bases = repo_get_merge_bases(the_repository, options->onto, - options->orig_head); + if (repo_get_merge_bases(the_repository, options->onto, + options->orig_head, &merge_bases) < 0) + exit(128); if (!merge_bases || merge_bases->next) oidcpy(branch_base, null_oid()); else @@ -983,6 +951,18 @@ static enum empty_type parse_empty_value(const char *value) die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value); } +static int parse_opt_keep_empty(const struct option *opt, const char *arg, + int unset) +{ + struct rebase_options *opts = opt->value; + + BUG_ON_OPT_ARG(arg); + + imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); + opts->keep_empty = !unset; + return 0; +} + static int parse_opt_empty(const struct option *opt, const char *arg, int unset) { struct rebase_options *options = opt->value; @@ -1147,7 +1127,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) "instead of ignoring them"), 1, PARSE_OPT_HIDDEN), OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), - OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}", + OPT_CALLBACK_F(0, "empty", &options, "(drop|keep|ask)", N_("how to handle commits that become empty"), PARSE_OPT_NONEG, parse_opt_empty), OPT_CALLBACK_F('k', "keep-empty", &options, NULL, @@ -1268,7 +1248,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point"); if (options.action != ACTION_NONE && !in_progress) - die(_("No rebase in progress?")); + die(_("no rebase in progress")); if (options.action == ACTION_EDIT_TODO && !is_merge(&options)) die(_("The --edit-todo action can only be used during " @@ -1407,7 +1387,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || (options.action != ACTION_NONE) || (options.exec.nr > 0) || - (options.autosquash == -1 && options.config_autosquash == 1) || options.autosquash == 1) { allow_preemptive_ff = 0; } @@ -1491,23 +1470,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.strategy) { options.strategy = xstrdup(options.strategy); - switch (options.type) { - case REBASE_APPLY: - die(_("--strategy requires --merge or --interactive")); - case REBASE_MERGE: - /* compatible */ - break; - case REBASE_UNSPECIFIED: - options.type = REBASE_MERGE; - break; - default: - BUG("unhandled rebase type (%d)", options.type); - } + imply_merge(&options, "--strategy"); } - if (options.type == REBASE_MERGE) - imply_merge(&options, "--merge"); - if (options.root && !options.onto_name) imply_merge(&options, "--root without --onto"); @@ -1524,8 +1489,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) 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.rebase_merges == -1 && options.config_rebase_merges == 1) die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges")); else if (options.update_refs == -1 && options.config_update_refs == 1) @@ -1545,14 +1508,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges : ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0); - if (options.autosquash == 1) + if (options.autosquash == 1) { imply_merge(&options, "--autosquash"); - options.autosquash = (options.autosquash >= 0) ? options.autosquash : - ((options.config_autosquash >= 0) ? options.config_autosquash : 0); + } else if (options.autosquash == -1) { + options.autosquash = + options.config_autosquash && + (options.flags & REBASE_INTERACTIVE_EXPLICIT); + } if (options.type == REBASE_UNSPECIFIED) { if (!strcmp(options.default_backend, "merge")) - imply_merge(&options, "--merge"); + options.type = REBASE_MERGE; else if (!strcmp(options.default_backend, "apply")) options.type = REBASE_APPLY; else @@ -1803,8 +1769,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* We want color (if set), but no pager */ repo_diff_setup(the_repository, &opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 8c4f0cb90a..56d8a77ed7 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -22,7 +22,6 @@ #include "connected.h" #include "strvec.h" #include "version.h" -#include "tag.h" #include "gpg-interface.h" #include "sigchain.h" #include "fsck.h" @@ -142,6 +141,7 @@ static enum deny_action parse_deny_action(const char *var, const char *value) static int receive_pack_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + const char *msg_id; int status = parse_hide_refs_config(var, value, "receive", &hidden_refs); if (status) @@ -178,12 +178,14 @@ static int receive_pack_config(const char *var, const char *value, return 0; } - if (skip_prefix(var, "receive.fsck.", &var)) { - if (is_valid_msg_type(var, value)) + if (skip_prefix(var, "receive.fsck.", &msg_id)) { + if (!value) + return config_error_nonbool(var); + if (is_valid_msg_type(msg_id, value)) strbuf_addf(&fsck_msg_types, "%c%s=%s", - fsck_msg_types.len ? ',' : '=', var, value); + fsck_msg_types.len ? ',' : '=', msg_id, value); else - warning("skipping unknown msg id '%s'", var); + warning("skipping unknown msg id '%s'", msg_id); return 0; } @@ -591,21 +593,6 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) return strbuf_detach(&buf, NULL); } -static char *find_header(const char *msg, size_t len, const char *key, - const char **next_line) -{ - size_t out_len; - const char *val = find_header_mem(msg, len, key, &out_len); - - if (!val) - return NULL; - - if (next_line) - *next_line = val + out_len + 1; - - return xmemdupz(val, out_len); -} - /* * Return zero if a and b are equal up to n bytes and nonzero if they are not. * This operation is guaranteed to run in constant time to avoid leaking data. @@ -620,13 +607,14 @@ static int constant_memequal(const char *a, const char *b, size_t n) return res; } -static const char *check_nonce(const char *buf, size_t len) +static const char *check_nonce(const char *buf) { - char *nonce = find_header(buf, len, "nonce", NULL); + size_t noncelen; + const char *found = find_commit_header(buf, "nonce", &noncelen); + char *nonce = found ? xmemdupz(found, noncelen) : NULL; timestamp_t stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; - size_t noncelen; if (!nonce) { retval = NONCE_MISSING; @@ -668,7 +656,6 @@ static const char *check_nonce(const char *buf, size_t len) goto leave; } - noncelen = strlen(nonce); expect = prepare_push_cert_nonce(service_dir, stamp); if (noncelen != strlen(expect)) { /* This is not even the right size. */ @@ -716,35 +703,28 @@ leave: static int check_cert_push_options(const struct string_list *push_options) { const char *buf = push_cert.buf; - int len = push_cert.len; - char *option; - const char *next_line; + const char *option; + size_t optionlen; int options_seen = 0; int retval = 1; - if (!len) + if (!*buf) return 1; - while ((option = find_header(buf, len, "push-option", &next_line))) { - len -= (next_line - buf); - buf = next_line; + while ((option = find_commit_header(buf, "push-option", &optionlen))) { + buf = option + optionlen + 1; options_seen++; if (options_seen > push_options->nr - || strcmp(option, - push_options->items[options_seen - 1].string)) { - retval = 0; - goto leave; - } - free(option); + || xstrncmpz(push_options->items[options_seen - 1].string, + option, optionlen)) + return 0; } if (options_seen != push_options->nr) retval = 0; -leave: - free(option); return retval; } @@ -771,7 +751,7 @@ static void prepare_push_cert_sha1(struct child_process *proc) check_signature(&sigcheck, push_cert.buf + bogs, push_cert.len - bogs); - nonce_status = check_nonce(push_cert.buf, bogs); + nonce_status = check_nonce(sigcheck.payload); } if (!is_null_oid(&push_cert_oid)) { strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s", @@ -1546,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; + int ret2; old_object = parse_object(the_repository, old_oid); new_object = parse_object(the_repository, new_oid); @@ -1559,7 +1540,10 @@ static const char *update(struct command *cmd, struct shallow_info *si) } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; - if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) { + ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit); + if (ret2 < 0) + exit(128); + if (!ret2) { rp_error("denying non-fast-forward %s" " (you should pull first)", name); ret = "non-fast-forward"; diff --git a/builtin/reflog.c b/builtin/reflog.c index df63a5892e..060eb3377e 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -7,11 +7,15 @@ #include "wildmatch.h" #include "worktree.h" #include "reflog.h" +#include "refs.h" #include "parse-options.h" #define BUILTIN_REFLOG_SHOW_USAGE \ N_("git reflog [show] [<log-options>] [<ref>]") +#define BUILTIN_REFLOG_LIST_USAGE \ + N_("git reflog list") + #define BUILTIN_REFLOG_EXPIRE_USAGE \ N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ " [--rewrite] [--updateref] [--stale-fix]\n" \ @@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = { NULL, }; +static const char *const reflog_list_usage[] = { + BUILTIN_REFLOG_LIST_USAGE, + NULL, +}; + static const char *const reflog_expire_usage[] = { BUILTIN_REFLOG_EXPIRE_USAGE, NULL @@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = { static const char *const reflog_usage[] = { BUILTIN_REFLOG_SHOW_USAGE, + BUILTIN_REFLOG_LIST_USAGE, BUILTIN_REFLOG_EXPIRE_USAGE, BUILTIN_REFLOG_DELETE_USAGE, BUILTIN_REFLOG_EXISTS_USAGE, @@ -60,8 +70,7 @@ struct worktree_reflogs { struct string_list reflogs; }; -static int collect_reflog(const char *ref, const struct object_id *oid UNUSED, - int flags UNUSED, void *cb_data) +static int collect_reflog(const char *ref, void *cb_data) { struct worktree_reflogs *cb = cb_data; struct worktree *worktree = cb->worktree; @@ -96,8 +105,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) reflog_expire_cfg_tail = &reflog_expire_cfg; for (ent = reflog_expire_cfg; ent; ent = ent->next) - if (!strncmp(ent->pattern, pattern, len) && - ent->pattern[len] == '\0') + if (!xstrncmpz(ent->pattern, pattern, len)) return ent; FLEX_ALLOC_MEM(ent, pattern, pattern, len); @@ -239,16 +247,39 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix) return cmd_log_reflog(argc, argv, prefix); } +static int show_reflog(const char *refname, void *cb_data UNUSED) +{ + printf("%s\n", refname); + return 0; +} + +static int cmd_reflog_list(int argc, const char **argv, const char *prefix) +{ + struct option options[] = { + OPT_END() + }; + struct ref_store *ref_store; + + argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0); + if (argc) + return error(_("%s does not accept arguments: '%s'"), + "list", argv[0]); + + ref_store = get_main_ref_store(the_repository); + + return refs_for_each_reflog(ref_store, show_reflog, NULL); +} + static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { struct cmd_reflog_expire_cb cmd = { 0 }; timestamp_t now = time(NULL); - int i, status, do_all, all_worktrees = 1; + int i, status, do_all, single_worktree = 0; unsigned int flags = 0; int verbose = 0; reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent; const struct option options[] = { - OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"), + OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), EXPIRE_REFLOGS_DRY_RUN), OPT_BIT(0, "rewrite", &flags, N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"), @@ -268,7 +299,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stale-fix", &cmd.stalefix, N_("prune any reflog entries that point to broken commits")), OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")), - OPT_BOOL(1, "single-worktree", &all_worktrees, + OPT_BOOL(0, "single-worktree", &single_worktree, N_("limits processing to reflogs from the current worktree only")), OPT_END() }; @@ -298,7 +329,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) struct rev_info revs; repo_init_revisions(the_repository, &revs, prefix); - revs.do_not_die_on_missing_tree = 1; + revs.do_not_die_on_missing_objects = 1; revs.ignore_missing = 1; revs.ignore_missing_links = 1; if (verbose) @@ -318,7 +349,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) worktrees = get_worktrees(); for (p = worktrees; *p; p++) { - if (!all_worktrees && !(*p)->is_current) + if (single_worktree && !(*p)->is_current) continue; collected.worktree = *p; refs_for_each_reflog(get_worktree_ref_store(*p), @@ -368,7 +399,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) int verbose = 0; const struct option options[] = { - OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"), + OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"), EXPIRE_REFLOGS_DRY_RUN), OPT_BIT(0, "rewrite", &flags, N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"), @@ -418,6 +449,7 @@ 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("list", &fn, cmd_reflog_list), OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), diff --git a/builtin/remote.c b/builtin/remote.c index d91bbe728d..8412d12fa5 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -150,7 +150,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) else if (!strcmp(arg, "push")) *mirror = MIRROR_PUSH; else - return error(_("unknown mirror argument: %s"), arg); + return error(_("unknown --mirror argument: %s"), arg); return 0; } diff --git a/builtin/repack.c b/builtin/repack.c index 97051479e4..15e4cccc45 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -8,7 +8,6 @@ #include "path.h" #include "run-command.h" #include "server-info.h" -#include "sigchain.h" #include "strbuf.h" #include "string-list.h" #include "strvec.h" @@ -21,13 +20,14 @@ #include "pack.h" #include "pack-bitmap.h" #include "refs.h" +#include "list-objects-filter-options.h" #define ALL_INTO_ONE 1 #define LOOSEN_UNREACHABLE 2 #define PACK_CRUFT 4 #define DELETE_PACK 1 -#define CRUFT_PACK 2 +#define RETAIN_PACK 2 static int pack_everything; static int delta_base_offset = 1; @@ -52,11 +52,12 @@ struct pack_objects_args { const char *window_memory; const char *depth; const char *threads; - const char *max_pack_size; + unsigned long max_pack_size; int no_reuse_delta; int no_reuse_object; int quiet; int local; + struct list_objects_filter_options filter_options; }; static int repack_config(const char *var, const char *value, @@ -95,14 +96,143 @@ static int repack_config(const char *var, const char *value, return git_default_config(var, value, ctx, cb); } +struct existing_packs { + struct string_list kept_packs; + struct string_list non_kept_packs; + struct string_list cruft_packs; +}; + +#define EXISTING_PACKS_INIT { \ + .kept_packs = STRING_LIST_INIT_DUP, \ + .non_kept_packs = STRING_LIST_INIT_DUP, \ + .cruft_packs = STRING_LIST_INIT_DUP, \ +} + +static int has_existing_non_kept_packs(const struct existing_packs *existing) +{ + return existing->non_kept_packs.nr || existing->cruft_packs.nr; +} + +static void pack_mark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | DELETE_PACK); +} + +static void pack_unmark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK); +} + +static int pack_is_marked_for_deletion(struct string_list_item *item) +{ + return (uintptr_t)item->util & DELETE_PACK; +} + +static void pack_mark_retained(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | RETAIN_PACK); +} + +static int pack_is_retained(struct string_list_item *item) +{ + return (uintptr_t)item->util & RETAIN_PACK; +} + +static void mark_packs_for_deletion_1(struct string_list *names, + struct string_list *list) +{ + struct string_list_item *item; + const int hexsz = the_hash_algo->hexsz; + + for_each_string_list_item(item, list) { + char *sha1; + size_t len = strlen(item->string); + if (len < hexsz) + continue; + sha1 = item->string + len - hexsz; + + if (pack_is_retained(item)) { + pack_unmark_for_deletion(item); + } else if (!string_list_has_string(names, sha1)) { + /* + * Mark this pack for deletion, which ensures + * that this pack won't be included in a MIDX + * (if `--write-midx` was given) and that we + * will actually delete this pack (if `-d` was + * given). + */ + pack_mark_for_deletion(item); + } + } +} + +static void retain_cruft_pack(struct existing_packs *existing, + struct packed_git *cruft) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list_item *item; + + strbuf_addstr(&buf, pack_basename(cruft)); + strbuf_strip_suffix(&buf, ".pack"); + + item = string_list_lookup(&existing->cruft_packs, buf.buf); + if (!item) + BUG("could not find cruft pack '%s'", pack_basename(cruft)); + + pack_mark_retained(item); + strbuf_release(&buf); +} + +static void mark_packs_for_deletion(struct existing_packs *existing, + struct string_list *names) + +{ + mark_packs_for_deletion_1(names, &existing->non_kept_packs); + mark_packs_for_deletion_1(names, &existing->cruft_packs); +} + +static void remove_redundant_pack(const char *dir_name, const char *base_name) +{ + struct strbuf buf = STRBUF_INIT; + struct multi_pack_index *m = get_local_multi_pack_index(the_repository); + strbuf_addf(&buf, "%s.pack", base_name); + if (m && midx_contains_pack(m, buf.buf)) + clear_midx_file(the_repository); + strbuf_insertf(&buf, 0, "%s/", dir_name); + unlink_pack_path(buf.buf, 1); + strbuf_release(&buf); +} + +static void remove_redundant_packs_1(struct string_list *packs) +{ + struct string_list_item *item; + for_each_string_list_item(item, packs) { + if (!pack_is_marked_for_deletion(item)) + continue; + remove_redundant_pack(packdir, item->string); + } +} + +static void remove_redundant_existing_packs(struct existing_packs *existing) +{ + remove_redundant_packs_1(&existing->non_kept_packs); + remove_redundant_packs_1(&existing->cruft_packs); +} + +static void existing_packs_release(struct existing_packs *existing) +{ + string_list_clear(&existing->kept_packs, 0); + string_list_clear(&existing->non_kept_packs, 0); + string_list_clear(&existing->cruft_packs, 0); +} + /* - * Adds all packs hex strings (pack-$HASH) to either fname_nonkept_list - * or fname_kept_list based on whether each pack has a corresponding + * Adds all packs hex strings (pack-$HASH) to either packs->non_kept + * or packs->kept based on whether each pack has a corresponding * .keep file or not. Packs without a .keep file are not to be kept * if we are going to pack everything into one file. */ -static void collect_pack_filenames(struct string_list *fname_nonkept_list, - struct string_list *fname_kept_list, +static void collect_pack_filenames(struct existing_packs *existing, const struct string_list *extra_keep) { struct packed_git *p; @@ -126,28 +256,16 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list, strbuf_strip_suffix(&buf, ".pack"); if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) - string_list_append(fname_kept_list, buf.buf); - else { - struct string_list_item *item; - item = string_list_append(fname_nonkept_list, buf.buf); - if (p->is_cruft) - item->util = (void*)(uintptr_t)CRUFT_PACK; - } + string_list_append(&existing->kept_packs, buf.buf); + else if (p->is_cruft) + string_list_append(&existing->cruft_packs, buf.buf); + else + string_list_append(&existing->non_kept_packs, buf.buf); } - string_list_sort(fname_kept_list); - strbuf_release(&buf); -} - -static void remove_redundant_pack(const char *dir_name, const char *base_name) -{ - struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_local_multi_pack_index(the_repository); - strbuf_addf(&buf, "%s.pack", base_name); - if (m && midx_contains_pack(m, buf.buf)) - clear_midx_file(the_repository); - strbuf_insertf(&buf, 0, "%s/", dir_name); - unlink_pack_path(buf.buf, 1); + string_list_sort(&existing->kept_packs); + string_list_sort(&existing->non_kept_packs); + string_list_sort(&existing->cruft_packs); strbuf_release(&buf); } @@ -165,7 +283,7 @@ static void prepare_pack_objects(struct child_process *cmd, if (args->threads) strvec_pushf(&cmd->args, "--threads=%s", args->threads); if (args->max_pack_size) - strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size); + strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size); if (args->no_reuse_delta) strvec_pushf(&cmd->args, "--no-reuse-delta"); if (args->no_reuse_object) @@ -196,8 +314,9 @@ static int write_oid(const struct object_id *oid, die(_("could not start pack-objects to repack promisor objects")); } - xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz); - xwrite(cmd->in, "\n", 1); + if (write_in_full(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 || + write_in_full(cmd->in, "\n", 1) < 0) + die(_("failed to feed promisor objects to pack-objects")); return 0; } @@ -238,6 +357,18 @@ static struct generated_pack_data *populate_pack_exts(const char *name) return data; } +static int has_pack_ext(const struct generated_pack_data *data, + const char *ext) +{ + int i; + for (i = 0; i < ARRAY_SIZE(exts); i++) { + if (strcmp(exts[i].name, ext)) + continue; + return !!data->tempfiles[i]; + } + BUG("unknown pack extension: '%s'", ext); +} + static void repack_promisor_objects(const struct pack_objects_args *args, struct string_list *names) { @@ -303,6 +434,8 @@ struct pack_geometry { struct packed_git **pack; uint32_t pack_nr, pack_alloc; uint32_t split; + + int split_factor; }; static uint32_t geometry_pack_weight(struct packed_git *p) @@ -324,17 +457,13 @@ static int geometry_cmp(const void *va, const void *vb) return 0; } -static void init_pack_geometry(struct pack_geometry **geometry_p, - struct string_list *existing_kept_packs, +static void init_pack_geometry(struct pack_geometry *geometry, + struct existing_packs *existing, const struct pack_objects_args *args) { struct packed_git *p; - struct pack_geometry *geometry; struct strbuf buf = STRBUF_INIT; - *geometry_p = xcalloc(1, sizeof(struct pack_geometry)); - geometry = *geometry_p; - for (p = get_all_packs(the_repository); p; p = p->next) { if (args->local && !p->pack_local) /* @@ -346,23 +475,24 @@ static void init_pack_geometry(struct pack_geometry **geometry_p, if (!pack_kept_objects) { /* - * Any pack that has its pack_keep bit set will appear - * in existing_kept_packs below, but this saves us from - * doing a more expensive check. + * Any pack that has its pack_keep bit set will + * appear in existing->kept_packs below, but + * this saves us from doing a more expensive + * check. */ if (p->pack_keep) continue; /* - * The pack may be kept via the --keep-pack option; - * check 'existing_kept_packs' to determine whether to - * ignore it. + * The pack may be kept via the --keep-pack + * option; check 'existing->kept_packs' to + * determine whether to ignore it. */ strbuf_reset(&buf); strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); - if (string_list_has_string(existing_kept_packs, buf.buf)) + if (string_list_has_string(&existing->kept_packs, buf.buf)) continue; } if (p->is_cruft) @@ -380,7 +510,7 @@ static void init_pack_geometry(struct pack_geometry **geometry_p, strbuf_release(&buf); } -static void split_pack_geometry(struct pack_geometry *geometry, int factor) +static void split_pack_geometry(struct pack_geometry *geometry) { uint32_t i; uint32_t split; @@ -399,12 +529,14 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) struct packed_git *ours = geometry->pack[i]; struct packed_git *prev = geometry->pack[i - 1]; - if (unsigned_mult_overflows(factor, geometry_pack_weight(prev))) + if (unsigned_mult_overflows(geometry->split_factor, + geometry_pack_weight(prev))) die(_("pack %s too large to consider in geometric " "progression"), prev->pack_name); - if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev)) + if (geometry_pack_weight(ours) < + geometry->split_factor * geometry_pack_weight(prev)) break; } @@ -439,10 +571,12 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) for (i = split; i < geometry->pack_nr; i++) { struct packed_git *ours = geometry->pack[i]; - if (unsigned_mult_overflows(factor, total_size)) + if (unsigned_mult_overflows(geometry->split_factor, + total_size)) die(_("pack %s too large to roll up"), ours->pack_name); - if (geometry_pack_weight(ours) < factor * total_size) { + if (geometry_pack_weight(ours) < + geometry->split_factor * total_size) { if (unsigned_add_overflows(total_size, geometry_pack_weight(ours))) die(_("pack %s too large to roll up"), @@ -492,13 +626,38 @@ static struct packed_git *get_preferred_pack(struct pack_geometry *geometry) return NULL; } +static void geometry_remove_redundant_packs(struct pack_geometry *geometry, + struct string_list *names, + struct existing_packs *existing) +{ + struct strbuf buf = STRBUF_INIT; + uint32_t i; + + for (i = 0; i < geometry->split; i++) { + struct packed_git *p = geometry->pack[i]; + if (string_list_has_string(names, hash_to_hex(p->hash))) + continue; + + strbuf_reset(&buf); + 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); +} + static void free_pack_geometry(struct pack_geometry *geometry) { if (!geometry) return; free(geometry->pack); - free(geometry); } struct midx_snapshot_ref_data { @@ -564,18 +723,17 @@ static void midx_snapshot_refs(struct tempfile *f) } static void midx_included_packs(struct string_list *include, - struct string_list *existing_nonkept_packs, - struct string_list *existing_kept_packs, + struct existing_packs *existing, struct string_list *names, struct pack_geometry *geometry) { struct string_list_item *item; - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) string_list_insert(include, xstrfmt("%s.idx", item->string)); for_each_string_list_item(item, names) string_list_insert(include, xstrfmt("pack-%s.idx", item->string)); - if (geometry) { + if (geometry->split_factor) { struct strbuf buf = STRBUF_INIT; uint32_t i; for (i = geometry->split; i < geometry->pack_nr; i++) { @@ -598,28 +756,37 @@ static void midx_included_packs(struct string_list *include, string_list_insert(include, strbuf_detach(&buf, NULL)); } - - for_each_string_list_item(item, existing_nonkept_packs) { - if (!((uintptr_t)item->util & CRUFT_PACK)) { - /* - * no need to check DELETE_PACK, since we're not - * doing an ALL_INTO_ONE repack - */ - continue; - } - string_list_insert(include, xstrfmt("%s.idx", item->string)); - } } else { - for_each_string_list_item(item, existing_nonkept_packs) { - if ((uintptr_t)item->util & DELETE_PACK) + for_each_string_list_item(item, &existing->non_kept_packs) { + if (pack_is_marked_for_deletion(item)) continue; string_list_insert(include, xstrfmt("%s.idx", item->string)); } } + + for_each_string_list_item(item, &existing->cruft_packs) { + /* + * When doing a --geometric repack, there is no need to check + * for deleted packs, since we're by definition not doing an + * ALL_INTO_ONE repack (hence no packs will be deleted). + * Otherwise we must check for and exclude any packs which are + * enqueued for deletion. + * + * So we could omit the conditional below in the --geometric + * case, but doing so is unnecessary since no packs are marked + * as pending deletion (since we only call + * `mark_packs_for_deletion()` when doing an all-into-one + * repack). + */ + if (pack_is_marked_for_deletion(item)) + continue; + string_list_insert(include, xstrfmt("%s.idx", item->string)); + } } static int write_midx_included_packs(struct string_list *include, struct pack_geometry *geometry, + struct string_list *names, const char *refs_snapshot, int show_progress, int write_bitmaps) { @@ -649,6 +816,38 @@ static int write_midx_included_packs(struct string_list *include, if (preferred) strvec_pushf(&cmd.args, "--preferred-pack=%s", pack_basename(preferred)); + else if (names->nr) { + /* The largest pack was repacked, meaning that either + * one or two packs exist depending on whether the + * repository has a cruft pack or not. + * + * Select the non-cruft one as preferred to encourage + * pack-reuse among packs containing reachable objects + * over unreachable ones. + * + * (Note we could write multiple packs here if + * `--max-pack-size` was given, but any one of them + * will suffice, so pick the first one.) + */ + for_each_string_list_item(item, names) { + struct generated_pack_data *data = item->util; + if (has_pack_ext(data, ".mtimes")) + continue; + + strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack", + item->string); + break; + } + } else { + /* + * No packs were kept, and no packs were written. The + * only thing remaining are .keep packs (unless + * --pack-kept-objects was given). + * + * Set the `--preferred-pack` arbitrarily here. + */ + ; + } if (refs_snapshot) strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot); @@ -694,18 +893,163 @@ static void remove_redundant_bitmaps(struct string_list *include, strbuf_release(&path); } +static int finish_pack_objects_cmd(struct child_process *cmd, + struct string_list *names, + int local) +{ + FILE *out; + struct strbuf line = STRBUF_INIT; + + 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.")); + /* + * 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); + + strbuf_release(&line); + + return finish_command(cmd); +} + +static int write_filtered_pack(const struct pack_objects_args *args, + const char *destination, + const char *pack_prefix, + struct existing_packs *existing, + struct string_list *names) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + struct string_list_item *item; + FILE *in; + int ret; + const char *caret; + const char *scratch; + int local = skip_prefix(destination, packdir, &scratch); + + prepare_pack_objects(&cmd, args, destination); + + strvec_push(&cmd.args, "--stdin-packs"); + + if (!pack_kept_objects) + strvec_push(&cmd.args, "--honor-pack-keep"); + for_each_string_list_item(item, &existing->kept_packs) + strvec_pushf(&cmd.args, "--keep-pack=%s", item->string); + + cmd.in = -1; + + ret = start_command(&cmd); + if (ret) + return ret; + + /* + * Here 'names' contains only the pack(s) that were just + * written, which is exactly the packs we want to keep. Also + * 'existing_kept_packs' already contains the packs in + * 'keep_pack_list'. + */ + in = xfdopen(cmd.in, "w"); + for_each_string_list_item(item, names) + fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string); + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) + fprintf(in, "%s.pack\n", item->string); + caret = pack_kept_objects ? "" : "^"; + for_each_string_list_item(item, &existing->kept_packs) + fprintf(in, "%s%s.pack\n", caret, item->string); + fclose(in); + + return finish_pack_objects_cmd(&cmd, names, local); +} + +static int existing_cruft_pack_cmp(const void *va, const void *vb) +{ + struct packed_git *a = *(struct packed_git **)va; + struct packed_git *b = *(struct packed_git **)vb; + + if (a->pack_size < b->pack_size) + return -1; + if (a->pack_size > b->pack_size) + return 1; + return 0; +} + +static void collapse_small_cruft_packs(FILE *in, size_t max_size, + struct existing_packs *existing) +{ + struct packed_git **existing_cruft, *p; + struct strbuf buf = STRBUF_INIT; + size_t total_size = 0; + size_t existing_cruft_nr = 0; + size_t i; + + ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr); + + for (p = get_all_packs(the_repository); p; p = p->next) { + if (!(p->is_cruft && p->pack_local)) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + if (!string_list_has_string(&existing->cruft_packs, buf.buf)) + continue; + + if (existing_cruft_nr >= existing->cruft_packs.nr) + BUG("too many cruft packs (found %"PRIuMAX", but knew " + "of %"PRIuMAX")", + (uintmax_t)existing_cruft_nr + 1, + (uintmax_t)existing->cruft_packs.nr); + existing_cruft[existing_cruft_nr++] = p; + } + + QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp); + + for (i = 0; i < existing_cruft_nr; i++) { + size_t proposed; + + p = existing_cruft[i]; + proposed = st_add(total_size, p->pack_size); + + if (proposed <= max_size) { + total_size = proposed; + fprintf(in, "-%s\n", pack_basename(p)); + } else { + retain_cruft_pack(existing, p); + fprintf(in, "%s\n", pack_basename(p)); + } + } + + for (i = 0; i < existing->non_kept_packs.nr; i++) + fprintf(in, "-%s.pack\n", + existing->non_kept_packs.items[i].string); + + strbuf_release(&buf); + free(existing_cruft); +} + 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) + struct existing_packs *existing) { struct child_process cmd = CHILD_PROCESS_INIT; - struct strbuf line = STRBUF_INIT; struct string_list_item *item; - FILE *in, *out; + FILE *in; int ret; const char *scratch; int local = skip_prefix(destination, packdir, &scratch); @@ -719,7 +1063,6 @@ static int write_cruft_pack(const struct pack_objects_args *args, strvec_push(&cmd.args, "--honor-pack-keep"); strvec_push(&cmd.args, "--non-empty"); - strvec_push(&cmd.args, "--max-pack-size=0"); cmd.in = -1; @@ -743,33 +1086,30 @@ static int write_cruft_pack(const struct pack_objects_args *args, in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) fprintf(in, "%s-%s.pack\n", pack_prefix, item->string); - for_each_string_list_item(item, existing_packs) - fprintf(in, "-%s.pack\n", item->string); - for_each_string_list_item(item, existing_kept_packs) + if (args->max_pack_size && !cruft_expiration) { + collapse_small_cruft_packs(in, args->max_pack_size, existing); + } else { + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "-%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) + fprintf(in, "-%s.pack\n", item->string); + } + for_each_string_list_item(item, &existing->kept_packs) fprintf(in, "%s.pack\n", item->string); fclose(in); - 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.")); - /* - * 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); - - strbuf_release(&line); + return finish_pack_objects_cmd(&cmd, names, local); +} - return finish_command(&cmd); +static const char *find_pack_prefix(const char *packdir, const char *packtmp) +{ + const char *pack_prefix; + if (!skip_prefix(packtmp, packdir, &pack_prefix)) + die(_("pack prefix %s does not begin with objdir %s"), + packtmp, packdir); + if (*pack_prefix == '/') + pack_prefix++; + return pack_prefix; } int cmd_repack(int argc, const char **argv, const char *prefix) @@ -777,13 +1117,10 @@ 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 existing_nonkept_packs = STRING_LIST_INIT_DUP; - struct string_list existing_kept_packs = STRING_LIST_INIT_DUP; - struct pack_geometry *geometry = NULL; - struct strbuf line = STRBUF_INIT; + struct existing_packs existing = EXISTING_PACKS_INIT; + struct pack_geometry geometry = { 0 }; struct tempfile *refs_snapshot = NULL; int i, ext, ret; - FILE *out; int show_progress; /* variables to be filled by option parsing */ @@ -793,10 +1130,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct pack_objects_args po_args = {NULL}; 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; + const char *filter_to = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -809,6 +1146,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) PACK_CRUFT), OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"), N_("with --cruft, expire objects older than this")), + OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL('d', NULL, &delete_redundant, N_("remove redundant packs, and run git-prune-packed")), OPT_BOOL('f', NULL, &po_args.no_reuse_delta, @@ -836,21 +1175,26 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("limits the maximum delta depth")), OPT_STRING(0, "threads", &po_args.threads, N_("n"), N_("limits the maximum number of threads")), - OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"), + OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size, N_("maximum size of each packfile")), + OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, N_("repack objects in packs marked with .keep")), OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), N_("do not repack this pack")), - OPT_INTEGER('g', "geometric", &geometric_factor, + OPT_INTEGER('g', "geometric", &geometry.split_factor, 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_STRING(0, "filter-to", &filter_to, N_("dir"), + N_("pack prefix to store a pack containing filtered out objects")), OPT_END() }; + list_objects_filter_init(&po_args.filter_options); + git_config(repack_config, &cruft_po_args); argc = parse_options(argc, argv, prefix, builtin_repack_options, @@ -859,19 +1203,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant && repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); - if (keep_unreachable && - (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))) - die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A"); + die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A", + keep_unreachable, "-k/--keep-unreachable", + pack_everything & PACK_CRUFT, "--cruft"); - if (pack_everything & PACK_CRUFT) { + if (pack_everything & PACK_CRUFT) pack_everything |= ALL_INTO_ONE; - if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)) - die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A"); - if (keep_unreachable) - die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k"); - } - if (write_bitmaps < 0) { if (!write_midx && (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository())) @@ -915,14 +1253,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid()); packtmp = mkpathdup("%s/%s", packdir, packtmp_name); - collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs, - &keep_pack_list); + collect_pack_filenames(&existing, &keep_pack_list); - if (geometric_factor) { + if (geometry.split_factor) { if (pack_everything) die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); - init_pack_geometry(&geometry, &existing_kept_packs, &po_args); - split_pack_geometry(geometry, geometric_factor); + init_pack_geometry(&geometry, &existing, &po_args); + split_pack_geometry(&geometry); } prepare_pack_objects(&cmd, &po_args, packtmp); @@ -936,7 +1273,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--keep-pack=%s", keep_pack_list.items[i].string); strvec_push(&cmd.args, "--non-empty"); - if (!geometry) { + if (!geometry.split_factor) { /* * We need to grab all reachable objects, including those that * are reachable from reflogs and the index. @@ -965,7 +1302,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (pack_everything & ALL_INTO_ONE) { repack_promisor_objects(&po_args, &names); - if (existing_nonkept_packs.nr && delete_redundant && + if (has_existing_non_kept_packs(&existing) && + delete_redundant && !(pack_everything & PACK_CRUFT)) { for_each_string_list_item(item, &names) { strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", @@ -983,7 +1321,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--pack-loose-unreachable"); } } - } else if (geometry) { + } else if (geometry.split_factor) { strvec_push(&cmd.args, "--stdin-packs"); strvec_push(&cmd.args, "--unpacked"); } else { @@ -991,7 +1329,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--incremental"); } - if (geometry) + if (po_args.filter_options.choice) + strvec_pushf(&cmd.args, "--filter=%s", + expand_list_objects_filter_spec(&po_args.filter_options)); + else if (filter_to) + die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter"); + + if (geometry.split_factor) cmd.in = -1; else cmd.no_stdin = 1; @@ -1000,32 +1344,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (ret) goto cleanup; - if (geometry) { + if (geometry.split_factor) { FILE *in = xfdopen(cmd.in, "w"); /* * The resulting pack should contain all objects in packs that * are going to be rolled up, but exclude objects in packs which * are being left alone. */ - for (i = 0; i < geometry->split; i++) - fprintf(in, "%s\n", pack_basename(geometry->pack[i])); - for (i = geometry->split; i < geometry->pack_nr; i++) - fprintf(in, "^%s\n", pack_basename(geometry->pack[i])); + for (i = 0; i < geometry.split; i++) + fprintf(in, "%s\n", pack_basename(geometry.pack[i])); + for (i = geometry.split; i < geometry.pack_nr; i++) + fprintf(in, "^%s\n", pack_basename(geometry.pack[i])); fclose(in); } - 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.")); - item = string_list_append(&names, line.buf); - item->util = populate_pack_exts(item->string); - } - strbuf_release(&line); - fclose(out); - ret = finish_command(&cmd); + ret = finish_pack_objects_cmd(&cmd, &names, 1); if (ret) goto cleanup; @@ -1033,12 +1366,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) printf_ln(_("Nothing new to pack.")); if (pack_everything & PACK_CRUFT) { - const char *pack_prefix; - if (!skip_prefix(packtmp, packdir, &pack_prefix)) - die(_("pack prefix %s does not begin with objdir %s"), - packtmp, packdir); - if (*pack_prefix == '/') - pack_prefix++; + const char *pack_prefix = find_pack_prefix(packdir, packtmp); if (!cruft_po_args.window) cruft_po_args.window = po_args.window; @@ -1048,14 +1376,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cruft_po_args.depth = po_args.depth; if (!cruft_po_args.threads) cruft_po_args.threads = po_args.threads; + if (!cruft_po_args.max_pack_size) + cruft_po_args.max_pack_size = po_args.max_pack_size; cruft_po_args.local = po_args.local; cruft_po_args.quiet = po_args.quiet; ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, cruft_expiration, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; @@ -1086,13 +1415,25 @@ int cmd_repack(int argc, const char **argv, const char *prefix) pack_prefix, NULL, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; } } + if (po_args.filter_options.choice) { + if (!filter_to) + filter_to = packtmp; + + ret = write_filtered_pack(&po_args, + filter_to, + find_pack_prefix(packdir, packtmp), + &existing, + &names); + if (ret) + goto cleanup; + } + string_list_sort(&names); close_object_store(the_repository->objects); @@ -1131,31 +1472,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } /* End of pack replacement. */ - if (delete_redundant && pack_everything & ALL_INTO_ONE) { - const int hexsz = the_hash_algo->hexsz; - for_each_string_list_item(item, &existing_nonkept_packs) { - char *sha1; - size_t len = strlen(item->string); - if (len < hexsz) - continue; - sha1 = item->string + len - hexsz; - /* - * Mark this pack for deletion, which ensures that this - * pack won't be included in a MIDX (if `--write-midx` - * was given) and that we will actually delete this pack - * (if `-d` was given). - */ - if (!string_list_has_string(&names, sha1)) - item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK); - } - } + if (delete_redundant && pack_everything & ALL_INTO_ONE) + mark_packs_for_deletion(&existing, &names); if (write_midx) { struct string_list include = STRING_LIST_INIT_NODUP; - midx_included_packs(&include, &existing_nonkept_packs, - &existing_kept_packs, &names, geometry); + midx_included_packs(&include, &existing, &names, &geometry); - ret = write_midx_included_packs(&include, geometry, + ret = write_midx_included_packs(&include, &geometry, &names, refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, show_progress, write_bitmaps > 0); @@ -1172,35 +1496,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant) { int opts = 0; - for_each_string_list_item(item, &existing_nonkept_packs) { - if (!((uintptr_t)item->util & DELETE_PACK)) - continue; - remove_redundant_pack(packdir, item->string); - } - - if (geometry) { - struct strbuf buf = STRBUF_INIT; - - uint32_t i; - for (i = 0; i < geometry->split; i++) { - struct packed_git *p = geometry->pack[i]; - if (string_list_has_string(&names, - hash_to_hex(p->hash))) - continue; + remove_redundant_existing_packs(&existing); - strbuf_reset(&buf); - 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); - } + if (geometry.split_factor) + geometry_remove_redundant_packs(&geometry, &names, + &existing); if (show_progress) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); @@ -1224,9 +1524,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cleanup: string_list_clear(&names, 1); - string_list_clear(&existing_nonkept_packs, 0); - string_list_clear(&existing_kept_packs, 0); - free_pack_geometry(geometry); + existing_packs_release(&existing); + free_pack_geometry(&geometry); + list_objects_filter_release(&po_args.filter_options); return ret; } diff --git a/builtin/replay.c b/builtin/replay.c new file mode 100644 index 0000000000..6bc4b47f09 --- /dev/null +++ b/builtin/replay.c @@ -0,0 +1,446 @@ +/* + * "git replay" builtin command + */ + +#define USE_THE_INDEX_VARIABLE +#include "git-compat-util.h" + +#include "builtin.h" +#include "environment.h" +#include "hex.h" +#include "lockfile.h" +#include "merge-ort.h" +#include "object-name.h" +#include "parse-options.h" +#include "refs.h" +#include "revision.h" +#include "strmap.h" +#include <oidset.h> +#include <tree.h> + +static const char *short_commit_name(struct commit *commit) +{ + return repo_find_unique_abbrev(the_repository, &commit->object.oid, + DEFAULT_ABBREV); +} + +static struct commit *peel_committish(const char *name) +{ + struct object *obj; + struct object_id oid; + + if (repo_get_oid(the_repository, name, &oid)) + return NULL; + obj = parse_object(the_repository, &oid); + return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj, + OBJ_COMMIT); +} + +static char *get_author(const char *message) +{ + size_t len; + const char *a; + + a = find_commit_header(message, "author", &len); + if (a) + return xmemdupz(a, len); + + return NULL; +} + +static struct commit *create_commit(struct tree *tree, + struct commit *based_on, + struct commit *parent) +{ + struct object_id ret; + struct object *obj; + struct commit_list *parents = NULL; + char *author; + char *sign_commit = NULL; /* FIXME: cli users might want to sign again */ + struct commit_extra_header *extra; + struct strbuf msg = STRBUF_INIT; + const char *out_enc = get_commit_output_encoding(); + const char *message = repo_logmsg_reencode(the_repository, based_on, + NULL, out_enc); + const char *orig_message = NULL; + const char *exclude_gpgsig[] = { "gpgsig", NULL }; + + commit_list_insert(parent, &parents); + extra = read_commit_extra_headers(based_on, exclude_gpgsig); + find_commit_subject(message, &orig_message); + strbuf_addstr(&msg, orig_message); + author = get_author(message); + reset_ident_date(); + if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, + &ret, author, NULL, sign_commit, extra)) { + error(_("failed to write commit object")); + return NULL; + } + free(author); + strbuf_release(&msg); + + obj = parse_object(the_repository, &ret); + return (struct commit *)obj; +} + +struct ref_info { + struct commit *onto; + struct strset positive_refs; + struct strset negative_refs; + int positive_refexprs; + int negative_refexprs; +}; + +static void get_ref_information(struct rev_cmdline_info *cmd_info, + struct ref_info *ref_info) +{ + int i; + + ref_info->onto = NULL; + strset_init(&ref_info->positive_refs); + strset_init(&ref_info->negative_refs); + ref_info->positive_refexprs = 0; + ref_info->negative_refexprs = 0; + + /* + * When the user specifies e.g. + * git replay origin/main..mybranch + * git replay ^origin/next mybranch1 mybranch2 + * we want to be able to determine where to replay the commits. In + * these examples, the branches are probably based on an old version + * of either origin/main or origin/next, so we want to replay on the + * newest version of that branch. In contrast we would want to error + * out if they ran + * git replay ^origin/master ^origin/next mybranch + * git replay mybranch~2..mybranch + * the first of those because there's no unique base to choose, and + * the second because they'd likely just be replaying commits on top + * of the same commit and not making any difference. + */ + for (i = 0; i < cmd_info->nr; i++) { + struct rev_cmdline_entry *e = cmd_info->rev + i; + struct object_id oid; + const char *refexpr = e->name; + char *fullname = NULL; + int can_uniquely_dwim = 1; + + if (*refexpr == '^') + refexpr++; + if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1) + can_uniquely_dwim = 0; + + if (e->flags & BOTTOM) { + if (can_uniquely_dwim) + strset_add(&ref_info->negative_refs, fullname); + if (!ref_info->negative_refexprs) + ref_info->onto = lookup_commit_reference_gently(the_repository, + &e->item->oid, 1); + ref_info->negative_refexprs++; + } else { + if (can_uniquely_dwim) + strset_add(&ref_info->positive_refs, fullname); + ref_info->positive_refexprs++; + } + + free(fullname); + } +} + +static void determine_replay_mode(struct rev_cmdline_info *cmd_info, + const char *onto_name, + const char **advance_name, + struct commit **onto, + struct strset **update_refs) +{ + struct ref_info rinfo; + + get_ref_information(cmd_info, &rinfo); + if (!rinfo.positive_refexprs) + die(_("need some commits to replay")); + if (onto_name && *advance_name) + die(_("--onto and --advance are incompatible")); + else if (onto_name) { + *onto = peel_committish(onto_name); + if (rinfo.positive_refexprs < + strset_get_size(&rinfo.positive_refs)) + die(_("all positive revisions given must be references")); + } else if (*advance_name) { + struct object_id oid; + char *fullname = NULL; + + *onto = peel_committish(*advance_name); + if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name), + &oid, &fullname, 0) == 1) { + *advance_name = fullname; + } else { + die(_("argument to --advance must be a reference")); + } + if (rinfo.positive_refexprs > 1) + die(_("cannot advance target with multiple sources because ordering would be ill-defined")); + } else { + int positive_refs_complete = ( + rinfo.positive_refexprs == + strset_get_size(&rinfo.positive_refs)); + int negative_refs_complete = ( + rinfo.negative_refexprs == + strset_get_size(&rinfo.negative_refs)); + /* + * We need either positive_refs_complete or + * negative_refs_complete, but not both. + */ + if (rinfo.negative_refexprs > 0 && + positive_refs_complete == negative_refs_complete) + die(_("cannot implicitly determine whether this is an --advance or --onto operation")); + if (negative_refs_complete) { + struct hashmap_iter iter; + struct strmap_entry *entry; + + if (rinfo.negative_refexprs == 0) + die(_("all positive revisions given must be references")); + else if (rinfo.negative_refexprs > 1) + die(_("cannot implicitly determine whether this is an --advance or --onto operation")); + else if (rinfo.positive_refexprs > 1) + die(_("cannot advance target with multiple source branches because ordering would be ill-defined")); + + /* Only one entry, but we have to loop to get it */ + strset_for_each_entry(&rinfo.negative_refs, + &iter, entry) { + *advance_name = entry->key; + } + } else { /* positive_refs_complete */ + if (rinfo.negative_refexprs > 1) + die(_("cannot implicitly determine correct base for --onto")); + if (rinfo.negative_refexprs == 1) + *onto = rinfo.onto; + } + } + if (!*advance_name) { + *update_refs = xcalloc(1, sizeof(**update_refs)); + **update_refs = rinfo.positive_refs; + memset(&rinfo.positive_refs, 0, sizeof(**update_refs)); + } + strset_clear(&rinfo.negative_refs); + strset_clear(&rinfo.positive_refs); +} + +static struct commit *mapped_commit(kh_oid_map_t *replayed_commits, + struct commit *commit, + struct commit *fallback) +{ + khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid); + if (pos == kh_end(replayed_commits)) + return fallback; + return kh_value(replayed_commits, pos); +} + +static struct commit *pick_regular_commit(struct commit *pickme, + kh_oid_map_t *replayed_commits, + struct commit *onto, + struct merge_options *merge_opt, + struct merge_result *result) +{ + struct commit *base, *replayed_base; + struct tree *pickme_tree, *base_tree; + + base = pickme->parents->item; + replayed_base = mapped_commit(replayed_commits, base, onto); + + result->tree = repo_get_commit_tree(the_repository, replayed_base); + pickme_tree = repo_get_commit_tree(the_repository, pickme); + base_tree = repo_get_commit_tree(the_repository, base); + + merge_opt->branch1 = short_commit_name(replayed_base); + merge_opt->branch2 = short_commit_name(pickme); + merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2); + + merge_incore_nonrecursive(merge_opt, + base_tree, + result->tree, + pickme_tree, + result); + + free((char*)merge_opt->ancestor); + merge_opt->ancestor = NULL; + if (!result->clean) + return NULL; + return create_commit(result->tree, pickme, replayed_base); +} + +int cmd_replay(int argc, const char **argv, const char *prefix) +{ + const char *advance_name = NULL; + struct commit *onto = NULL; + const char *onto_name = NULL; + int contained = 0; + + struct rev_info revs; + struct commit *last_commit = NULL; + struct commit *commit; + struct merge_options merge_opt; + struct merge_result result; + struct strset *update_refs = NULL; + kh_oid_map_t *replayed_commits; + int ret = 0; + + const char * const replay_usage[] = { + N_("(EXPERIMENTAL!) git replay " + "([--contained] --onto <newbase> | --advance <branch>) " + "<revision-range>..."), + NULL + }; + struct option replay_options[] = { + OPT_STRING(0, "advance", &advance_name, + N_("branch"), + N_("make replay advance given branch")), + OPT_STRING(0, "onto", &onto_name, + N_("revision"), + N_("replay onto given commit")), + OPT_BOOL(0, "contained", &contained, + N_("advance all branches contained in revision-range")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, replay_options, replay_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); + + if (!onto_name && !advance_name) { + error(_("option --onto or --advance is mandatory")); + usage_with_options(replay_usage, replay_options); + } + + if (advance_name && contained) + die(_("options '%s' and '%s' cannot be used together"), + "--advance", "--contained"); + + repo_init_revisions(the_repository, &revs, prefix); + + /* + * Set desired values for rev walking options here. If they + * are changed by some user specified option in setup_revisions() + * below, we will detect that below and then warn. + * + * TODO: In the future we might want to either die(), or allow + * some options changing these values if we think they could + * be useful. + */ + revs.reverse = 1; + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + revs.topo_order = 1; + revs.simplify_history = 0; + + argc = setup_revisions(argc, argv, &revs, NULL); + if (argc > 1) { + ret = error(_("unrecognized argument: %s"), argv[1]); + goto cleanup; + } + + /* + * Detect and warn if we override some user specified rev + * walking options. + */ + if (revs.reverse != 1) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "reverse"); + revs.reverse = 1; + } + if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "sort_order"); + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + } + if (revs.topo_order != 1) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "topo_order"); + revs.topo_order = 1; + } + if (revs.simplify_history != 0) { + warning(_("some rev walking options will be overridden as " + "'%s' bit in 'struct rev_info' will be forced"), + "simplify_history"); + revs.simplify_history = 0; + } + + determine_replay_mode(&revs.cmdline, onto_name, &advance_name, + &onto, &update_refs); + + if (!onto) /* FIXME: Should handle replaying down to root commit */ + die("Replaying down to root commit is not supported yet!"); + + if (prepare_revision_walk(&revs) < 0) { + ret = error(_("error preparing revisions")); + goto cleanup; + } + + init_merge_options(&merge_opt, the_repository); + memset(&result, 0, sizeof(result)); + merge_opt.show_rename_progress = 0; + last_commit = onto; + replayed_commits = kh_init_oid_map(); + while ((commit = get_revision(&revs))) { + const struct name_decoration *decoration; + khint_t pos; + int hr; + + if (!commit->parents) + die(_("replaying down to root commit is not supported yet!")); + if (commit->parents->next) + die(_("replaying merge commits is not supported yet!")); + + last_commit = pick_regular_commit(commit, replayed_commits, onto, + &merge_opt, &result); + if (!last_commit) + break; + + /* Record commit -> last_commit mapping */ + pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr); + if (hr == 0) + BUG("Duplicate rewritten commit: %s\n", + oid_to_hex(&commit->object.oid)); + kh_value(replayed_commits, pos) = last_commit; + + /* Update any necessary branches */ + if (advance_name) + continue; + decoration = get_name_decoration(&commit->object); + if (!decoration) + continue; + while (decoration) { + if (decoration->type == DECORATION_REF_LOCAL && + (contained || strset_contains(update_refs, + decoration->name))) { + printf("update %s %s %s\n", + decoration->name, + oid_to_hex(&last_commit->object.oid), + oid_to_hex(&commit->object.oid)); + } + decoration = decoration->next; + } + } + + /* In --advance mode, advance the target ref */ + if (result.clean == 1 && advance_name) { + printf("update %s %s %s\n", + advance_name, + oid_to_hex(&last_commit->object.oid), + oid_to_hex(&onto->object.oid)); + } + + merge_finalize(&merge_opt, &result); + kh_destroy_oid_map(replayed_commits); + if (update_refs) { + strset_clear(update_refs); + free(update_refs); + } + ret = result.clean; + +cleanup: + release_revisions(&revs); + + /* Return */ + if (ret < 0) + exit(128); + return ret ? 0 : 1; +} diff --git a/builtin/rerere.c b/builtin/rerere.c index 07a9d37275..b2efc6f640 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -1,6 +1,5 @@ #include "builtin.h" #include "config.h" -#include "dir.h" #include "gettext.h" #include "parse-options.h" #include "repository.h" diff --git a/builtin/reset.c b/builtin/reset.c index 4b018d20e3..1d62ff6332 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -16,10 +16,8 @@ #include "hash.h" #include "hex.h" #include "lockfile.h" -#include "tag.h" #include "object.h" #include "pretty.h" -#include "run-command.h" #include "refs.h" #include "diff.h" #include "diffcore.h" @@ -33,7 +31,6 @@ #include "setup.h" #include "sparse-index.h" #include "submodule.h" -#include "submodule-config.h" #include "trace.h" #include "trace2.h" #include "dir.h" @@ -119,6 +116,10 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t if (reset_type == MIXED || reset_type == HARD) { tree = parse_tree_indirect(oid); + if (!tree) { + error(_("unable to read tree (%s)"), oid_to_hex(oid)); + goto out; + } prime_cache_tree(the_repository, the_repository->index, tree); } @@ -284,7 +285,9 @@ static void parse_args(struct pathspec *pathspec, verify_filename(prefix, argv[0], 1); } } - *rev_ret = rev; + + /* treat '@' as a shortcut for 'HEAD' */ + *rev_ret = !strcmp("@", rev) ? "HEAD" : rev; parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL | diff --git a/builtin/rev-list.c b/builtin/rev-list.c index ff715d6918..77803727e0 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -7,13 +7,11 @@ #include "hex.h" #include "revision.h" #include "list-objects.h" -#include "list-objects-filter.h" #include "list-objects-filter-options.h" #include "object.h" #include "object-name.h" #include "object-file.h" #include "object-store-ll.h" -#include "pack.h" #include "pack-bitmap.h" #include "log-tree.h" #include "graph.h" @@ -100,7 +98,48 @@ static off_t get_object_disk_usage(struct object *obj) return size; } -static void finish_commit(struct commit *commit); +static inline void finish_object__ma(struct object *obj) +{ + /* + * Whether or not we try to dynamically fetch missing objects + * from the server, we currently DO NOT have the object. We + * can either print, allow (ignore), or conditionally allow + * (ignore) them. + */ + switch (arg_missing_action) { + case MA_ERROR: + die("missing %s object '%s'", + type_name(obj->type), oid_to_hex(&obj->oid)); + return; + + case MA_ALLOW_ANY: + return; + + case MA_PRINT: + oidset_insert(&missing_objects, &obj->oid); + return; + + case MA_ALLOW_PROMISOR: + if (is_promisor_object(&obj->oid)) + return; + die("unexpected missing %s object '%s'", + type_name(obj->type), oid_to_hex(&obj->oid)); + return; + + default: + BUG("unhandled missing_action"); + return; + } +} + +static void finish_commit(struct commit *commit) +{ + free_commit_list(commit->parents); + commit->parents = NULL; + free_commit_buffer(the_repository->parsed_objects, + commit); +} + static void show_commit(struct commit *commit, void *data) { struct rev_list_info *info = data; @@ -108,6 +147,12 @@ static void show_commit(struct commit *commit, void *data) display_progress(progress, ++progress_counter); + if (revs->do_not_die_on_missing_objects && + oidset_contains(&revs->missing_commits, &commit->object.oid)) { + finish_object__ma(&commit->object); + return; + } + if (show_disk_usage) total_disk_usage += get_object_disk_usage(&commit->object); @@ -174,6 +219,7 @@ static void show_commit(struct commit *commit, void *data) ctx.fmt = revs->commit_format; ctx.output_encoding = get_log_output_encoding(); ctx.color = revs->diffopt.use_color; + ctx.rev = revs; pretty_print_commit(&ctx, commit, &buf); if (buf.len) { if (revs->commit_format != CMIT_FMT_ONELINE) @@ -219,48 +265,6 @@ static void show_commit(struct commit *commit, void *data) finish_commit(commit); } -static void finish_commit(struct commit *commit) -{ - free_commit_list(commit->parents); - commit->parents = NULL; - free_commit_buffer(the_repository->parsed_objects, - commit); -} - -static inline void finish_object__ma(struct object *obj) -{ - /* - * Whether or not we try to dynamically fetch missing objects - * from the server, we currently DO NOT have the object. We - * can either print, allow (ignore), or conditionally allow - * (ignore) them. - */ - switch (arg_missing_action) { - case MA_ERROR: - die("missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - case MA_ALLOW_ANY: - return; - - case MA_PRINT: - oidset_insert(&missing_objects, &obj->oid); - return; - - case MA_ALLOW_PROMISOR: - if (is_promisor_object(&obj->oid)) - return; - die("unexpected missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - default: - BUG("unhandled missing_action"); - return; - } -} - static int finish_object(struct object *obj, const char *name UNUSED, void *cb_data) { @@ -542,6 +546,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) * * Let "--missing" to conditionally set fetch_if_missing. */ + /* + * NEEDSWORK: These loops that attempt to find presence of + * options without understanding that the options they are + * skipping are broken (e.g., it would not know "--grep + * --exclude-promisor-objects" is not triggering + * "--exclude-promisor-objects" option). We really need + * setup_revisions() to have a mechanism to allow and disallow + * some sets of options for different commands (like rev-list, + * replay, etc). Such a mechanism should do an early parsing + * of options and be able to manage the `--missing=...` and + * `--exclude-promisor-objects` options below. + */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--exclude-promisor-objects")) { @@ -561,7 +577,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } if (arg_missing_action) - revs.do_not_die_on_missing_tree = 1; + revs.do_not_die_on_missing_objects = 1; argc = setup_revisions(argc, argv, &revs, &s_r_opt); @@ -750,8 +766,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (arg_print_omitted) oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE); - if (arg_missing_action == MA_PRINT) + if (arg_missing_action == MA_PRINT) { oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE); + /* Add missing tips */ + oidset_insert_from_set(&missing_objects, &revs.missing_commits); + oidset_clear(&revs.missing_commits); + } traverse_commit_list_filtered( &revs, show_commit, show_object, &info, diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index fde8861ca4..624182e507 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -25,6 +25,7 @@ #include "submodule.h" #include "commit-reach.h" #include "shallow.h" +#include "object-file-convert.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -297,7 +298,7 @@ static int try_difference(const char *arg) show_rev(NORMAL, &end_oid, end); show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); if (symmetric) { - struct commit_list *exclude; + struct commit_list *exclude = NULL; struct commit *a, *b; a = lookup_commit_reference(the_repository, &start_oid); b = lookup_commit_reference(the_repository, &end_oid); @@ -305,7 +306,8 @@ static int try_difference(const char *arg) *dotdot = '.'; return 0; } - exclude = repo_get_merge_bases(the_repository, a, b); + if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) + exit(128); while (exclude) { struct commit *commit = pop_commit(&exclude); show_rev(REVERSED, &commit->object.oid, NULL); @@ -675,6 +677,8 @@ static void print_path(const char *path, const char *prefix, enum format_type fo int cmd_rev_parse(int argc, const char **argv, const char *prefix) { int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; + const struct git_hash_algo *output_algo = NULL; + const struct git_hash_algo *compat = NULL; int did_repo_setup = 0; int has_dashdash = 0; int output_prefix = 0; @@ -746,6 +750,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; + compat = the_repository->compat_hash_algo; } if (!strcmp(arg, "--")) { @@ -833,6 +838,22 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) flags |= GET_OID_QUIETLY; continue; } + if (opt_with_value(arg, "--output-object-format", &arg)) { + if (!arg) + die(_("no object format specified")); + if (!strcmp(arg, the_hash_algo->name) || + !strcmp(arg, "storage")) { + flags |= GET_OID_HASH_ANY; + output_algo = the_hash_algo; + continue; + } + else if (compat && !strcmp(arg, compat->name)) { + flags |= GET_OID_HASH_ANY; + output_algo = compat; + continue; + } + else die(_("unsupported object format: %s"), arg); + } if (opt_with_value(arg, "--short", &arg)) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; @@ -882,7 +903,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (skip_prefix(arg, "--disambiguate=", &arg)) { - repo_for_each_abbrev(the_repository, arg, + repo_for_each_abbrev(the_repository, arg, the_hash_algo, show_abbrev, NULL); continue; } @@ -893,13 +914,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (opt_with_value(arg, "--branches", &arg)) { if (ref_excludes.hidden_refs_configured) - return error(_("--exclude-hidden cannot be used together with --branches")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--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")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--tags"); handle_ref_opt(arg, "refs/tags/"); continue; } @@ -909,7 +932,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (opt_with_value(arg, "--remotes", &arg)) { if (ref_excludes.hidden_refs_configured) - return error(_("--exclude-hidden cannot be used together with --remotes")); + return error(_("options '%s' and '%s' cannot be used together"), + "--exclude-hidden", "--remotes"); handle_ref_opt(arg, "refs/remotes/"); continue; } @@ -1059,6 +1083,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) puts(the_hash_algo->name); continue; } + if (!strcmp(arg, "--show-ref-format")) { + puts(ref_storage_format_to_name(the_repository->ref_storage_format)); + continue; + } if (!strcmp(arg, "--end-of-options")) { seen_end_of_options = 1; if (filter & (DO_FLAGS | DO_REVS)) @@ -1083,6 +1111,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!get_oid_with_context(the_repository, name, flags, &oid, &unused)) { + if (output_algo) + repo_oid_to_algop(the_repository, &oid, + output_algo, &oid); if (verify) revs_count++; else diff --git a/builtin/revert.c b/builtin/revert.c index e6f9a1ad26..89821bab95 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -1,5 +1,4 @@ #include "git-compat-util.h" -#include "config.h" #include "builtin.h" #include "parse-options.h" #include "diff.h" @@ -7,7 +6,6 @@ #include "repository.h" #include "revision.h" #include "rerere.h" -#include "dir.h" #include "sequencer.h" #include "branch.h" diff --git a/builtin/rm.c b/builtin/rm.c index dff819ae50..fd130cea2d 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -9,7 +9,6 @@ #include "config.h" #include "lockfile.h" #include "dir.h" -#include "cache-tree.h" #include "gettext.h" #include "hash.h" #include "tree-walk.h" diff --git a/builtin/send-pack.c b/builtin/send-pack.c index cd6d9e4112..3df9eaad09 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,19 +1,14 @@ #include "builtin.h" #include "config.h" -#include "commit.h" #include "hex.h" -#include "refs.h" #include "pkt-line.h" -#include "sideband.h" #include "run-command.h" #include "remote.h" #include "connect.h" #include "send-pack.h" #include "quote.h" #include "transport.h" -#include "version.h" #include "oid-array.h" -#include "gpg-interface.h" #include "gettext.h" #include "protocol.h" #include "parse-options.h" @@ -135,21 +130,18 @@ static int send_pack_config(const char *k, const char *v, const struct config_context *ctx, void *cb) { if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - args.push_cert = SEND_PACK_PUSH_CERT_NEVER; - break; - case 1: - args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS; - break; - default: - if (value && !strcasecmp(value, "if-asked")) - args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED; - else - return error(_("invalid value for '%s'"), k); - } + switch (git_parse_maybe_bool(v)) { + case 0: + args.push_cert = SEND_PACK_PUSH_CERT_NEVER; + break; + case 1: + args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS; + break; + default: + if (!strcasecmp(v, "if-asked")) + args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED; + else + return error(_("invalid value for '%s'"), k); } } return git_default_config(k, v, ctx, cb); @@ -208,7 +200,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")), OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")), OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")), - OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), + OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"), N_("require old value of ref to be at this value"), PARSE_OPT_OPTARG, parseopt_push_cas_option), OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes, @@ -341,6 +333,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) } if (!ret && !transport_refs_pushed(remote_refs)) + /* stable plumbing output; do not modify or localize */ fprintf(stderr, "Everything up-to-date\n"); return ret; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 1307ed2b88..3c7cd2d6ef 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -245,7 +245,6 @@ 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 = log->date_mode; ctx.output_encoding = get_log_output_encoding(); diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 5110814f79..1c15421e60 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -2,28 +2,34 @@ #include "config.h" #include "gettext.h" #include "hex.h" -#include "refs.h" +#include "refs/refs-internal.h" #include "object-name.h" #include "object-store-ll.h" #include "object.h" -#include "tag.h" #include "string-list.h" #include "parse-options.h" static const char * const show_ref_usage[] = { - N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference]\n" + N_("git show-ref [--head] [-d | --dereference]\n" " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags]\n" " [--heads] [--] [<pattern>...]"), + N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n" + " [-s | --hash[=<n>]] [--abbrev[=<n>]]\n" + " [--] [<ref>...]"), N_("git show-ref --exclude-existing[=<pattern>]"), + N_("git show-ref --exists <ref>"), NULL }; -static int deref_tags, show_head, tags_only, heads_only, found_match, verify, - quiet, hash_only, abbrev, exclude_arg; -static const char **pattern; -static const char *exclude_existing_arg; +struct show_one_options { + int quiet; + int hash_only; + int abbrev; + int deref_tags; +}; -static void show_one(const char *refname, const struct object_id *oid) +static void show_one(const struct show_one_options *opts, + const char *refname, const struct object_id *oid) { const char *hex; struct object_id peeled; @@ -32,33 +38,42 @@ static void show_one(const char *refname, const struct object_id *oid) die("git show-ref: bad ref %s (%s)", refname, oid_to_hex(oid)); - if (quiet) + if (opts->quiet) return; - hex = repo_find_unique_abbrev(the_repository, oid, abbrev); - if (hash_only) + hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev); + if (opts->hash_only) printf("%s\n", hex); else printf("%s %s\n", hex, refname); - if (!deref_tags) + if (!opts->deref_tags) return; if (!peel_iterated_oid(oid, &peeled)) { - hex = repo_find_unique_abbrev(the_repository, &peeled, abbrev); + hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev); printf("%s %s^{}\n", hex, refname); } } +struct show_ref_data { + const struct show_one_options *show_one_opts; + const char **patterns; + int found_match; + int show_head; +}; + static int show_ref(const char *refname, const struct object_id *oid, - int flag UNUSED, void *cbdata UNUSED) + int flag UNUSED, void *cbdata) { - if (show_head && !strcmp(refname, "HEAD")) + struct show_ref_data *data = cbdata; + + if (data->show_head && !strcmp(refname, "HEAD")) goto match; - if (pattern) { + if (data->patterns) { int reflen = strlen(refname); - const char **p = pattern, *m; + const char **p = data->patterns, *m; while ((m = *p++) != NULL) { int len = strlen(m); if (len > reflen) @@ -74,9 +89,9 @@ static int show_ref(const char *refname, const struct object_id *oid, } match: - found_match++; + data->found_match++; - show_one(refname, oid); + show_one(data->show_one_opts, refname, oid); return 0; } @@ -90,6 +105,15 @@ static int add_existing(const char *refname, return 0; } +struct exclude_existing_options { + /* + * We need an explicit `enabled` field because it is perfectly valid + * for `pattern` to be `NULL` even if `--exclude-existing` was given. + */ + int enabled; + const char *pattern; +}; + /* * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input, * and @@ -99,11 +123,11 @@ static int add_existing(const char *refname, * (4) ignore if refname is a ref that exists in the local repository; * (5) otherwise output the line. */ -static int exclude_existing(const char *match) +static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts) { - static struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list existing_refs = STRING_LIST_INIT_DUP; char buf[1024]; - int matchlen = match ? strlen(match) : 0; + int patternlen = opts->pattern ? strlen(opts->pattern) : 0; for_each_ref(add_existing, &existing_refs); while (fgets(buf, sizeof(buf), stdin)) { @@ -119,11 +143,11 @@ static int exclude_existing(const char *match) for (ref = buf + len; buf < ref; ref--) if (isspace(ref[-1])) break; - if (match) { + if (opts->pattern) { int reflen = buf + len - ref; - if (reflen < matchlen) + if (reflen < patternlen) continue; - if (strncmp(ref, match, matchlen)) + if (strncmp(ref, opts->pattern, patternlen)) continue; } if (check_refname_format(ref, 0)) { @@ -134,97 +158,172 @@ static int exclude_existing(const char *match) printf("%s\n", buf); } } + + string_list_clear(&existing_refs, 0); + return 0; +} + +static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, + const char **refs) +{ + if (!refs || !*refs) + die("--verify requires a reference"); + + while (*refs) { + struct object_id oid; + + if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && + !read_ref(*refs, &oid)) { + show_one(show_one_opts, *refs, &oid); + } + else if (!show_one_opts->quiet) + die("'%s' - not a valid ref", *refs); + else + return 1; + refs++; + } + return 0; } +struct patterns_options { + int show_head; + int heads_only; + int tags_only; +}; + +static int cmd_show_ref__patterns(const struct patterns_options *opts, + const struct show_one_options *show_one_opts, + const char **patterns) +{ + struct show_ref_data show_ref_data = { + .show_one_opts = show_one_opts, + .show_head = opts->show_head, + }; + + if (patterns && *patterns) + show_ref_data.patterns = patterns; + + if (opts->show_head) + head_ref(show_ref, &show_ref_data); + if (opts->heads_only || opts->tags_only) { + if (opts->heads_only) + for_each_fullref_in("refs/heads/", show_ref, &show_ref_data); + if (opts->tags_only) + for_each_fullref_in("refs/tags/", show_ref, &show_ref_data); + } else { + for_each_ref(show_ref, &show_ref_data); + } + if (!show_ref_data.found_match) + return 1; + + return 0; +} + +static int cmd_show_ref__exists(const char **refs) +{ + struct strbuf unused_referent = STRBUF_INIT; + struct object_id unused_oid; + unsigned int unused_type; + int failure_errno = 0; + const char *ref; + int ret = 0; + + if (!refs || !*refs) + die("--exists requires a reference"); + ref = *refs++; + if (*refs) + die("--exists requires exactly one reference"); + + if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, + &unused_oid, &unused_referent, &unused_type, + &failure_errno)) { + if (failure_errno == ENOENT || failure_errno == EISDIR) { + error(_("reference does not exist")); + ret = 2; + } else { + errno = failure_errno; + error_errno(_("failed to look up reference")); + ret = 1; + } + + goto out; + } + +out: + strbuf_release(&unused_referent); + return ret; +} + static int hash_callback(const struct option *opt, const char *arg, int unset) { - hash_only = 1; + struct show_one_options *opts = opt->value; + struct option abbrev_opt = *opt; + + opts->hash_only = 1; /* Use full length SHA1 if no argument */ if (!arg) return 0; - return parse_opt_abbrev_cb(opt, arg, unset); + + abbrev_opt.value = &opts->abbrev; + return parse_opt_abbrev_cb(&abbrev_opt, arg, unset); } static int exclude_existing_callback(const struct option *opt, const char *arg, int unset) { + struct exclude_existing_options *opts = opt->value; BUG_ON_OPT_NEG(unset); - exclude_arg = 1; - *(const char **)opt->value = arg; + opts->enabled = 1; + opts->pattern = arg; return 0; } -static const struct option show_ref_options[] = { - OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")), - OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")), - OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " - "requires exact ref path")), - OPT_HIDDEN_BOOL('h', NULL, &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL(0, "head", &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL('d', "dereference", &deref_tags, - N_("dereference tags into object IDs")), - OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"), - N_("only show SHA1 hash using <n> digits"), - PARSE_OPT_OPTARG, &hash_callback), - OPT__ABBREV(&abbrev), - OPT__QUIET(&quiet, - N_("do not print results to stdout (useful with --verify)")), - OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg, - N_("pattern"), N_("show refs from stdin that aren't in local repository"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), - OPT_END() -}; - int cmd_show_ref(int argc, const char **argv, const char *prefix) { + struct exclude_existing_options exclude_existing_opts = {0}; + struct patterns_options patterns_opts = {0}; + struct show_one_options show_one_opts = {0}; + int verify = 0, exists = 0; + const struct option show_ref_options[] = { + OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with heads)")), + OPT_BOOL(0, "heads", &patterns_opts.heads_only, N_("only show heads (can be combined with tags)")), + OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")), + OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " + "requires exact ref path")), + OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head, + N_("show the HEAD reference, even if it would be filtered out")), + OPT_BOOL(0, "head", &patterns_opts.show_head, + N_("show the HEAD reference, even if it would be filtered out")), + OPT_BOOL('d', "dereference", &show_one_opts.deref_tags, + N_("dereference tags into object IDs")), + OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"), + N_("only show SHA1 hash using <n> digits"), + PARSE_OPT_OPTARG, &hash_callback), + OPT__ABBREV(&show_one_opts.abbrev), + OPT__QUIET(&show_one_opts.quiet, + N_("do not print results to stdout (useful with --verify)")), + OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts, + N_("pattern"), N_("show refs from stdin that aren't in local repository"), + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), + OPT_END() + }; + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, show_ref_options, show_ref_usage, 0); - if (exclude_arg) - return exclude_existing(exclude_existing_arg); - - pattern = argv; - if (!*pattern) - pattern = NULL; - - if (verify) { - if (!pattern) - die("--verify requires a reference"); - while (*pattern) { - struct object_id oid; - - if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) && - !read_ref(*pattern, &oid)) { - show_one(*pattern, &oid); - } - else if (!quiet) - die("'%s' - not a valid ref", *pattern); - else - return 1; - pattern++; - } - return 0; - } + die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing", + verify, "--verify", + exists, "--exists"); - if (show_head) - head_ref(show_ref, NULL); - if (heads_only || tags_only) { - if (heads_only) - for_each_fullref_in("refs/heads/", show_ref, NULL); - if (tags_only) - for_each_fullref_in("refs/tags/", show_ref, NULL); - } else { - for_each_ref(show_ref, NULL); - } - if (!found_match) { - if (verify && !quiet) - die("No match"); - return 1; - } - return 0; + if (exclude_existing_opts.enabled) + return cmd_show_ref__exclude_existing(&exclude_existing_opts); + else if (verify) + return cmd_show_ref__verify(&show_one_opts, argv); + else if (exists) + return cmd_show_ref__exists(argv); + else + return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv); } diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 5c8ffb1f75..0f52e25249 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -8,14 +8,10 @@ #include "parse-options.h" #include "pathspec.h" #include "repository.h" -#include "run-command.h" #include "strbuf.h" #include "string-list.h" -#include "cache-tree.h" #include "lockfile.h" -#include "resolve-undo.h" #include "unpack-trees.h" -#include "wt-status.h" #include "quote.h" #include "setup.h" #include "sparse-index.h" @@ -777,8 +773,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_OPT); + builtin_sparse_checkout_add_usage, 0); sanitize_paths(argc, argv, prefix, add_opts.skip_checks); @@ -824,8 +819,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_OPT); + builtin_sparse_checkout_set_usage, 0); if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index)) return 1; @@ -835,7 +829,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix) * non-cone mode, if nothing is specified, manually select just the * top-level directory (much as 'init' would do). */ - if (!core_sparse_checkout_cone && argc == 0) { + if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) { argv = default_patterns; argc = default_patterns_nr; } else { @@ -996,8 +990,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char * argc = parse_options(argc, argv, prefix, builtin_sparse_checkout_check_rules_options, - builtin_sparse_checkout_check_rules_usage, - PARSE_OPT_KEEP_UNKNOWN_OPT); + builtin_sparse_checkout_check_rules_usage, 0); if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0) check_rules_opts.cone_mode = 1; diff --git a/builtin/stash.c b/builtin/stash.c index 53e8868ba1..062be1fbc0 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -26,7 +26,6 @@ #include "sparse-index.h" #include "log-tree.h" #include "diffcore.h" -#include "exec-cmd.h" #include "reflog.h" #include "add-interactive.h" @@ -285,7 +284,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset) if (parse_tree(tree)) return -1; - init_tree_desc(t, tree->buffer, tree->size); + init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size); opts.head_idx = 1; opts.src_index = &the_index; @@ -362,7 +361,7 @@ static int is_path_a_directory(const char *path) } static void add_diff_to_buf(struct diff_queue_struct *q, - struct diff_options *options, + struct diff_options *options UNUSED, void *data) { int i; @@ -521,7 +520,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree) repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR); if (write_locked_index(&the_index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("Unable to write index.")); + die(_("could not write index")); } static int do_apply_stash(const char *prefix, struct stash_info *info, @@ -538,7 +537,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, 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; + return error(_("could not write index")); if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0, NULL)) @@ -871,7 +870,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op tree[i] = parse_tree_indirect(oid[i]); if (parse_tree(tree[i]) < 0) die(_("failed to parse tree")); - init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size); + init_tree_desc(&tree_desc[i], &tree[i]->object.oid, + tree[i]->buffer, tree[i]->size); } unpack_tree_opt.head_idx = -1; @@ -989,6 +989,12 @@ usage: static int do_store_stash(const struct object_id *w_commit, const char *stash_msg, int quiet) { + struct stash_info info; + char revision[GIT_MAX_HEXSZ]; + + oid_to_hex_r(revision, w_commit); + assert_stash_like(&info, revision); + if (!stash_msg) stash_msg = "Created via \"git stash store\"."; @@ -1359,7 +1365,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b 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; + ret = error(_("could not write index")); goto done; } @@ -1550,7 +1556,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0, NULL, NULL, NULL)) { - ret = -1; + ret = error(_("could not write index")); goto done; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 6f3bf33e61..fda50f2af1 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -22,7 +22,6 @@ #include "remote.h" #include "refs.h" #include "refspec.h" -#include "connect.h" #include "revision.h" #include "diffcore.h" #include "diff.h" @@ -2889,7 +2888,7 @@ cleanup: static int module_set_url(int argc, const char **argv, const char *prefix) { - int quiet = 0; + int quiet = 0, ret; const char *newurl; const char *path; char *config_name; @@ -2901,20 +2900,29 @@ static int module_set_url(int argc, const char **argv, const char *prefix) N_("git submodule set-url [--quiet] <path> <newurl>"), NULL }; + const struct submodule *sub; argc = parse_options(argc, argv, prefix, options, usage, 0); if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1])) usage_with_options(usage, options); - config_name = xstrfmt("submodule.%s.url", path); + sub = submodule_from_path(the_repository, null_oid(), path); - config_set_in_gitmodules_file_gently(config_name, newurl); - sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0); + if (!sub) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); - free(config_name); + config_name = xstrfmt("submodule.%s.url", sub->name); + ret = config_set_in_gitmodules_file_gently(config_name, newurl); - return 0; + if (!ret) { + repo_read_gitmodules(the_repository, 0); + sync_submodule(sub->path, prefix, NULL, quiet ? OPT_QUIET : 0); + } + + free(config_name); + return !!ret; } static int module_set_branch(int argc, const char **argv, const char *prefix) @@ -2941,6 +2949,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"), NULL }; + const struct submodule *sub; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -2953,7 +2962,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) if (argc != 1 || !(path = argv[0])) usage_with_options(usage, options); - config_name = xstrfmt("submodule.%s.branch", path); + sub = submodule_from_path(the_repository, null_oid(), path); + + if (!sub) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); + + config_name = xstrfmt("submodule.%s.branch", sub->name); ret = config_set_in_gitmodules_file_gently(config_name, opt_branch); free(config_name); diff --git a/builtin/tag.c b/builtin/tag.c index 3918eacbb5..e2ff749832 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -18,7 +18,6 @@ #include "object-store-ll.h" #include "path.h" #include "tag.h" -#include "run-command.h" #include "parse-options.h" #include "diff.h" #include "revision.h" @@ -28,6 +27,7 @@ #include "ref-filter.h" #include "date.h" #include "write-or-die.h" +#include "object-file-convert.h" static const char * const git_tag_usage[] = { N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n" @@ -44,18 +44,11 @@ static const char * const git_tag_usage[] = { static unsigned int colopts; static int force_sign_annotate; static int config_sign_tag = -1; /* unspecified */ -static int omit_empty = 0; static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format) { - struct ref_array array; - struct strbuf output = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; char *to_free = NULL; - int i; - - memset(&array, 0, sizeof(array)); if (filter->lines == -1) filter->lines = 0; @@ -73,23 +66,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, if (verify_ref_format(format)) die(_("unable to parse format string")); filter->with_commit_tag_algo = 1; - filter_refs(&array, filter, FILTER_REFS_TAGS); - filter_ahead_behind(the_repository, format, &array); - ref_array_sort(sorting, &array); - - for (i = 0; i < array.nr; i++) { - strbuf_reset(&output); - strbuf_reset(&err); - if (format_ref_array_item(array.items[i], format, &output, &err)) - die("%s", err.buf); - fwrite(output.buf, 1, output.len, stdout); - if (output.len || !omit_empty) - putchar('\n'); - } + filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format); - strbuf_release(&err); - strbuf_release(&output); - ref_array_clear(&array); free(to_free); return 0; @@ -174,9 +152,43 @@ static int verify_tag(const char *name, const char *ref UNUSED, return 0; } -static int do_sign(struct strbuf *buffer) +static int do_sign(struct strbuf *buffer, struct object_id **compat_oid, + struct object_id *compat_oid_buf) { - return sign_buffer(buffer, buffer, get_signing_key()); + const struct git_hash_algo *compat = the_repository->compat_hash_algo; + struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT; + struct strbuf compat_buf = STRBUF_INIT; + const char *keyid = get_signing_key(); + int ret = -1; + + if (sign_buffer(buffer, &sig, keyid)) + return -1; + + if (compat) { + const struct git_hash_algo *algo = the_repository->hash_algo; + + if (convert_object_file(&compat_buf, algo, compat, + buffer->buf, buffer->len, OBJ_TAG, 1)) + goto out; + if (sign_buffer(&compat_buf, &compat_sig, keyid)) + goto out; + add_header_signature(&compat_buf, &sig, algo); + strbuf_addbuf(&compat_buf, &compat_sig); + hash_object_file(compat, compat_buf.buf, compat_buf.len, + OBJ_TAG, compat_oid_buf); + *compat_oid = compat_oid_buf; + } + + if (compat_sig.len) + add_header_signature(buffer, &compat_sig, compat); + + strbuf_addbuf(buffer, &sig); + ret = 0; +out: + strbuf_release(&sig); + strbuf_release(&compat_sig); + strbuf_release(&compat_buf); + return ret; } static const char tag_template[] = @@ -249,9 +261,11 @@ static void write_tag_body(int fd, const struct object_id *oid) static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result) { - if (sign && do_sign(buf) < 0) + struct object_id *compat_oid = NULL, compat_oid_buf; + if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0) return error(_("unable to sign the tag")); - if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0) + if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result, + compat_oid, 0) < 0) return error(_("unable to write tag file")); return 0; } @@ -481,7 +495,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")), OPT_MERGED(&filter, N_("print only tags that are merged")), OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), - OPT_BOOL(0, "omit-empty", &omit_empty, + OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty, N_("do not output a newline after empty formatted refs")), OPT_REF_SORT(&sorting_options), { @@ -501,7 +515,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) setup_ref_filter_porcelain_msg(); + /* + * Try to set sort keys from config. If config does not set any, + * fall back on default (refname) sorting. + */ git_config(git_tag_config, &sorting_options); + if (!sorting_options.nr) + string_list_append(&sorting_options, "refname"); memset(&opt, 0, sizeof(opt)); filter.lines = -1; @@ -547,7 +567,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct column_options copts; memset(&copts, 0, sizeof(copts)); copts.padding = 2; - run_column_filter(colopts, &copts); + if (run_column_filter(colopts, &copts)) + die(_("could not start 'git column'")); } filter.name_patterns = argv; ret = list_tags(&filter, sorting, &format); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 32505255a0..f1c85a00ae 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -10,12 +10,8 @@ #include "delta.h" #include "pack.h" #include "blob.h" -#include "commit.h" #include "replace-object.h" #include "strbuf.h" -#include "tag.h" -#include "tree.h" -#include "tree-walk.h" #include "progress.h" #include "decorate.h" #include "fsck.h" @@ -609,6 +605,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) { int i; struct object_id oid; + git_hash_ctx tmp_ctx; disable_replace_refs(); @@ -669,7 +666,9 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) the_hash_algo->init_fn(&ctx); unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); - the_hash_algo->final_oid_fn(&oid, &ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &ctx); + the_hash_algo->final_oid_fn(&oid, &tmp_ctx); if (strict) { write_rest(); if (fsck_finish(&fsck_options)) @@ -680,13 +679,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) use(the_hash_algo->rawsz); /* Write the last part of the buffer to stdout */ - while (len) { - int ret = xwrite(1, buffer + offset, len); - if (ret <= 0) - break; - len -= ret; - offset += ret; - } + write_in_full(1, buffer + offset, len); /* All done */ return has_errors; diff --git a/builtin/update-index.c b/builtin/update-index.c index aee3cb8cbd..7bcaa1476c 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -609,9 +609,6 @@ static const char * const update_index_usage[] = { NULL }; -static struct object_id head_oid; -static struct object_id merge_head_oid; - static struct cache_entry *read_one_ent(const char *which, struct object_id *ent, const char *path, int namelen, int stage) @@ -642,84 +639,17 @@ static struct cache_entry *read_one_ent(const char *which, static int unresolve_one(const char *path) { - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = index_name_pos(&the_index, path, namelen); - if (0 <= pos) { - /* already merged */ - 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)) - return 0; - } - /* no resolve-undo information; fall back */ - } else { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - 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, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", &head_oid, path, namelen, 2); - ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (oideq(&ce_2->oid, &ce_3->oid) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - 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_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; - free_return: - discard_cache_entry(ce_2); - discard_cache_entry(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref("HEAD", &head_oid)) - die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", &merge_head_oid)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } + struct string_list_item *item; + int res = 0; + + if (!the_index.resolve_undo) + return res; + item = string_list_lookup(the_index.resolve_undo, path); + if (!item) + return res; /* no resolve-undo record for the path */ + res = unmerge_index_entry(&the_index, path, item->util, 0); + FREE_AND_NULL(item->util); + return res; } static int do_unresolve(int ac, const char **av, @@ -728,11 +658,6 @@ static int do_unresolve(int ac, const char **av, int i; int err = 0; - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - for (i = 1; i < ac; i++) { const char *arg = av[i]; char *p = prefix_path(prefix, prefix_length, arg); @@ -751,6 +676,7 @@ static int do_reupdate(const char **paths, int pos; int has_head = 1; struct pathspec pathspec; + struct object_id head_oid; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, @@ -856,7 +782,7 @@ static int chmod_callback(const struct option *opt, return 0; } -static int resolve_undo_clear_callback(const struct option *opt, +static int resolve_undo_clear_callback(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); @@ -890,7 +816,7 @@ static int parse_new_style_cacheinfo(const char *arg, } static enum parse_opt_result cacheinfo_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, + struct parse_opt_ctx_t *ctx, const struct option *opt UNUSED, const char *arg, int unset) { struct object_id oid; @@ -1090,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) resolve_undo_clear_callback), OPT_INTEGER(0, "index-version", &preferred_index_format, N_("write index in this format")), + OPT_SET_INT(0, "show-index-version", &preferred_index_format, + N_("report on-disk index format version"), -1), OPT_BOOL(0, "split-index", &split_index, N_("enable or disable split index")), OPT_BOOL(0, "untracked-cache", &untracked_cache, @@ -1182,15 +1110,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { - if (preferred_index_format < INDEX_FORMAT_LB || - INDEX_FORMAT_UB < preferred_index_format) + if (preferred_index_format < 0) { + printf(_("%d\n"), the_index.version); + } else if (preferred_index_format < INDEX_FORMAT_LB || + INDEX_FORMAT_UB < preferred_index_format) { die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); - - if (the_index.version != preferred_index_format) - the_index.cache_changed |= SOMETHING_CHANGED; - the_index.version = preferred_index_format; + } else { + if (the_index.version != preferred_index_format) + the_index.cache_changed |= SOMETHING_CHANGED; + report(_("index-version: was %d, set to %d"), + the_index.version, preferred_index_format); + the_index.version = preferred_index_format; + } } if (read_from_stdin) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 242102273e..61338a01ec 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -7,7 +7,6 @@ #include "parse-options.h" #include "quote.h" #include "repository.h" -#include "strvec.h" static const char * const git_update_ref_usage[] = { N_("git update-ref [<options>] -d <refname> [<old-val>]"), @@ -311,8 +310,8 @@ static void report_ok(const char *command) fflush(stdout); } -static void parse_cmd_option(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_option(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { const char *rest; if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) @@ -321,8 +320,8 @@ static void parse_cmd_option(struct ref_transaction *transaction, die("option unknown: %s", next); } -static void parse_cmd_start(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_start(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { if (*next != line_termination) die("start: extra input: %s", next); @@ -330,7 +329,7 @@ static void parse_cmd_start(struct ref_transaction *transaction, } static void parse_cmd_prepare(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -341,7 +340,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction, } static void parse_cmd_abort(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -352,7 +351,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction, } static void parse_cmd_commit(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 9b021ef026..15afb97260 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -8,6 +8,7 @@ #include "replace-object.h" #include "upload-pack.h" #include "serve.h" +#include "commit.h" static const char * const upload_pack_usage[] = { N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" @@ -37,6 +38,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("upload-pack"); disable_replace_refs(); + save_commit_buffer = 0; argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0); diff --git a/builtin/var.c b/builtin/var.c index 8cf7dd9e2e..cf5567208a 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -90,7 +90,7 @@ static char *git_config_val_global(int ident_flag UNUSED) char *user, *xdg; size_t unused; - git_global_config(&user, &xdg); + git_global_config_paths(&user, &xdg); if (xdg && *xdg) { normalize_path_copy(xdg, xdg); strbuf_addf(&buf, "%s\n", xdg); diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 9680b58701..0d2b9aea2a 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -9,10 +9,8 @@ #include "config.h" #include "gettext.h" #include "object-name.h" -#include "object-store-ll.h" #include "repository.h" #include "commit.h" -#include "run-command.h" #include "parse-options.h" #include "gpg-interface.h" diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index d8753270eb..c731e2f87b 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -9,7 +9,6 @@ #include "config.h" #include "gettext.h" #include "tag.h" -#include "run-command.h" #include "object-name.h" #include "parse-options.h" #include "gpg-interface.h" diff --git a/builtin/worktree.c b/builtin/worktree.c index 10db70b7ec..9c76b62b02 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -49,14 +49,14 @@ _("No possible source branch, inferring '--orphan'") #define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \ - _("If you meant to create a worktree containing a new orphan branch\n" \ + _("If you meant to create a worktree containing a new unborn branch\n" \ "(branch with no commits) for this repository, you can do so\n" \ "using the --orphan flag:\n" \ "\n" \ " git worktree add --orphan -b %s %s\n") #define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \ - _("If you meant to create a worktree containing a new orphan branch\n" \ + _("If you meant to create a worktree containing a new unborn branch\n" \ "(branch with no commits) for this repository, you can do so\n" \ "using the --orphan flag:\n" \ "\n" \ @@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname, struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT; const char *name; - struct child_process cp = CHILD_PROCESS_INIT; struct strvec child_env = STRVEC_INIT; unsigned int counter = 0; int len, ret; @@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname, struct commit *commit = NULL; int is_branch = 0; struct strbuf sb_name = STRBUF_INIT; - struct worktree **worktrees; + struct worktree **worktrees, *wt = NULL; + struct ref_store *wt_refs; worktrees = get_worktrees(); check_candidate_path(path, opts->force, worktrees, "add"); @@ -495,21 +495,33 @@ static int add_worktree(const char *path, const char *refname, strbuf_realpath(&realpath, get_git_common_dir(), 1); write_file(sb_git.buf, "gitdir: %s/worktrees/%s", realpath.buf, name); - /* - * This is to keep resolve_ref() happy. We need a valid HEAD - * or is_git_directory() will reject the directory. Any value which - * looks like an object ID will do since it will be immediately - * replaced by the symbolic-ref or update-ref invocation in the new - * worktree. - */ - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); - write_file(sb.buf, "%s", oid_to_hex(null_oid())); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); /* + * Set up the ref store of the worktree and create the HEAD reference. + */ + wt = get_linked_worktree(name, 1); + if (!wt) { + ret = error(_("could not find created worktree '%s'"), name); + goto done; + } + wt_refs = get_worktree_ref_store(wt); + + ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb); + if (ret) + goto done; + + if (!is_branch && commit) + ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid, + NULL, 0, UPDATE_REFS_MSG_ON_ERR); + else + ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL); + if (ret) + goto done; + + /* * If the current worktree has sparse-checkout enabled, then copy * the sparse-checkout patterns from the current worktree. */ @@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname, strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); - cp.git_cmd = 1; - - if (!is_branch && commit) { - strvec_pushl(&cp.args, "update-ref", "HEAD", - oid_to_hex(&commit->object.oid), NULL); - } else { - strvec_pushl(&cp.args, "symbolic-ref", "HEAD", - symref.buf, NULL); - if (opts->quiet) - strvec_push(&cp.args, "--quiet"); - } - - strvec_pushv(&cp.env, child_env.v); - ret = run_command(&cp); - if (ret) - goto done; if (opts->orphan && (ret = make_worktree_orphan(refname, opts, &child_env))) @@ -587,6 +583,7 @@ done: strbuf_release(&sb_git); strbuf_release(&sb_name); strbuf_release(&realpath); + free_worktree(wt); return ret; } @@ -628,10 +625,10 @@ static void print_preparing_worktree_line(int detach, * * Returns 0 on failure and non-zero on success. */ -static int first_valid_ref(const char *refname, - const struct object_id *oid, - int flags, - void *cb_data) +static int first_valid_ref(const char *refname UNUSED, + const struct object_id *oid UNUSED, + int flags UNUSED, + void *cb_data UNUSED) { return 1; } @@ -730,11 +727,11 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote) } if (opt_track) { - die(_("'%s' and '%s' cannot be used together"), "--orphan", - "--track"); + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--track"); } else if (!opts->checkout) { - die(_("'%s' and '%s' cannot be used together"), "--orphan", - "--no-checkout"); + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--no-checkout"); } return 1; } @@ -784,7 +781,7 @@ static int add(int ac, const char **av, const char *prefix) N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), - OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn/orphaned branch")), + OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn branch")), OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")), OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")), @@ -806,16 +803,17 @@ static int add(int ac, const char **av, const char *prefix) if (!!opts.detach + !!new_branch + !!new_branch_force > 1) die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach"); if (opts.detach && opts.orphan) - die(_("options '%s', and '%s' cannot be used together"), + die(_("options '%s' and '%s' cannot be used together"), "--orphan", "--detach"); if (opts.orphan && opt_track) - die(_("'%s' and '%s' cannot be used together"), "--orphan", "--track"); + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--track"); if (opts.orphan && !opts.checkout) - die(_("'%s' and '%s' cannot be used together"), "--orphan", - "--no-checkout"); + die(_("options '%s' and '%s' cannot be used together"), + "--orphan", "--no-checkout"); if (opts.orphan && ac == 2) - die(_("'%s' and '%s' cannot be used together"), "--orphan", - _("<commit-ish>")); + die(_("option '%s' and commit-ish cannot be used together"), + "--orphan"); if (lock_reason && !keep_locked) die(_("the option '%s' requires '%s'"), "--reason", "--lock"); if (lock_reason) @@ -850,21 +848,21 @@ static int add(int ac, const char **av, const char *prefix) const char *s = worktree_basename(path, &n); new_branch = xstrndup(s, n); } else if (opts.orphan) { - // No-op + ; /* no-op */ } else if (opts.detach) { - // Check HEAD + /* Check HEAD */ if (!strcmp(branch, "HEAD")) can_use_local_refs(&opts); } else if (ac < 2 && new_branch) { - // DWIM: Infer --orphan when repo has no refs. + /* DWIM: Infer --orphan when repo has no refs. */ opts.orphan = dwim_orphan(&opts, !!opt_track, 0); } else if (ac < 2) { - // DWIM: Guess branch name from path. + /* DWIM: Guess branch name from path. */ const char *s = dwim_branch(path, &new_branch); if (s) branch = s; - // DWIM: Infer --orphan when repo has no refs. + /* DWIM: Infer --orphan when repo has no refs. */ opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1); } else if (ac == 2) { struct object_id oid; |
