diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-20 05:14:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-20 05:14:36 +0000 |
commit | 09f61306ecfdf0e532c58460d8d868d50021e7db (patch) | |
tree | b6f46cb23ce41cddcb6e1320d5c1c4bd5d69e899 /builtin | |
parent | Adding debian version 1:2.43.0-1. (diff) | |
download | git-09f61306ecfdf0e532c58460d8d868d50021e7db.tar.xz git-09f61306ecfdf0e532c58460d8d868d50021e7db.zip |
Merging upstream version 1:2.45.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'builtin')
87 files changed, 2116 insertions, 1118 deletions
diff --git a/builtin/add.c b/builtin/add.c index 5126d2e..b7d3ff1 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); @@ -153,7 +150,7 @@ static int refresh(int verbose, const struct pathspec *pathspec) int interactive_add(const char **argv, const char *prefix, int patch) { struct pathspec pathspec; - int unused; + int unused, ret; if (!git_config_get_bool("add.interactive.usebuiltin", &unused)) warning(_("the add.interactive.useBuiltin setting has been removed!\n" @@ -166,9 +163,12 @@ int interactive_add(const char **argv, const char *prefix, int patch) prefix, argv); if (patch) - return !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec); else - return !!run_add_i(the_repository, &pathspec); + ret = !!run_add_i(the_repository, &pathspec); + + clear_pathspec(&pathspec); + return ret; } static int edit_patch(int argc, const char **argv, const char *prefix) @@ -313,9 +313,9 @@ static void check_embedded_repo(const char *path) strbuf_strip_suffix(&name, "/"); warning(_("adding embedded git repository: %s"), name.buf); - if (!adviced_on_embedded_repo && - advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) { - advise(embedded_advice, name.buf, name.buf); + if (!adviced_on_embedded_repo) { + advise_if_enabled(ADVICE_ADD_EMBEDDED_REPO, + embedded_advice, name.buf, name.buf); adviced_on_embedded_repo = 1; } @@ -331,10 +331,8 @@ static int add_files(struct dir_struct *dir, int flags) fprintf(stderr, _(ignore_error)); for (i = 0; i < dir->ignored_nr; i++) fprintf(stderr, "%s\n", dir->ignored[i]->name); - if (advice_enabled(ADVICE_ADD_IGNORED_FILE)) - advise(_("Use -f if you really want to add them.\n" - "Turn this message off by running\n" - "\"git config advice.addIgnoredFile false\"")); + advise_if_enabled(ADVICE_ADD_IGNORED_FILE, + _("Use -f if you really want to add them.")); exit_status = 1; } @@ -373,6 +371,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; + char *ps_matched = NULL; struct lock_file lock_file = LOCK_INIT; git_config(add_config, NULL); @@ -424,7 +423,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); @@ -433,7 +432,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); @@ -443,10 +442,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (require_pathspec && pathspec.nr == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); - if (advice_enabled(ADVICE_ADD_EMPTY_PATHSPEC)) - advise( _("Maybe you wanted to say 'git add .'?\n" - "Turn this message off by running\n" - "\"git config advice.addEmptyPathspec false\"")); + advise_if_enabled(ADVICE_ADD_EMPTY_PATHSPEC, + _("Maybe you wanted to say 'git add .'?")); return 0; } @@ -504,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; @@ -551,12 +549,17 @@ int cmd_add(int argc, const char **argv, const char *prefix) begin_odb_transaction(); + ps_matched = xcalloc(pathspec.nr, 1); if (add_renormalize) exit_status |= renormalize_tracked_files(&pathspec, flags); else exit_status |= add_files_to_cache(the_repository, prefix, - &pathspec, include_sparse, - flags); + &pathspec, ps_matched, + include_sparse, flags); + + if (take_worktree_changes && !add_renormalize && !ignore_add_errors && + report_path_error(ps_matched, &pathspec)) + exit(128); if (add_new_files) exit_status |= add_files(&dir, flags); @@ -570,6 +573,7 @@ finish: COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("unable to write new index file")); + free(ps_matched); dir_clear(&dir); clear_pathspec(&pathspec); return exit_status; diff --git a/builtin/am.c b/builtin/am.c index 9f084d5..022cae2 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" @@ -1154,19 +1150,23 @@ static const char *msgnum(const struct am_state *state) static void NORETURN die_user_resolve(const struct am_state *state) { if (state->resolvemsg) { - printf_ln("%s", state->resolvemsg); + advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", state->resolvemsg); } else { const char *cmdline = state->interactive ? "git am -i" : "git am"; + struct strbuf sb = STRBUF_INIT; - printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline); - printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline); + strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline); + strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline); if (advice_enabled(ADVICE_AM_WORK_DIR) && is_empty_or_missing_file(am_path(state, "patch")) && !repo_index_has_changes(the_repository, NULL, NULL)) - printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline); + strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline); - printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline); + strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline); + + advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", sb.buf); + strbuf_release(&sb); } exit(128); @@ -1290,7 +1290,7 @@ static int parse_mail(struct am_state *state, const char *mail) strbuf_addstr(&msg, "\n\n"); strbuf_addbuf(&msg, &mi.log_message); - strbuf_stripspace(&msg, '\0'); + strbuf_stripspace(&msg, NULL); assert(!state->author_name); state->author_name = strbuf_detach(&author_name, NULL); @@ -1998,8 +1998,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); @@ -2033,7 +2033,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); diff --git a/builtin/apply.c b/builtin/apply.c index c18b7ea..861a019 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 90761fd..15ee1ec 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 35938b0..f69c3f7 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") @@ -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 9c987d6..9aa7468 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" @@ -317,7 +316,7 @@ static const char *format_time(timestamp_t time, const char *tz_str, size_t time_width; int tz; tz = atoi(tz_str); - time_str = show_date(time, tz, &blame_date_mode); + time_str = show_date(time, tz, blame_date_mode); strbuf_addstr(&time_buf, time_str); /* * Add space paddings to time_buf to display a fixed width @@ -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")) { @@ -1028,7 +1029,7 @@ parse_done: blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); break; case DATE_STRFTIME: - blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */ + blame_date_width = strlen(show_date(0, 0, blame_date_mode)) + 1; /* add the null */ break; } blame_date_width -= 1; /* strip the null */ diff --git a/builtin/branch.c b/builtin/branch.c index e7ee9bd..dd3e3a7 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,9 +168,13 @@ 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"), name, reference_name); @@ -194,9 +197,10 @@ static int check_branch_commit(const char *branchname, const char *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; @@ -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); } @@ -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++) { @@ -668,18 +677,18 @@ static int edit_branch_description(const char *branch_name) exists = !read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); - strbuf_commented_addf(&buf, comment_line_char, + strbuf_commented_addf(&buf, comment_line_str, _("Please edit the description for the branch\n" " %s\n" - "Lines starting with '%c' will be stripped.\n"), - branch_name, comment_line_char); + "Lines starting with '%s' will be stripped.\n"), + branch_name, comment_line_str); write_file_buf(edit_description(), buf.buf, buf.len); strbuf_reset(&buf); if (launch_editor(edit_description(), &buf, NULL)) { strbuf_release(&buf); return -1; } - strbuf_stripspace(&buf, comment_line_char); + strbuf_stripspace(&buf, comment_line_str); strbuf_addf(&name, "branch.%s.description", branch_name); if (buf.len || exists) @@ -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,7 +776,13 @@ 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; diff --git a/builtin/bugreport.c b/builtin/bugreport.c index 3106e56..25f860a 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 }; @@ -138,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 ea8ad60..0c948f4 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 @@ -307,8 +314,8 @@ static int is_atom(const char *atom, const char *s, int slen) return alen == slen && !memcmp(atom, s, alen); } -static void expand_atom(struct strbuf *sb, const char *atom, int len, - struct expand_data *data) +static int expand_atom(struct strbuf *sb, const char *atom, int len, + struct expand_data *data) { if (is_atom("objectname", atom, len)) { if (!data->mark_query) @@ -340,7 +347,8 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, strbuf_addstr(sb, oid_to_hex(&data->delta_base_oid)); } else - die("unknown format element: %.*s", len, atom); + return 0; + return 1; } static void expand_format(struct strbuf *sb, const char *start, @@ -351,12 +359,11 @@ static void expand_format(struct strbuf *sb, const char *start, if (skip_prefix(start, "%", &start) || *start != '(') strbuf_addch(sb, '%'); - else if (!(end = strchr(start + 1, ')'))) - die("format element '%s' does not end in ')'", start); - else { - expand_atom(sb, start + 1, end - start - 1, data); + else if ((end = strchr(start + 1, ')')) && + expand_atom(sb, start + 1, end - start - 1, data)) start = end + 1; - } + else + strbuf_expand_bad_format(start, "cat-file"); } } @@ -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/checkout-index.c b/builtin/checkout-index.c index 3b68b47..2e086a2 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" diff --git a/builtin/checkout.c b/builtin/checkout.c index f02434b..71e6036 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; } } @@ -707,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; @@ -786,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) @@ -822,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); @@ -871,7 +882,8 @@ static int merge_working_tree(const struct checkout_opts *opts, * entries in the index. */ - add_files_to_cache(the_repository, NULL, NULL, 0, 0); + add_files_to_cache(the_repository, NULL, NULL, NULL, 0, + 0); init_merge_options(&o, the_repository); o.verbosity = 0; work = write_in_core_index_as_tree(the_repository); @@ -890,6 +902,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, @@ -1023,7 +1036,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts, remove_branch_state(the_repository, !opts->quiet); strbuf_release(&msg); if (!opts->quiet && - (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD")))) + !opts->force_detach && + (new_branch_info->path || !strcmp(new_branch_info->name, "HEAD"))) report_tracking(new_branch_info); } @@ -1202,6 +1216,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; } @@ -1225,7 +1241,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) && @@ -1239,10 +1257,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)); } } @@ -1516,6 +1539,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) { @@ -1576,14 +1619,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) { @@ -1597,6 +1641,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) { @@ -1607,8 +1669,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); @@ -1627,7 +1690,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), @@ -1667,10 +1730,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; @@ -1699,15 +1763,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"; @@ -1786,7 +1845,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) { @@ -1795,7 +1854,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); @@ -1815,7 +1874,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); @@ -1865,14 +1924,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"), @@ -1885,10 +1951,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; @@ -1916,18 +1979,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"), @@ -1940,10 +1998,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; @@ -1960,16 +2015,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>", @@ -1983,10 +2035,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; @@ -1999,9 +2048,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 49c224e..29efe84 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 c6357af..221edbc 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, @@ -327,7 +329,20 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, int src_len, dest_len; struct dir_iterator *iter; int iter_status; - struct strbuf realpath = STRBUF_INIT; + + /* + * Refuse copying directories by default which aren't owned by us. The + * code that performs either the copying or hardlinking is not prepared + * to handle various edge cases where an adversary may for example + * racily swap out files for symlinks. This can cause us to + * inadvertently use the wrong source file. + * + * Furthermore, even if we were prepared to handle such races safely, + * creating hardlinks across user boundaries is an inherently unsafe + * operation as the hardlinked files can be rewritten at will by the + * potentially-untrusted user. We thus refuse to do so by default. + */ + die_upon_dubious_ownership(NULL, NULL, src_repo); mkdir_if_missing(dest->buf, 0777); @@ -375,9 +390,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, if (unlink(dest->buf) && errno != ENOENT) die_errno(_("failed to unlink '%s'"), dest->buf); if (!option_no_hardlinks) { - strbuf_realpath(&realpath, src->buf, 1); - if (!link(realpath.buf, dest->buf)) + if (!link(src->buf, dest->buf)) { + struct stat st; + + /* + * Sanity-check whether the created hardlink + * actually links to the expected file now. This + * catches time-of-check-time-of-use bugs in + * case the source file was meanwhile swapped. + */ + if (lstat(dest->buf, &st)) + die(_("hardlink cannot be checked at '%s'"), dest->buf); + if (st.st_mode != iter->st.st_mode || + st.st_ino != iter->st.st_ino || + st.st_dev != iter->st.st_dev || + st.st_size != iter->st.st_size || + st.st_uid != iter->st.st_uid || + st.st_gid != iter->st.st_gid) + die(_("hardlink different from source at '%s'"), dest->buf); + continue; + } if (option_local > 0) die_errno(_("failed to create link '%s'"), dest->buf); option_no_hardlinks = 1; @@ -390,8 +423,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, strbuf_setlen(src, src_len); die(_("failed to iterate over '%s'"), src->buf); } - - strbuf_release(&realpath); } static void clone_local(const char *src_repo, const char *dest_repo) @@ -736,8 +767,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 +823,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 +956,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,7 +965,10 @@ 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; + const char *template_dir; + char *template_dir_dup = NULL; struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; @@ -950,11 +988,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); + xsetenv("GIT_CLONE_PROTECTION_ACTIVE", "true", 0 /* allow user override */); + template_dir = get_template_dir(option_template); + if (*template_dir && !is_absolute_path(template_dir)) + template_dir = template_dir_dup = + absolute_pathdup(template_dir); + xsetenv("GIT_CLONE_TEMPLATE_DIR", template_dir, 1); + if (option_depth || option_since || option_not.nr) deepen = 1; if (option_single_branch == -1) 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 +1016,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 +1150,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, template_dir, 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 +1166,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 +1289,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 +1367,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 +1403,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 +1424,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 +1528,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); @@ -1441,6 +1545,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) free(dir); free(path); free(repo_to_free); + free(template_dir_dup); junk_mode = JUNK_LEAVE_ALL; transport_ls_refs_options_release(&transport_ls_refs_options); diff --git a/builtin/column.c b/builtin/column.c index a83be8b..10ff7e0 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 45d035a..7102ee9 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, diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 02625e7..1bb7819 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 781af2e..6e14844 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 */ } @@ -448,16 +441,21 @@ static const char *prepare_index(const char **argv, const char *prefix, * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { + char *ps_matched = xcalloc(pathspec.nr, 1); repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR); add_files_to_cache(the_repository, also ? prefix : NULL, - &pathspec, 0, 0); + &pathspec, ps_matched, 0, 0); + if (!all && report_path_error(ps_matched, &pathspec)) + exit(128); + refresh_cache_or_die(refresh_flags); cache_tree_update(&the_index, WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, 0)) die(_("unable to write new index file")); commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); + free(ps_matched); goto out; } @@ -692,9 +690,10 @@ static void adjust_comment_line_char(const struct strbuf *sb) char *candidate; const char *p; - comment_line_char = candidates[0]; - if (!memchr(sb->buf, comment_line_char, sb->len)) + if (!memchr(sb->buf, candidates[0], sb->len)) { + comment_line_str = xstrfmt("%c", candidates[0]); return; + } p = sb->buf; candidate = strchr(candidates, *p); @@ -713,7 +712,7 @@ static void adjust_comment_line_char(const struct strbuf *sb) if (!*p) die(_("unable to select a comment character that is not used\n" "in the current commit message")); - comment_line_char = *p; + comment_line_str = xstrfmt("%c", *p); } static void prepare_amend_commit(struct commit *commit, struct strbuf *sb, @@ -745,7 +744,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 +847,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")); @@ -897,10 +895,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix, s->hints = 0; if (clean_message_contents) - strbuf_stripspace(&sb, '\0'); + strbuf_stripspace(&sb, NULL); 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")); @@ -917,24 +915,23 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct ident_split ci, ai; const char *hint_cleanup_all = allow_empty_message ? _("Please enter the commit message for your changes." - " Lines starting\nwith '%c' will be ignored.\n") : + " Lines starting\nwith '%s' will be ignored.\n") : _("Please enter the commit message for your changes." - " Lines starting\nwith '%c' will be ignored, and an empty" + " Lines starting\nwith '%s' will be ignored, and an empty" " message aborts the commit.\n"); const char *hint_cleanup_space = allow_empty_message ? _("Please enter the commit message for your changes." " Lines starting\n" - "with '%c' will be kept; you may remove them" + "with '%s' will be kept; you may remove them" " yourself if you want to.\n") : _("Please enter the commit message for your changes." " Lines starting\n" - "with '%c' will be kept; you may remove them" + "with '%s' will be kept; you may remove them" " 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 ? @@ -952,12 +949,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, fprintf(s->fp, "\n"); if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) - status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char); + status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_str); 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); + status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_str); /* * These should never fail because they come from our own @@ -1166,22 +1163,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 +1484,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 +1901,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 11a4d4e..0015620 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -44,6 +44,7 @@ static struct config_options config_options; static int show_origin; static int show_scope; static int fixed_value; +static const char *comment; #define ACTION_GET (1<<0) #define ACTION_GET_ALL (1<<1) @@ -173,6 +174,7 @@ static struct option builtin_config_options[] = { OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")), OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), + OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")), OPT_END(), }; @@ -708,10 +710,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 +719,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 +749,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 @@ -811,6 +799,12 @@ int cmd_config(int argc, const char **argv, const char *prefix) usage_builtin_config(); } + if (comment && + !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) { + error(_("--comment is only applicable to add/set/replace operations")); + usage_builtin_config(); + } + /* check usage of --fixed-value */ if (fixed_value) { int allowed_usage = 0; @@ -847,6 +841,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) flags |= CONFIG_FLAGS_FIXED_VALUE; } + comment = git_config_prepare_comment_string(comment); + if (actions & PAGING_ACTIONS) setup_auto_pager("config", 1); @@ -894,7 +890,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1], &default_kvi); - ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value); + ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s."), argv[0]); @@ -905,7 +901,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, argv[2], - flags); + comment, flags); } else if (actions == ACTION_ADD) { check_write(); @@ -914,7 +910,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, CONFIG_REGEX_NONE, - flags); + comment, flags); } else if (actions == ACTION_REPLACE_ALL) { check_write(); @@ -922,7 +918,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) value = normalize_value(argv[0], argv[1], &default_kvi); ret = git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], value, argv[2], - flags | CONFIG_FLAGS_MULTI_REPLACE); + comment, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); @@ -950,17 +946,17 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (argc == 2) return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], NULL, argv[1], - flags); + NULL, flags); else return git_config_set_in_file_gently(given_config_source.file, - argv[0], NULL); + argv[0], NULL, NULL); } else if (actions == ACTION_UNSET_ALL) { check_write(); check_argc(argc, 1, 2); return git_config_set_multivar_in_file_gently(given_config_source.file, argv[0], NULL, argv[1], - flags | CONFIG_FLAGS_MULTI_REPLACE); + NULL, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { check_write(); diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c index 3a6a750..17f929d 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -294,6 +294,8 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, usage, 0); socket_path = argv[0]; + if (!have_unix_sockets()) + die(_("credential-cache--daemon unavailable; no unix socket support")); if (!socket_path) usage_with_options(usage, options); diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index 43b9d0e..bef120b 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" @@ -151,6 +149,9 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix) usage_with_options(usage, options); op = argv[0]; + if (!have_unix_sockets()) + die(_("credential-cache unavailable; no unix socket support")); + if (!socket_path) socket_path = get_socket_path(); if (!socket_path) diff --git a/builtin/describe.c b/builtin/describe.c index fb6b050..d6c77a7 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" diff --git a/builtin/diff-files.c b/builtin/diff-files.c index f38912c..018011f 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 220f341..3e05260 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 86be634..a8e68ce 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 55e7d21..6e196e0 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" diff --git a/builtin/difftool.c b/builtin/difftool.c index 0f5eae9..a3c72b8 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 70aff51..4693d18 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>]"), @@ -137,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 444f41c..dc5a9d3 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1236,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; @@ -1287,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; @@ -1625,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); @@ -1633,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), @@ -2220,7 +2210,7 @@ static int parse_mapped_oid_hex(const char *hex, struct object_id *oid, const ch * * idnum ::= ':' bigint; * - * Return the first character after the value in *endptr. + * Update *endptr to point to the first character after the value. * * Complain if the following character is not what is expected, * either a space or end of the string. @@ -2253,8 +2243,8 @@ static uintmax_t parse_mark_ref_eol(const char *p) } /* - * Parse the mark reference, demanding a trailing space. Return a - * pointer to the space. + * Parse the mark reference, demanding a trailing space. Update *p to + * point to the first character after the space. */ static uintmax_t parse_mark_ref_space(const char **p) { @@ -2268,15 +2258,67 @@ static uintmax_t parse_mark_ref_space(const char **p) return mark; } +/* + * Parse the path string into the strbuf. The path can either be quoted with + * escape sequences or unquoted without escape sequences. Unquoted strings may + * contain spaces only if `is_last_field` is nonzero; otherwise, it stops + * parsing at the first space. + */ +static void parse_path(struct strbuf *sb, const char *p, const char **endp, + int is_last_field, const char *field) +{ + if (*p == '"') { + if (unquote_c_style(sb, p, endp)) + die("Invalid %s: %s", field, command_buf.buf); + if (strlen(sb->buf) != sb->len) + die("NUL in %s: %s", field, command_buf.buf); + } else { + /* + * Unless we are parsing the last field of a line, + * SP is the end of this field. + */ + *endp = is_last_field + ? p + strlen(p) + : strchrnul(p, ' '); + strbuf_add(sb, p, *endp - p); + } +} + +/* + * Parse the path string into the strbuf, and complain if this is not the end of + * the string. Unquoted strings may contain spaces. + */ +static void parse_path_eol(struct strbuf *sb, const char *p, const char *field) +{ + const char *end; + + parse_path(sb, p, &end, 1, field); + if (*end) + die("Garbage after %s: %s", field, command_buf.buf); +} + +/* + * Parse the path string into the strbuf, and ensure it is followed by a space. + * Unquoted strings may not contain spaces. Update *endp to point to the first + * character after the space. + */ +static void parse_path_space(struct strbuf *sb, const char *p, + const char **endp, const char *field) +{ + parse_path(sb, p, endp, 0, field); + if (**endp != ' ') + die("Missing space after %s: %s", field, command_buf.buf); + (*endp)++; +} + static void file_change_m(const char *p, struct branch *b) { - static struct strbuf uq = STRBUF_INIT; - const char *endp; + static struct strbuf path = STRBUF_INIT; struct object_entry *oe; 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) { @@ -2308,16 +2350,12 @@ static void file_change_m(const char *p, struct branch *b) die("Missing space after SHA1: %s", command_buf.buf); } - strbuf_reset(&uq); - if (!unquote_c_style(&uq, p, &endp)) { - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } + strbuf_reset(&path); + parse_path_eol(&path, p, "path"); /* Git does not track empty, non-toplevel directories. */ - if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) { - tree_content_remove(&b->branch_tree, p, NULL, 0); + if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *path.buf) { + tree_content_remove(&b->branch_tree, path.buf, NULL, 0); return; } @@ -2338,10 +2376,6 @@ static void file_change_m(const char *p, struct branch *b) if (S_ISDIR(mode)) die("Directories cannot be specified 'inline': %s", command_buf.buf); - if (p != uq.buf) { - strbuf_addstr(&uq, p); - p = uq.buf; - } while (read_next_command() != EOF) { const char *v; if (skip_prefix(command_buf.buf, "cat-blob ", &v)) @@ -2367,74 +2401,48 @@ static void file_change_m(const char *p, struct branch *b) command_buf.buf); } - if (!*p) { + if (!*path.buf) { tree_content_replace(&b->branch_tree, &oid, mode, NULL); return; } - tree_content_set(&b->branch_tree, p, &oid, mode, NULL); + tree_content_set(&b->branch_tree, path.buf, &oid, mode, NULL); } static void file_change_d(const char *p, struct branch *b) { - static struct strbuf uq = STRBUF_INIT; - const char *endp; + static struct strbuf path = STRBUF_INIT; - strbuf_reset(&uq); - if (!unquote_c_style(&uq, p, &endp)) { - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } - tree_content_remove(&b->branch_tree, p, NULL, 1); + strbuf_reset(&path); + parse_path_eol(&path, p, "path"); + tree_content_remove(&b->branch_tree, path.buf, NULL, 1); } -static void file_change_cr(const char *s, struct branch *b, int rename) +static void file_change_cr(const char *p, struct branch *b, int rename) { - const char *d; - static struct strbuf s_uq = STRBUF_INIT; - static struct strbuf d_uq = STRBUF_INIT; - const char *endp; + static struct strbuf source = STRBUF_INIT; + static struct strbuf dest = STRBUF_INIT; struct tree_entry leaf; - strbuf_reset(&s_uq); - if (!unquote_c_style(&s_uq, s, &endp)) { - if (*endp != ' ') - die("Missing space after source: %s", command_buf.buf); - } else { - endp = strchr(s, ' '); - if (!endp) - die("Missing space after source: %s", command_buf.buf); - strbuf_add(&s_uq, s, endp - s); - } - s = s_uq.buf; - - endp++; - if (!*endp) - die("Missing dest: %s", command_buf.buf); - - d = endp; - strbuf_reset(&d_uq); - if (!unquote_c_style(&d_uq, d, &endp)) { - if (*endp) - die("Garbage after dest in: %s", command_buf.buf); - d = d_uq.buf; - } + strbuf_reset(&source); + parse_path_space(&source, p, &p, "source"); + strbuf_reset(&dest); + parse_path_eol(&dest, p, "dest"); memset(&leaf, 0, sizeof(leaf)); if (rename) - tree_content_remove(&b->branch_tree, s, &leaf, 1); + tree_content_remove(&b->branch_tree, source.buf, &leaf, 1); else - tree_content_get(&b->branch_tree, s, &leaf, 1); + tree_content_get(&b->branch_tree, source.buf, &leaf, 1); if (!leaf.versions[1].mode) - die("Path %s not in branch", s); - if (!*d) { /* C "path/to/subdir" "" */ + die("Path %s not in branch", source.buf); + if (!*dest.buf) { /* C "path/to/subdir" "" */ tree_content_replace(&b->branch_tree, &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); return; } - tree_content_set(&b->branch_tree, d, + tree_content_set(&b->branch_tree, dest.buf, &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); @@ -2442,7 +2450,6 @@ static void file_change_cr(const char *s, struct branch *b, int rename) static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout) { - static struct strbuf uq = STRBUF_INIT; struct object_entry *oe; struct branch *s; struct object_id oid, commit_oid; @@ -2507,10 +2514,6 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa die("Invalid ref name or SHA1 expression: %s", p); if (inline_data) { - if (p != uq.buf) { - strbuf_addstr(&uq, p); - p = uq.buf; - } read_next_command(); parse_and_store_blob(&last_blob, &oid, 0); } else if (oe) { @@ -2809,8 +2812,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; @@ -3163,6 +3165,7 @@ static void print_ls(int mode, const unsigned char *hash, const char *path) static void parse_ls(const char *p, struct branch *b) { + static struct strbuf path = STRBUF_INIT; struct tree_entry *root = NULL; struct tree_entry leaf = {NULL}; @@ -3179,17 +3182,9 @@ static void parse_ls(const char *p, struct branch *b) root->versions[1].mode = S_IFDIR; load_tree(root); } - if (*p == '"') { - static struct strbuf uq = STRBUF_INIT; - const char *endp; - strbuf_reset(&uq); - if (unquote_c_style(&uq, p, &endp)) - die("Invalid path: %s", command_buf.buf); - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } - tree_content_get(root, p, &leaf, 1); + strbuf_reset(&path); + parse_path_eol(&path, p, "path"); + tree_content_get(root, path.buf, &leaf, 1); /* * A directory in preparation would have a sha1 of zero * until it is saved. Save, for simplicity. @@ -3197,7 +3192,7 @@ static void parse_ls(const char *p, struct branch *b) if (S_ISDIR(leaf.versions[1].mode)) store_tree(&leaf); - print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p); + print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, path.buf); if (leaf.tree) release_tree_content_recursive(leaf.tree); if (!b || root != &b->branch_tree) diff --git a/builtin/fetch.c b/builtin/fetch.c index fd134ba..5857d86 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; @@ -134,6 +138,7 @@ static int git_fetch_config(const char *k, const char *v, int r = git_config_bool(k, v) ? RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; fetch_config->recurse_submodules = r; + return 0; } if (!strcmp(k, "submodule.fetchjobs")) { @@ -444,9 +449,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 +982,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 +1657,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 +1717,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 +1780,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 +2138,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; @@ -2333,11 +2343,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 350bfa6..919282e 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 6119259..f892487 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 5d01db5..1593713 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" diff --git a/builtin/gc.c b/builtin/gc.c index 7c11d5e..d187cec 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -180,13 +180,51 @@ static void gc_config(void) git_config(git_default_config, NULL); } -struct maintenance_run_opts; +enum schedule_priority { + SCHEDULE_NONE = 0, + SCHEDULE_WEEKLY = 1, + SCHEDULE_DAILY = 2, + SCHEDULE_HOURLY = 3, +}; + +static enum schedule_priority parse_schedule(const char *value) +{ + if (!value) + return SCHEDULE_NONE; + if (!strcasecmp(value, "hourly")) + return SCHEDULE_HOURLY; + if (!strcasecmp(value, "daily")) + return SCHEDULE_DAILY; + if (!strcasecmp(value, "weekly")) + return SCHEDULE_WEEKLY; + return SCHEDULE_NONE; +} + +struct maintenance_run_opts { + int auto_flag; + int quiet; + enum schedule_priority schedule; +}; + +static int pack_refs_condition(void) +{ + /* + * The auto-repacking logic for refs is handled by the ref backends and + * exposed via `git pack-refs --auto`. We thus always return truish + * here and let the backend decide for us. + */ + return 1; +} + static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts) { struct child_process cmd = CHILD_PROCESS_INIT; cmd.git_cmd = 1; strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL); + if (opts->auto_flag) + strvec_push(&cmd.args, "--auto"); + return run_command(&cmd); } @@ -547,7 +585,7 @@ done: return ret; } -static void gc_before_repack(void) +static void gc_before_repack(struct maintenance_run_opts *opts) { /* * We may be called twice, as both the pre- and @@ -558,7 +596,7 @@ static void gc_before_repack(void) if (done++) return; - if (pack_refs && maintenance_task_pack_refs(NULL)) + if (pack_refs && maintenance_task_pack_refs(opts)) die(FAILED_RUN, "pack-refs"); if (prune_reflogs) { @@ -574,7 +612,6 @@ static void gc_before_repack(void) int cmd_gc(int argc, const char **argv, const char *prefix) { int aggressive = 0; - int auto_gc = 0; int quiet = 0; int force = 0; const char *name; @@ -583,6 +620,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) int keep_largest_pack = -1; timestamp_t dummy; struct child_process rerere_cmd = CHILD_PROCESS_INIT; + struct maintenance_run_opts opts = {0}; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), @@ -593,7 +631,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) 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"), + OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), OPT_BOOL_F(0, "force", &force, N_("force running gc even if there may be another gc running"), @@ -638,7 +676,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (quiet) strvec_push(&repack, "-q"); - if (auto_gc) { + if (opts.auto_flag) { /* * Auto-gc should be least intrusive as possible. */ @@ -663,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (lock_repo_for_gc(force, &pid)) return 0; - gc_before_repack(); /* dies on failure */ + gc_before_repack(&opts); /* dies on failure */ delete_tempfile(&pidfile); /* @@ -688,7 +726,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) name = lock_repo_for_gc(force, &pid); if (name) { - if (auto_gc) + if (opts.auto_flag) return 0; /* be quiet on --auto */ die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"), name, (uintmax_t)pid); @@ -703,7 +741,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) atexit(process_log_file_at_exit); } - gc_before_repack(); + gc_before_repack(&opts); if (!repository_format_precious_objects) { struct child_process repack_cmd = CHILD_PROCESS_INIT; @@ -758,7 +796,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, NULL); - if (auto_gc && too_many_loose_objects()) + if (opts.auto_flag && too_many_loose_objects()) warning(_("There are too many unreachable loose objects; " "run 'git prune' to remove them.")); @@ -773,26 +811,6 @@ static const char *const builtin_maintenance_run_usage[] = { NULL }; -enum schedule_priority { - SCHEDULE_NONE = 0, - SCHEDULE_WEEKLY = 1, - SCHEDULE_DAILY = 2, - SCHEDULE_HOURLY = 3, -}; - -static enum schedule_priority parse_schedule(const char *value) -{ - if (!value) - return SCHEDULE_NONE; - if (!strcasecmp(value, "hourly")) - return SCHEDULE_HOURLY; - if (!strcasecmp(value, "daily")) - return SCHEDULE_DAILY; - if (!strcasecmp(value, "weekly")) - return SCHEDULE_WEEKLY; - return SCHEDULE_NONE; -} - static int maintenance_opt_schedule(const struct option *opt, const char *arg, int unset) { @@ -809,12 +827,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg, return 0; } -struct maintenance_run_opts { - int auto_flag; - int quiet; - enum schedule_priority schedule; -}; - /* Remember to update object flag allocation in object.h */ #define SEEN (1u<<0) @@ -1296,7 +1308,7 @@ static struct maintenance_task tasks[] = { [TASK_PACK_REFS] = { "pack-refs", maintenance_task_pack_refs, - NULL, + pack_refs_condition, }, }; @@ -1543,19 +1555,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); + CONFIG_REGEX_NONE, NULL, 0); + free(global_config_file); if (rc) die(_("unable to add '%s' value of '%s'"), @@ -1612,18 +1623,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_file, key, NULL, maintpath, NULL, 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)) diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c index 20d0dfe..66a7389 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 fe78d4c..5777ba8 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -9,15 +9,11 @@ #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" @@ -531,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); @@ -575,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); @@ -671,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); @@ -715,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); diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 5ffec99..82ca6d2 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 09b51a6..5234693 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 dda94a9..856428f 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; @@ -1257,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); @@ -1264,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(); @@ -1524,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")); } @@ -1566,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); @@ -1785,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 cb727c8..0170469 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 033bd15..8768bfe 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 (<key>|<keyAlias>)[(=|:)<value>])...]\n" + " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n" " [--parse] [<file>...]"), NULL }; @@ -91,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; @@ -132,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 ba775d7..c0a8bb9 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" @@ -594,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; @@ -1296,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); @@ -1363,12 +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, 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); @@ -1623,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: @@ -1656,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; @@ -1667,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 { @@ -1702,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 { @@ -1723,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 { diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a0229c3..6eeb5cb 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" @@ -270,7 +266,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce, struct strbuf sb = STRBUF_INIT; while (strbuf_expand_step(&sb, &format)) { - const char *end; size_t len; struct stat st; @@ -278,12 +273,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce, strbuf_addch(&sb, '%'); else if ((len = strbuf_expand_literal(&sb, format))) format += len; - else if (*format != '(') - die(_("bad ls-files format: element '%s' " - "does not start with '('"), format); - else if (!(end = strchr(format + 1, ')'))) - die(_("bad ls-files format: element '%s' " - "does not end in ')'"), format); else if (skip_prefix(format, "(objectmode)", &format)) strbuf_addf(&sb, "%06o", ce->ce_mode); else if (skip_prefix(format, "(objectname)", &format)) @@ -312,8 +301,7 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce, else if (skip_prefix(format, "(path)", &format)) write_name_to_buf(&sb, fullname); else - die(_("bad ls-files format: %%%.*s"), - (int)(end - format + 1), format); + strbuf_expand_bad_format(format, "ls-files"); } strbuf_addch(&sb, line_terminator); fwrite(sb.buf, sb.len, 1, stdout); diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index fc76575..e8d65eb 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 209d2dc..7bf84b2 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" @@ -102,19 +100,12 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, return 0; while (strbuf_expand_step(&sb, &format)) { - const char *end; size_t len; if (skip_prefix(format, "%", &format)) strbuf_addch(&sb, '%'); else if ((len = strbuf_expand_literal(&sb, format))) format += len; - else if (*format != '(') - die(_("bad ls-tree format: element '%s' " - "does not start with '('"), format); - else if (!(end = strchr(format + 1, ')'))) - die(_("bad ls-tree format: element '%s' " - "does not end in ')'"), format); else if (skip_prefix(format, "(objectmode)", &format)) strbuf_addf(&sb, "%06o", mode); else if (skip_prefix(format, "(objecttype)", &format)) @@ -137,8 +128,7 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, strbuf_setlen(base, baselen); strbuf_release(&sbuf); } else - die(_("bad ls-tree format: %%%.*s"), - (int)(end - format + 1), format); + strbuf_expand_bad_format(format, "ls-tree"); } strbuf_addch(&sb, options->null_termination ? '\0' : '\n'); fwrite(sb.buf, sb.len, 1, stdout); @@ -377,6 +367,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; + struct object_context obj_context; int ret; git_config(git_default_config, NULL); @@ -408,7 +399,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 53b55dd..53a2264 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 e68b7fe..5a8e729 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 832c93d..1f98733 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "abspath.h" +#include "diff.h" #include "hex.h" #include "object-name.h" #include "object-store.h" @@ -28,6 +29,30 @@ 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 }; @@ -48,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")), diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 3366699..c2ce044 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 a35e045..8bdb439 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -13,7 +13,6 @@ #include "parse-options.h" #include "repository.h" #include "blob.h" -#include "exec-cmd.h" #include "merge-blobs.h" #include "quote.h" #include "tree.h" @@ -430,41 +429,56 @@ static int real_merge(struct merge_tree_options *o, struct merge_options opt; copy_merge_options(&opt, &o->merge_options); - 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")); - 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); @@ -549,7 +563,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) PARSE_OPT_NONEG), OPT_STRING(0, "merge-base", &merge_base, - N_("commit"), + N_("tree-ish"), N_("specify a merge-base for the merge")), OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), N_("option for selected merge strategy")), @@ -577,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 d748d46..6f4fec8 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" @@ -196,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); @@ -480,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); } @@ -682,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; @@ -826,7 +822,7 @@ static const char scissors_editor_comment[] = N_("An empty message aborts the commit.\n"); static const char no_scissors_editor_comment[] = -N_("Lines starting with '%c' will be ignored, and an empty message aborts\n" +N_("Lines starting with '%s' will be ignored, and an empty message aborts\n" "the commit.\n"); static void write_merge_heads(struct commit_list *); @@ -857,19 +853,19 @@ static void prepare_to_commit(struct commit_list *remoteheads) strbuf_addch(&msg, '\n'); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { wt_status_append_cut_line(&msg); - strbuf_commented_addf(&msg, comment_line_char, "\n"); + strbuf_commented_addf(&msg, comment_line_str, "\n"); } - strbuf_commented_addf(&msg, comment_line_char, + strbuf_commented_addf(&msg, comment_line_str, _(merge_editor_comment)); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) - strbuf_commented_addf(&msg, comment_line_char, + strbuf_commented_addf(&msg, comment_line_str, _(scissors_editor_comment)); else - strbuf_commented_addf(&msg, comment_line_char, - _(no_scissors_editor_comment), comment_line_char); + strbuf_commented_addf(&msg, comment_line_str, + _(no_scissors_editor_comment), comment_line_str); } 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, @@ -1319,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"), @@ -1328,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; } @@ -1517,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); } @@ -1567,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; } @@ -1631,7 +1634,7 @@ 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; /* @@ -1639,9 +1642,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ - common_one = repo_get_merge_bases(the_repository, - head_commit, - j->item); + 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)) { @@ -1659,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); @@ -1745,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 d8e0b5a..4767f1a 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 c596515..22e64fc 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 2dd1807..ad9930c 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 9f38863..cb01130 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" @@ -181,7 +179,7 @@ static void write_commented_object(int fd, const struct object_id *object) if (strbuf_read(&buf, show.out, 0) < 0) die_errno(_("could not read 'show' output")); - strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char); + strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_str); write_or_die(fd, cbuf.buf, cbuf.len); strbuf_release(&cbuf); @@ -209,10 +207,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data * copy_obj_to_fd(fd, old_note); strbuf_addch(&buf, '\n'); - strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str); strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)), - comment_line_char); - strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); + comment_line_str); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str); write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); @@ -225,7 +223,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * die(_("please supply the note contents using either -m or -F option")); } if (d->stripspace) - strbuf_stripspace(&d->buf, comment_line_char); + strbuf_stripspace(&d->buf, comment_line_str); } } @@ -266,7 +264,7 @@ static void concat_messages(struct note_data *d) if ((d->stripspace == UNSPECIFIED && d->messages[i]->stripspace == STRIPSPACE) || d->stripspace == STRIPSPACE) - strbuf_stripspace(&d->buf, 0); + strbuf_stripspace(&d->buf, NULL); strbuf_reset(&msg); } strbuf_release(&msg); @@ -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 89a8b5a..baf0090 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) || @@ -3930,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 && @@ -3943,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); @@ -4305,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); @@ -4517,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/pack-refs.c b/builtin/pack-refs.c index bcf383c..db40825 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -7,24 +7,28 @@ #include "revision.h" static char const * const pack_refs_usage[] = { - N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"), + N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"), NULL }; int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - unsigned int flags = PACK_REFS_PRUNE; - static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT; - static struct string_list included_refs = STRING_LIST_INIT_NODUP; - struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes, - .includes = &included_refs, - .flags = flags }; - static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP; + struct ref_exclusions excludes = REF_EXCLUSIONS_INIT; + struct string_list included_refs = STRING_LIST_INIT_NODUP; + struct pack_refs_opts pack_refs_opts = { + .exclusions = &excludes, + .includes = &included_refs, + .flags = PACK_REFS_PRUNE, + }; + struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP; struct string_list_item *item; + int pack_all = 0; + int ret; struct option opts[] = { - OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL), + OPT_BOOL(0, "all", &pack_all, N_("pack everything")), OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), + OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO), OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"), N_("references to include")), OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"), @@ -38,11 +42,16 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) for_each_string_list_item(item, &option_excluded_refs) add_ref_exclusion(pack_refs_opts.exclusions, item->string); - if (pack_refs_opts.flags & PACK_REFS_ALL) + if (pack_all) string_list_append(pack_refs_opts.includes, "*"); if (!pack_refs_opts.includes->nr) string_list_append(pack_refs_opts.includes, "refs/tags/*"); - return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts); + ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts); + + clear_ref_exclusions(&excludes); + string_list_clear(&included_refs, 0); + string_list_clear(&option_excluded_refs, 0); + return ret; } diff --git a/builtin/pull.c b/builtin/pull.c index be2b2c9..72cbb76 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 2e70838..2fbb31c 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 e455a47..f02cbac 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 8196ca9..6f89cec 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; @@ -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 043c65d..891f284 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" @@ -62,7 +58,7 @@ enum empty_type { EMPTY_UNSPECIFIED = -1, EMPTY_DROP, EMPTY_KEEP, - EMPTY_ASK + EMPTY_STOP }; enum action { @@ -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, \ @@ -209,7 +204,7 @@ static int edit_todo_file(unsigned flags) if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error_errno(_("could not read '%s'."), todo_file); - strbuf_stripspace(&todo_list.buf, comment_line_char); + strbuf_stripspace(&todo_list.buf, comment_line_str); res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) @@ -520,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 @@ -572,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; @@ -593,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); @@ -604,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; @@ -616,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); @@ -630,7 +610,7 @@ static int run_am(struct rebase_options *opts) status = error_errno(_("could not open '%s' for writing"), rebased_patches); free(rebased_patches); - strvec_clear(&am.args); + child_process_clear(&am); return status; } @@ -643,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)); @@ -653,7 +638,7 @@ static int run_am(struct rebase_options *opts) struct reset_head_opts ropts = { 0 }; unlink(rebased_patches); free(rebased_patches); - strvec_clear(&am.args); + child_process_clear(&am); ropts.oid = &opts->orig_head->object.oid; ropts.branch = opts->head_name; @@ -666,23 +651,21 @@ 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) { status = error_errno(_("could not open '%s' for reading"), rebased_patches); free(rebased_patches); - strvec_clear(&am.args); + child_process_clear(&am); return status; } 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"); @@ -710,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); @@ -879,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; @@ -898,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 @@ -963,10 +945,14 @@ static enum empty_type parse_empty_value(const char *value) return EMPTY_DROP; else if (!strcasecmp(value, "keep")) return EMPTY_KEEP; - else if (!strcasecmp(value, "ask")) - return EMPTY_ASK; + else if (!strcasecmp(value, "stop")) + return EMPTY_STOP; + else if (!strcasecmp(value, "ask")) { + warning(_("--empty=ask is deprecated; use '--empty=stop' instead.")); + return EMPTY_STOP; + } - die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value); + die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value); } static int parse_opt_keep_empty(const struct option *opt, const char *arg, @@ -1145,7 +1131,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|stop)", N_("how to handle commits that become empty"), PARSE_OPT_NONEG, parse_opt_empty), OPT_CALLBACK_F('k', "keep-empty", &options, NULL, @@ -1266,7 +1252,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 " @@ -1405,7 +1391,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; } @@ -1508,8 +1493,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) @@ -1529,10 +1512,13 @@ 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")) @@ -1562,7 +1548,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (options.empty == EMPTY_UNSPECIFIED) { if (options.flags & REBASE_INTERACTIVE_EXPLICIT) - options.empty = EMPTY_ASK; + options.empty = EMPTY_STOP; else if (options.exec.nr > 0) options.empty = EMPTY_KEEP; else diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 8c4f0cb..e8d7df1 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"; @@ -2601,17 +2585,16 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (auto_gc) { struct child_process proc = CHILD_PROCESS_INIT; - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.git_cmd = proc.close_object_store = 1; - strvec_pushl(&proc.args, "gc", "--auto", "--quiet", - NULL); - - if (!start_command(&proc)) { - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - finish_command(&proc); + if (prepare_auto_maintenance(1, &proc)) { + proc.no_stdin = 1; + proc.stdout_to_stderr = 1; + proc.err = use_sideband ? -1 : 0; + + if (!start_command(&proc)) { + if (use_sideband) + copy_to_sideband(proc.err, -1, NULL); + finish_command(&proc); + } } } if (auto_update_server_info) diff --git a/builtin/reflog.c b/builtin/reflog.c index 6e490f8..060eb33 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,6 +247,29 @@ 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 }; @@ -248,7 +279,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) 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"), @@ -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 d91bbe7..8412d12 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 edaee4d..15e4ccc 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" @@ -315,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; } @@ -1203,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())) diff --git a/builtin/replay.c b/builtin/replay.c new file mode 100644 index 0000000..6bc4b47 --- /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 07a9d37..b2efc6f 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 4b018d2..1d62ff6 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 181353d..7780372 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" @@ -221,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) @@ -547,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")) { @@ -755,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 fde8861..624182e 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 e6f9a1a..53935d2 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" @@ -45,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts) return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage; } +enum empty_action { + EMPTY_COMMIT_UNSPECIFIED = -1, + STOP_ON_EMPTY_COMMIT, /* output errors and stop in the middle of a cherry-pick */ + DROP_EMPTY_COMMIT, /* skip with a notice message */ + KEEP_EMPTY_COMMIT, /* keep recording as empty commits */ +}; + +static int parse_opt_empty(const struct option *opt, const char *arg, int unset) +{ + int *opt_value = opt->value; + + BUG_ON_OPT_NEG(unset); + + if (!strcmp(arg, "stop")) + *opt_value = STOP_ON_EMPTY_COMMIT; + else if (!strcmp(arg, "drop")) + *opt_value = DROP_EMPTY_COMMIT; + else if (!strcmp(arg, "keep")) + *opt_value = KEEP_EMPTY_COMMIT; + else + return error(_("invalid value for '%s': '%s'"), "--empty", arg); + + return 0; +} + static int option_parse_m(const struct option *opt, const char *arg, int unset) { @@ -87,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, const char * const * usage_str = revert_or_cherry_pick_usage(opts); const char *me = action_name(opts); const char *cleanup_arg = NULL; + enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED; int cmd = 0; struct option base_options[] = { OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'), @@ -116,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")), OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), - OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")), + OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")), + OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)", + N_("how to handle commits that become empty"), + PARSE_OPT_NONEG, parse_opt_empty), OPT_END(), }; options = parse_options_concat(options, cp_extra); @@ -136,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; + if (opts->action == REPLAY_PICK) { + opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT); + opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT); + } + /* implies allow_empty */ if (opts->keep_redundant_commits) opts->allow_empty = 1; @@ -169,6 +201,8 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, "--ff", opts->allow_ff, "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE, "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE, + "--keep-redundant-commits", opts->keep_redundant_commits, + "--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED, NULL); } diff --git a/builtin/rm.c b/builtin/rm.c index dff819a..fd130ce 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 cd6d9e4..3df9eaa 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 1307ed2..3c7cd2d 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 7aac525..1c15421 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -6,7 +6,6 @@ #include "object-name.h" #include "object-store-ll.h" #include "object.h" -#include "tag.h" #include "string-list.h" #include "parse-options.h" @@ -173,7 +172,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, while (*refs) { struct object_id oid; - if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) && + if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && !read_ref(*refs, &oid)) { show_one(show_one_opts, *refs, &oid); } @@ -239,7 +238,7 @@ static int cmd_show_ref__exists(const char **refs) if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, &unused_oid, &unused_referent, &unused_type, &failure_errno)) { - if (failure_errno == ENOENT) { + if (failure_errno == ENOENT || failure_errno == EISDIR) { error(_("reference does not exist")); ret = 2; } else { @@ -315,9 +314,9 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, show_ref_options, show_ref_usage, 0); - if ((!!exclude_existing_opts.enabled + !!verify + !!exists) > 1) - die(_("only one of '%s', '%s' or '%s' can be given"), - "--exclude-existing", "--verify", "--exists"); + die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing", + verify, "--verify", + exists, "--exists"); if (exclude_existing_opts.enabled) return cmd_show_ref__exclude_existing(&exclude_existing_opts); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 5c8ffb1..0f52e25 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 4a6771c..062be1f 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; @@ -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; @@ -1365,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; } @@ -1556,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/stripspace.c b/builtin/stripspace.c index 7b700a9..e5626e5 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -13,7 +13,7 @@ static void comment_lines(struct strbuf *buf) size_t len; msg = strbuf_detach(buf, &len); - strbuf_add_commented_lines(buf, msg, len, comment_line_char); + strbuf_add_commented_lines(buf, msg, len, comment_line_str); free(msg); } @@ -59,7 +59,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS) strbuf_stripspace(&buf, - mode == STRIP_COMMENTS ? comment_line_char : '\0'); + mode == STRIP_COMMENTS ? comment_line_str : NULL); else comment_lines(&buf); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index cce4645..5a71b1e 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" @@ -304,6 +303,9 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, struct child_process cp = CHILD_PROCESS_INIT; char *displaypath; + if (validate_submodule_path(path) < 0) + exit(128); + displaypath = get_submodule_displaypath(path, info->prefix, info->super_prefix); @@ -635,6 +637,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, .free_removed_argv_elements = 1, }; + if (validate_submodule_path(path) < 0) + exit(128); + if (!submodule_from_path(the_repository, null_oid(), path)) die(_("no submodule mapping found in .gitmodules for path '%s'"), path); @@ -1239,6 +1244,9 @@ static void sync_submodule(const char *path, const char *prefix, if (!is_submodule_active(the_repository, path)) return; + if (validate_submodule_path(path) < 0) + exit(128); + sub = submodule_from_path(the_repository, null_oid(), path); if (sub && sub->url) { @@ -1284,7 +1292,7 @@ static void sync_submodule(const char *path, const char *prefix, submodule_to_gitdir(&sb, path); strbuf_addstr(&sb, "/config"); - if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url)) + if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url)) die(_("failed to update remote for submodule '%s'"), path); @@ -1382,6 +1390,9 @@ static void deinit_submodule(const char *path, const char *prefix, struct strbuf sb_config = STRBUF_INIT; char *sub_git_dir = xstrfmt("%s/.git", path); + if (validate_submodule_path(path) < 0) + exit(128); + sub = submodule_from_path(the_repository, null_oid(), path); if (!sub || !sub->name) @@ -1663,16 +1674,42 @@ static char *clone_submodule_sm_gitdir(const char *name) return sm_gitdir; } +static int dir_contains_only_dotgit(const char *path) +{ + DIR *dir = opendir(path); + struct dirent *e; + int ret = 1; + + if (!dir) + return 0; + + e = readdir_skip_dot_and_dotdot(dir); + if (!e) + ret = 0; + else if (strcmp(DEFAULT_GIT_DIR_ENVIRONMENT, e->d_name) || + (e = readdir_skip_dot_and_dotdot(dir))) { + error("unexpected item '%s' in '%s'", e->d_name, path); + ret = 0; + } + + closedir(dir); + return ret; +} + static int clone_submodule(const struct module_clone_data *clone_data, struct string_list *reference) { char *p; char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name); char *sm_alternate = NULL, *error_strategy = NULL; + struct stat st; struct child_process cp = CHILD_PROCESS_INIT; const char *clone_data_path = clone_data->path; char *to_free = NULL; + if (validate_submodule_path(clone_data_path) < 0) + exit(128); + if (!is_absolute_path(clone_data->path)) clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(), clone_data->path); @@ -1682,6 +1719,10 @@ static int clone_submodule(const struct module_clone_data *clone_data, "git dir"), sm_gitdir); if (!file_exists(sm_gitdir)) { + if (clone_data->require_init && !stat(clone_data_path, &st) && + !is_empty_dir(clone_data_path)) + die(_("directory not empty: '%s'"), clone_data_path); + if (safe_create_leading_directories_const(sm_gitdir) < 0) die(_("could not create directory '%s'"), sm_gitdir); @@ -1726,10 +1767,18 @@ static int clone_submodule(const struct module_clone_data *clone_data, if(run_command(&cp)) die(_("clone of '%s' into submodule path '%s' failed"), clone_data->url, clone_data_path); + + if (clone_data->require_init && !stat(clone_data_path, &st) && + !dir_contains_only_dotgit(clone_data_path)) { + char *dot_git = xstrfmt("%s/.git", clone_data_path); + unlink(dot_git); + free(dot_git); + die(_("directory not empty: '%s'"), clone_data_path); + } } else { char *path; - if (clone_data->require_init && !access(clone_data_path, X_OK) && + if (clone_data->require_init && !stat(clone_data_path, &st) && !is_empty_dir(clone_data_path)) die(_("directory not empty: '%s'"), clone_data_path); if (safe_create_leading_directories_const(clone_data_path) < 0) @@ -1739,6 +1788,23 @@ static int clone_submodule(const struct module_clone_data *clone_data, free(path); } + /* + * We already performed this check at the beginning of this function, + * before cloning the objects. This tries to detect racy behavior e.g. + * in parallel clones, where another process could easily have made the + * gitdir nested _after_ it was created. + * + * To prevent further harm coming from this unintentionally-nested + * gitdir, let's disable it by deleting the `HEAD` file. + */ + if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) { + char *head = xstrfmt("%s/HEAD", sm_gitdir); + unlink(head); + free(head); + die(_("refusing to create/use '%s' in another submodule's " + "git dir"), sm_gitdir); + } + connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0); p = git_pathdup_submodule(clone_data_path, "config"); @@ -2518,6 +2584,9 @@ static int update_submodule(struct update_data *update_data) { int ret; + if (validate_submodule_path(update_data->sm_path) < 0) + return -1; + ret = determine_submodule_update_strategy(the_repository, update_data->just_cloned, update_data->sm_path, @@ -2625,12 +2694,21 @@ static int update_submodules(struct update_data *update_data) for (i = 0; i < suc.update_clone_nr; i++) { struct update_clone_data ucd = suc.update_clone[i]; - int code; + int code = 128; oidcpy(&update_data->oid, &ucd.oid); update_data->just_cloned = ucd.just_cloned; update_data->sm_path = ucd.sub->path; + /* + * Verify that the submodule path does not contain any + * symlinks; if it does, it might have been tampered with. + * TODO: allow exempting it via + * `safe.submodule.path` or something + */ + if (validate_submodule_path(update_data->sm_path) < 0) + goto fail; + code = ensure_core_worktree(update_data->sm_path); if (code) goto fail; @@ -3357,6 +3435,9 @@ static int module_add(int argc, const char **argv, const char *prefix) normalize_path_copy(add_data.sm_path, add_data.sm_path); strip_dir_trailing_slashes(add_data.sm_path); + if (validate_submodule_path(add_data.sm_path) < 0) + exit(128); + die_on_index_match(add_data.sm_path, force); die_on_repo_without_commits(add_data.sm_path); diff --git a/builtin/tag.c b/builtin/tag.c index 3918eac..9a33cb5 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,18 +152,52 @@ 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[] = N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be ignored.\n"); + "Lines starting with '%s' will be ignored.\n"); static const char tag_template_nocleanup[] = N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be kept; you may remove them" + "Lines starting with '%s' will be kept; you may remove them" " yourself if you want to.\n"); static int git_tag_config(const char *var, const char *value, @@ -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; } @@ -314,11 +328,11 @@ static void create_tag(const struct object_id *object, const char *object_ref, struct strbuf buf = STRBUF_INIT; strbuf_addch(&buf, '\n'); if (opt->cleanup_mode == CLEANUP_ALL) - strbuf_commented_addf(&buf, comment_line_char, - _(tag_template), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_str, + _(tag_template), tag, comment_line_str); else - strbuf_commented_addf(&buf, comment_line_char, - _(tag_template_nocleanup), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_str, + _(tag_template_nocleanup), tag, comment_line_str); write_or_die(fd, buf.buf, buf.len); strbuf_release(&buf); } @@ -333,7 +347,7 @@ static void create_tag(const struct object_id *object, const char *object_ref, if (opt->cleanup_mode != CLEANUP_NONE) strbuf_stripspace(buf, - opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0'); + opt->cleanup_mode == CLEANUP_ALL ? comment_line_str : NULL); if (!opt->message_given && !buf->len) die(_("no tag message?")); @@ -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 fef7423..f1c85a0 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" @@ -683,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-ref.c b/builtin/update-ref.c index c0c4e65..e46afbc 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -7,11 +7,10 @@ #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>]"), - N_("git update-ref [<options>] <refname> <new-val> [<old-val>]"), + N_("git update-ref [<options>] -d <refname> [<old-oid>]"), + N_("git update-ref [<options>] <refname> <new-oid> [<old-oid>]"), N_("git update-ref [<options>] --stdin [-z]"), NULL }; @@ -78,14 +77,14 @@ static char *parse_refname(const char **next) } /* - * The value being parsed is <oldvalue> (as opposed to <newvalue>; the + * The value being parsed is <old-oid> (as opposed to <new-oid>; the * difference affects which error messages are generated): */ #define PARSE_SHA1_OLD 0x01 /* * For backwards compatibility, accept an empty string for update's - * <newvalue> in binary mode to be equivalent to specifying zeros. + * <new-oid> in binary mode to be equivalent to specifying zeros. */ #define PARSE_SHA1_ALLOW_EMPTY 0x02 @@ -141,7 +140,7 @@ static int parse_next_oid(const char **next, const char *end, goto invalid; } else if (flags & PARSE_SHA1_ALLOW_EMPTY) { /* With -z, treat an empty value as all zeros: */ - warning("%s %s: missing <newvalue>, treating as zero", + warning("%s %s: missing <new-oid>, treating as zero", command, refname); oidclr(oid); } else { @@ -159,14 +158,14 @@ static int parse_next_oid(const char **next, const char *end, invalid: die(flags & PARSE_SHA1_OLD ? - "%s %s: invalid <oldvalue>: %s" : - "%s %s: invalid <newvalue>: %s", + "%s %s: invalid <old-oid>: %s" : + "%s %s: invalid <new-oid>: %s", command, refname, arg.buf); eof: die(flags & PARSE_SHA1_OLD ? - "%s %s: unexpected end of input when reading <oldvalue>" : - "%s %s: unexpected end of input when reading <newvalue>", + "%s %s: unexpected end of input when reading <old-oid>" : + "%s %s: unexpected end of input when reading <new-oid>", command, refname); } @@ -195,7 +194,7 @@ static void parse_cmd_update(struct ref_transaction *transaction, if (parse_next_oid(&next, end, &new_oid, "update", refname, PARSE_SHA1_ALLOW_EMPTY)) - die("update %s: missing <newvalue>", refname); + die("update %s: missing <new-oid>", refname); have_old = !parse_next_oid(&next, end, &old_oid, "update", refname, PARSE_SHA1_OLD); @@ -226,10 +225,10 @@ static void parse_cmd_create(struct ref_transaction *transaction, die("create: missing <ref>"); if (parse_next_oid(&next, end, &new_oid, "create", refname, 0)) - die("create %s: missing <newvalue>", refname); + die("create %s: missing <new-oid>", refname); if (is_null_oid(&new_oid)) - die("create %s: zero <newvalue>", refname); + die("create %s: zero <new-oid>", refname); if (*next != line_termination) die("create %s: extra input: %s", refname, next); @@ -261,7 +260,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction, have_old = 0; } else { if (is_null_oid(&old_oid)) - die("delete %s: zero <oldvalue>", refname); + die("delete %s: zero <old-oid>", refname); have_old = 1; } diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index 9b021ef..46d9327 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -8,6 +8,8 @@ #include "replace-object.h" #include "upload-pack.h" #include "serve.h" +#include "commit.h" +#include "environment.h" static const char * const upload_pack_usage[] = { N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" @@ -37,6 +39,8 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) packet_trace_identity("upload-pack"); disable_replace_refs(); + save_commit_buffer = 0; + xsetenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 0); argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0); diff --git a/builtin/var.c b/builtin/var.c index 8cf7dd9..cf55672 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 9680b58..0d2b9ae 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 d875327..c731e2f 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 62b7e26..7c6c725 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" \ @@ -365,12 +365,12 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir) if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare && git_config_set_multivar_in_file_gently( - to_file, "core.bare", NULL, "true", 0)) + to_file, "core.bare", NULL, "true", NULL, 0)) error(_("failed to unset '%s' in '%s'"), "core.bare", to_file); if (!git_configset_get(&cs, "core.worktree") && git_config_set_in_file_gently(to_file, - "core.worktree", NULL)) + "core.worktree", NULL, NULL)) error(_("failed to unset '%s' in '%s'"), "core.worktree", to_file); @@ -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; } @@ -660,7 +657,7 @@ static int can_use_local_refs(const struct add_opts *opts) strbuf_add_real_path(&path, get_worktree_git_dir(NULL)); strbuf_addstr(&path, "/HEAD"); strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, 0); + strbuf_stripspace(&contents, NULL); strbuf_strip_suffix(&contents, "\n"); warning(_("HEAD points to an invalid (or orphaned) reference.\n" @@ -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; |