summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c21
-rw-r--r--builtin/am.c95
-rw-r--r--builtin/blame.c36
-rw-r--r--builtin/branch.c30
-rw-r--r--builtin/cat-file.c56
-rw-r--r--builtin/checkout--worker.c4
-rw-r--r--builtin/checkout.c108
-rw-r--r--builtin/clean.c44
-rw-r--r--builtin/clone.c14
-rw-r--r--builtin/commit-graph.c6
-rw-r--r--builtin/commit.c17
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/credential.c2
-rw-r--r--builtin/describe.c6
-rw-r--r--builtin/diff-tree.c2
-rw-r--r--builtin/diff.c5
-rw-r--r--builtin/difftool.c18
-rw-r--r--builtin/fast-export.c46
-rw-r--r--builtin/fast-import.c12
-rw-r--r--builtin/fetch.c156
-rw-r--r--builtin/fmt-merge-msg.c4
-rw-r--r--builtin/for-each-ref.c10
-rw-r--r--builtin/fsck.c64
-rw-r--r--builtin/gc.c46
-rw-r--r--builtin/grep.c14
-rw-r--r--builtin/help.c3
-rw-r--r--builtin/index-pack.c8
-rw-r--r--builtin/init-db.c2
-rw-r--r--builtin/log.c32
-rw-r--r--builtin/ls-files.c28
-rw-r--r--builtin/ls-remote.c13
-rw-r--r--builtin/merge-file.c2
-rw-r--r--builtin/merge.c13
-rw-r--r--builtin/mktag.c3
-rw-r--r--builtin/multi-pack-index.c40
-rw-r--r--builtin/name-rev.c17
-rw-r--r--builtin/notes.c20
-rw-r--r--builtin/pack-objects.c15
-rw-r--r--builtin/prune.c20
-rw-r--r--builtin/pull.c52
-rw-r--r--builtin/push.c8
-rw-r--r--builtin/rebase.c348
-rw-r--r--builtin/receive-pack.c186
-rw-r--r--builtin/reflog.c224
-rw-r--r--builtin/remote.c114
-rw-r--r--builtin/repack.c296
-rw-r--r--builtin/replace.c3
-rw-r--r--builtin/reset.c123
-rw-r--r--builtin/rev-list.c4
-rw-r--r--builtin/rm.c7
-rw-r--r--builtin/send-pack.c4
-rw-r--r--builtin/shortlog.c3
-rw-r--r--builtin/show-branch.c16
-rw-r--r--builtin/sparse-checkout.c209
-rw-r--r--builtin/stash.c100
-rw-r--r--builtin/submodule--helper.c63
-rw-r--r--builtin/tag.c69
-rw-r--r--builtin/upload-archive.c5
-rw-r--r--builtin/var.c7
-rw-r--r--builtin/worktree.c33
60 files changed, 1745 insertions, 1163 deletions
diff --git a/builtin/add.c b/builtin/add.c
index ef6b619c45..84dff3e796 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -302,15 +302,11 @@ int interactive_add(const char **argv, const char *prefix, int patch)
static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = git_pathdup("ADD_EDIT.patch");
- const char *apply_argv[] = { "apply", "--recount", "--cached",
- NULL, NULL };
struct child_process child = CHILD_PROCESS_INIT;
struct rev_info rev;
int out;
struct stat st;
- apply_argv[3] = file;
-
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
@@ -338,7 +334,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
die(_("Empty patch. Aborted."));
child.git_cmd = 1;
- child.argv = apply_argv;
+ strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
+ NULL);
if (run_command(&child))
die(_("Could not apply '%s'"), file);
@@ -510,9 +507,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
add_interactive = 1;
if (add_interactive) {
if (show_only)
- die(_("--dry-run is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
if (pathspec_from_file)
- die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
exit(interactive_add(argv + 1, prefix, patch_interactive));
}
if (legacy_stash_p) {
@@ -529,7 +526,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (edit_interactive) {
if (pathspec_from_file)
- die(_("--pathspec-from-file is incompatible with --edit"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--edit");
return(edit_patch(argc, argv, prefix));
}
argc--;
@@ -541,10 +538,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
addremove = 0; /* "-u" was given but not "-A" */
if (addremove && take_worktree_changes)
- die(_("-A and -u are mutually incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "-A", "-u");
if (!show_only && ignore_missing)
- die(_("Option --ignore-missing can only be used together with --dry-run"));
+ die(_("the option '%s' requires '%s'"), "--ignore-missing", "--dry-run");
if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
chmod_arg[1] != 'x' || chmod_arg[2]))
@@ -569,14 +566,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (pathspec_from_file) {
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (require_pathspec && pathspec.nr == 0) {
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2348..b6be1f1cb1 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@ enum show_patch_type {
SHOW_PATCH_DIFF = 1,
};
+enum empty_action {
+ STOP_ON_EMPTY_COMMIT = 0, /* output errors and stop in the middle of an am session */
+ DROP_EMPTY_COMMIT, /* skip with a notice message, unless "--quiet" has been passed */
+ KEEP_EMPTY_COMMIT, /* keep recording as empty commits */
+};
+
struct am_state {
/* state directory path */
char *dir;
@@ -118,6 +124,7 @@ struct am_state {
int message_id;
int scissors; /* enum scissors_type */
int quoted_cr; /* enum quoted_cr_action */
+ int empty_type; /* enum empty_action */
struct strvec git_apply_opts;
const char *resolvemsg;
int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
return 0;
}
+static int am_option_parse_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 --empty: %s"), arg);
+
+ return 0;
+}
+
/**
* Returns path relative to the am_state directory.
*/
@@ -1126,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
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);
+
+ 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);
+
printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
}
@@ -1248,11 +1280,6 @@ static int parse_mail(struct am_state *state, const char *mail)
goto finish;
}
- if (is_empty_or_missing_file(am_path(state, "patch"))) {
- printf_ln(_("Patch is empty."));
- die_user_resolve(state);
- }
-
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
strbuf_stripspace(&msg, 0);
@@ -1763,6 +1790,7 @@ static void am_run(struct am_state *state, int resume)
while (state->cur <= state->last) {
const char *mail = am_path(state, msgnum(state));
int apply_status;
+ int to_keep;
reset_ident_date();
@@ -1792,8 +1820,29 @@ static void am_run(struct am_state *state, int resume)
if (state->interactive && do_interactive(state))
goto next;
+ to_keep = 0;
+ if (is_empty_or_missing_file(am_path(state, "patch"))) {
+ switch (state->empty_type) {
+ case DROP_EMPTY_COMMIT:
+ say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+ goto next;
+ break;
+ case KEEP_EMPTY_COMMIT:
+ to_keep = 1;
+ say(state, stdout, _("Creating an empty commit: %.*s"),
+ linelen(state->msg), state->msg);
+ break;
+ case STOP_ON_EMPTY_COMMIT:
+ printf_ln(_("Patch is empty."));
+ die_user_resolve(state);
+ break;
+ }
+ }
+
if (run_applypatch_msg_hook(state))
exit(1);
+ if (to_keep)
+ goto commit;
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
@@ -1827,6 +1876,7 @@ static void am_run(struct am_state *state, int resume)
die_user_resolve(state);
}
+commit:
do_commit(state);
next:
@@ -1856,19 +1906,24 @@ next:
/**
* Resume the current am session after patch application failure. The user did
* all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
*/
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
{
validate_resume_state(state);
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
if (!repo_index_has_changes(the_repository, NULL, NULL)) {
- printf_ln(_("No changes - did you forget to use 'git add'?\n"
- "If there is nothing left to stage, chances are that something else\n"
- "already introduced the same changes; you might want to skip this patch."));
- die_user_resolve(state);
+ if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
+ printf_ln(_("No changes - recorded it as an empty commit."));
+ } else {
+ printf_ln(_("No changes - did you forget to use 'git add'?\n"
+ "If there is nothing left to stage, chances are that something else\n"
+ "already introduced the same changes; you might want to skip this patch."));
+ die_user_resolve(state);
+ }
}
if (unmerged_cache()) {
@@ -2195,7 +2250,8 @@ enum resume_type {
RESUME_SKIP,
RESUME_ABORT,
RESUME_QUIT,
- RESUME_SHOW_PATCH
+ RESUME_SHOW_PATCH,
+ RESUME_ALLOW_EMPTY,
};
struct resume_mode {
@@ -2230,9 +2286,9 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar
}
if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
- return error(_("--show-current-patch=%s is incompatible with "
- "--show-current-patch=%s"),
- arg, valid_modes[resume->sub_mode]);
+ return error(_("options '%s=%s' and '%s=%s' "
+ "cannot be used together"),
+ "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]);
resume->mode = RESUME_SHOW_PATCH;
resume->sub_mode = new_value;
@@ -2348,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+ OPT_CMDMODE(0, "allow-empty", &resume.mode,
+ N_("record the empty patch as an empty commit"),
+ RESUME_ALLOW_EMPTY),
OPT_BOOL(0, "committer-date-is-author-date",
&state.committer_date_is_author_date,
N_("lie about committer date")),
@@ -2357,6 +2416,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+ N_("how to handle empty patches"),
+ PARSE_OPT_NONEG, am_option_parse_empty),
OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
N_("(internal use for git-rebase)")),
OPT_END()
@@ -2453,7 +2515,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
am_run(&state, 1);
break;
case RESUME_RESOLVED:
- am_resolve(&state);
+ case RESUME_ALLOW_EMPTY:
+ am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
break;
case RESUME_SKIP:
am_skip(&state);
diff --git a/builtin/blame.c b/builtin/blame.c
index 641523ff9a..7fafeac408 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -101,6 +101,16 @@ struct commit_info {
struct strbuf summary;
};
+#define COMMIT_INFO_INIT { \
+ .author = STRBUF_INIT, \
+ .author_mail = STRBUF_INIT, \
+ .author_tz = STRBUF_INIT, \
+ .committer = STRBUF_INIT, \
+ .committer_mail = STRBUF_INIT, \
+ .committer_tz = STRBUF_INIT, \
+ .summary = STRBUF_INIT, \
+}
+
/*
* Parse author/committer line in the commit object buffer
*/
@@ -160,18 +170,6 @@ static void get_ac_line(const char *inbuf, const char *what,
strbuf_add(name, namebuf, namelen);
}
-static void commit_info_init(struct commit_info *ci)
-{
-
- strbuf_init(&ci->author, 0);
- strbuf_init(&ci->author_mail, 0);
- strbuf_init(&ci->author_tz, 0);
- strbuf_init(&ci->committer, 0);
- strbuf_init(&ci->committer_mail, 0);
- strbuf_init(&ci->committer_tz, 0);
- strbuf_init(&ci->summary, 0);
-}
-
static void commit_info_destroy(struct commit_info *ci)
{
@@ -192,8 +190,6 @@ static void get_commit_info(struct commit *commit,
const char *subject, *encoding;
const char *message;
- commit_info_init(ret);
-
encoding = get_log_output_encoding();
message = logmsg_reencode(commit, NULL, encoding);
get_ac_line(message, "\nauthor ",
@@ -246,7 +242,7 @@ static void write_filename_info(struct blame_origin *suspect)
*/
static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
{
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
return 0;
@@ -440,7 +436,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
int cnt;
const char *cp;
struct blame_origin *suspect = ent->suspect;
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
const char *default_color = NULL, *color = NULL, *reset = NULL;
@@ -630,7 +626,7 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)
if (longest_file < num)
longest_file = num;
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
suspect->commit->object.flags |= METAINFO_SHOWN;
get_commit_info(suspect->commit, &ci, 1);
if (*option & OUTPUT_SHOW_EMAIL)
@@ -917,6 +913,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
for (;;) {
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
@@ -940,6 +939,9 @@ parse_done:
revs.diffopt.flags.follow_renames = 0;
argc = parse_options_end(&ctx);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
if (incremental || (output_option & OUTPUT_PORCELAIN)) {
if (show_progress > 0)
die(_("--progress can't be used with --incremental or porcelain formats"));
diff --git a/builtin/branch.c b/builtin/branch.c
index c5de1207e4..4ce2a24754 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -77,12 +77,11 @@ define_list_config_array(color_branch_slots);
static int git_branch_config(const char *var, const char *value, void *cb)
{
const char *slot_name;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "branch.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -193,6 +192,7 @@ static void delete_branch_config(const char *branchname)
static int delete_branches(int argc, const char **argv, int force, int kinds,
int quiet)
{
+ struct worktree **worktrees;
struct commit *head_rev = NULL;
struct object_id oid;
char *name = NULL;
@@ -229,6 +229,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
if (!head_rev)
die(_("Couldn't look up commit object for HEAD"));
}
+
+ worktrees = get_worktrees();
+
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
int flags = 0;
@@ -239,7 +242,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
if (kinds == FILTER_REFS_BRANCHES) {
const struct worktree *wt =
- find_shared_symref("HEAD", name);
+ find_shared_symref(worktrees, "HEAD", name);
if (wt) {
error(_("Cannot delete branch '%s' "
"checked out at '%s'"),
@@ -300,6 +303,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
free(name);
strbuf_release(&bname);
+ free_worktrees(worktrees);
return ret;
}
@@ -407,7 +411,8 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
return strbuf_detach(&fmt, NULL);
}
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting,
+ struct ref_format *format, struct string_list *output)
{
int i;
struct ref_array array;
@@ -449,7 +454,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */
- string_list_append(&output, out.buf);
+ string_list_append(output, out.buf);
} else {
fwrite(out.buf, 1, out.len, stdout);
putchar('\n');
@@ -624,7 +629,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
enum branch_track track;
struct ref_filter filter;
int icase = 0;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ static struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
struct option options[] = {
@@ -667,7 +673,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_MERGED(&filter, N_("print only branches that are merged")),
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
N_("print only branches of the object"), parse_opt_object_name),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
@@ -684,7 +690,7 @@ 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);
- git_config(git_branch_config, sorting_tail);
+ git_config(git_branch_config, &sorting_options);
track = git_branch_track;
@@ -718,7 +724,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
finalize_colopts(&colopts, -1);
if (filter.verbose) {
if (explicitly_enable_column(colopts))
- die(_("--column and --verbose are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--column", "--verbose");
colopts = 0;
}
@@ -750,14 +756,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
* local branches 'refs/heads/...' and finally remote-tracking
* branches 'refs/remotes/...'.
*/
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
ref_sorting_set_sort_flags_all(
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
- print_ref_list(&filter, sorting, &format);
+ print_ref_list(&filter, sorting, &format, &output);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
+ ref_sorting_release(sorting);
return 0;
} else if (edit_description) {
const char *branch_name;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 243fe6844b..d94050e6c1 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -355,18 +355,34 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
}
}
+/*
+ * If "pack" is non-NULL, then "offset" is the byte offset within the pack from
+ * which the object may be accessed (though note that we may also rely on
+ * data->oid, too). If "pack" is NULL, then offset is ignored.
+ */
static void batch_object_write(const char *obj_name,
struct strbuf *scratch,
struct batch_options *opt,
- struct expand_data *data)
+ struct expand_data *data,
+ struct packed_git *pack,
+ off_t offset)
{
- if (!data->skip_object_info &&
- oid_object_info_extended(the_repository, &data->oid, &data->info,
- OBJECT_INFO_LOOKUP_REPLACE) < 0) {
- printf("%s missing\n",
- obj_name ? obj_name : oid_to_hex(&data->oid));
- fflush(stdout);
- return;
+ if (!data->skip_object_info) {
+ int ret;
+
+ if (pack)
+ ret = packed_object_info(the_repository, pack, offset,
+ &data->info);
+ else
+ ret = oid_object_info_extended(the_repository,
+ &data->oid, &data->info,
+ OBJECT_INFO_LOOKUP_REPLACE);
+ if (ret < 0) {
+ printf("%s missing\n",
+ obj_name ? obj_name : oid_to_hex(&data->oid));
+ fflush(stdout);
+ return;
+ }
}
strbuf_reset(scratch);
@@ -428,7 +444,7 @@ static void batch_one_object(const char *obj_name,
return;
}
- batch_object_write(obj_name, scratch, opt, data);
+ batch_object_write(obj_name, scratch, opt, data, NULL, 0);
}
struct object_cb_data {
@@ -442,7 +458,8 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
{
struct object_cb_data *data = vdata;
oidcpy(&data->expand->oid, oid);
- batch_object_write(NULL, data->scratch, data->opt, data->expand);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ NULL, 0);
return 0;
}
@@ -463,21 +480,26 @@ static int collect_packed_object(const struct object_id *oid,
return 0;
}
-static int batch_unordered_object(const struct object_id *oid, void *vdata)
+static int batch_unordered_object(const struct object_id *oid,
+ struct packed_git *pack, off_t offset,
+ void *vdata)
{
struct object_cb_data *data = vdata;
if (oidset_insert(data->seen, oid))
return 0;
- return batch_object_cb(oid, data);
+ oidcpy(&data->expand->oid, oid);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ pack, offset);
+ return 0;
}
static int batch_unordered_loose(const struct object_id *oid,
const char *path,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, NULL, 0, data);
}
static int batch_unordered_packed(const struct object_id *oid,
@@ -485,7 +507,9 @@ static int batch_unordered_packed(const struct object_id *oid,
uint32_t pos,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, pack,
+ nth_packed_object_offset(pack, pos),
+ data);
}
static int batch_objects(struct batch_options *opt)
@@ -529,6 +553,8 @@ static int batch_objects(struct batch_options *opt)
if (has_promisor_remote())
warning("This repository uses promisor remotes. Some objects may not be loaded.");
+ read_replace_refs = 0;
+
cb.opt = opt;
cb.expand = &data;
cb.scratch = &output;
@@ -703,7 +729,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
}
if (force_path && batch.enabled) {
- error("--path=<path> incompatible with --batch");
+ error("options '--path=<path>' and '--batch' cannot be used together");
usage_with_options(cat_file_usage, options);
}
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index fb9fd13b73..ede7dc32a4 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -82,8 +82,8 @@ static void worker_loop(struct checkout *state)
size_t i, nr = 0, alloc = 0;
while (1) {
- int len = packet_read(0, NULL, NULL, packet_buffer,
- sizeof(packet_buffer), 0);
+ int len = packet_read(0, packet_buffer, sizeof(packet_buffer),
+ 0);
if (len < 0)
BUG("packet_read() returned negative value");
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1809ac12df..0bc2e63510 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -91,8 +91,8 @@ struct checkout_opts {
};
struct branch_info {
- const char *name; /* The short name used */
- const char *path; /* The full name of a real branch */
+ char *name; /* The short name used */
+ char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
char *refname; /* The full name of the ref being checked out. */
struct object_id oid; /* The object ID of the commit being checked out. */
@@ -103,6 +103,14 @@ struct branch_info {
char *checkout;
};
+static void branch_info_release(struct branch_info *info)
+{
+ free(info->name);
+ free(info->path);
+ free(info->refname);
+ free(info->checkout);
+}
+
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
@@ -456,10 +464,10 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("'%s' cannot be used with updating paths"), "--detach");
if (opts->merge && opts->patch_mode)
- die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+ die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch");
if (opts->ignore_unmerged && opts->merge)
- die(_("'%s' cannot be used with %s"),
+ die(_("options '%s' and '%s' cannot be used together"),
opts->ignore_unmerged_opt, "-m");
if (opts->new_branch)
@@ -688,9 +696,12 @@ static void setup_branch_path(struct branch_info *branch)
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
- if (strcmp(buf.buf, branch->name))
+ if (strcmp(buf.buf, branch->name)) {
+ free(branch->name);
branch->name = xstrdup(buf.buf);
+ }
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+ free(branch->path);
branch->path = strbuf_detach(&buf, NULL);
}
@@ -874,7 +885,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
int ret;
struct strbuf err = STRBUF_INIT;
- ret = safe_create_reflog(refname, 1, &err);
+ ret = safe_create_reflog(refname, &err);
if (ret) {
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
opts->new_orphan_branch, err.buf);
@@ -894,7 +905,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
opts->new_branch_log,
opts->quiet,
opts->track);
- new_branch_info->name = opts->new_branch;
+ free(new_branch_info->name);
+ free(new_branch_info->refname);
+ new_branch_info->name = xstrdup(opts->new_branch);
setup_branch_path(new_branch_info);
}
@@ -1062,8 +1075,7 @@ static int switch_branches(const struct checkout_opts *opts,
struct branch_info *new_branch_info)
{
int ret = 0;
- struct branch_info old_branch_info;
- void *path_to_free;
+ struct branch_info old_branch_info = { 0 };
struct object_id rev;
int flag, writeout_error = 0;
int do_merge = 1;
@@ -1071,25 +1083,32 @@ static int switch_branches(const struct checkout_opts *opts,
trace2_cmd_mode("branch");
memset(&old_branch_info, 0, sizeof(old_branch_info));
- old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
+ old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
if (old_branch_info.path)
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
if (!(flag & REF_ISSYMREF))
- old_branch_info.path = NULL;
+ FREE_AND_NULL(old_branch_info.path);
- if (old_branch_info.path)
- skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
+ if (old_branch_info.path) {
+ const char *const prefix = "refs/heads/";
+ const char *p;
+ if (skip_prefix(old_branch_info.path, prefix, &p))
+ old_branch_info.name = xstrdup(p);
+ else
+ BUG("should be able to skip past '%s' in '%s'!",
+ prefix, old_branch_info.path);
+ }
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->name)
BUG("'switch --orphan' should never accept a commit as starting point");
new_branch_info->commit = NULL;
- new_branch_info->name = "(empty)";
+ new_branch_info->name = xstrdup("(empty)");
do_merge = 1;
}
if (!new_branch_info->name) {
- new_branch_info->name = "HEAD";
+ new_branch_info->name = xstrdup("HEAD");
new_branch_info->commit = old_branch_info.commit;
if (!new_branch_info->commit)
die(_("You are on a branch yet to be born"));
@@ -1102,7 +1121,7 @@ static int switch_branches(const struct checkout_opts *opts,
if (do_merge) {
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
- free(path_to_free);
+ branch_info_release(&old_branch_info);
return ret;
}
}
@@ -1113,7 +1132,8 @@ static int switch_branches(const struct checkout_opts *opts,
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
- free(path_to_free);
+ branch_info_release(&old_branch_info);
+
return ret || writeout_error;
}
@@ -1145,16 +1165,15 @@ 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 = arg;
+ new_branch_info->name = xstrdup(arg);
setup_branch_path(new_branch_info);
if (!check_refname_format(new_branch_info->path, 0) &&
!read_ref(new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
- else {
- free((char *)new_branch_info->path);
- new_branch_info->path = NULL; /* not an existing branch */
- }
+ else
+ /* not an existing branch */
+ FREE_AND_NULL(new_branch_info->path);
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
if (!new_branch_info->commit) {
@@ -1517,7 +1536,7 @@ static struct option *add_common_options(struct checkout_opts *opts,
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 or diff3)")),
+ N_("conflict style (merge, diff3, or zdiff3)")),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1576,12 +1595,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[])
+ const char * const usagestr[],
+ struct branch_info *new_branch_info)
{
- struct branch_info new_branch_info;
int parseopt_flags = 0;
- memset(&new_branch_info, 0, sizeof(new_branch_info));
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = -1;
@@ -1619,11 +1637,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
- die(_("-%c, -%c and --orphan are mutually exclusive"),
- cb_option, toupper(cb_option));
+ die(_("options '-%c', '-%c', and '%s' cannot be used together"),
+ cb_option, toupper(cb_option), "--orphan");
if (opts->overlay_mode == 1 && opts->patch_mode)
- die(_("-p and --overlay are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "-p", "--overlay");
if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
if (opts->checkout_index < 0)
@@ -1690,7 +1708,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) {
@@ -1699,7 +1717,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (get_oid_mb(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);
@@ -1719,7 +1737,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);
@@ -1730,19 +1748,19 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (opts->pathspec_from_file) {
if (opts->pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
if (opts->force_detach)
- die(_("--pathspec-from-file is incompatible with --detach"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--detach");
if (opts->patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
parse_pathspec_file(&opts->pathspec, 0,
0,
prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
} else if (opts->pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
opts->pathspec.recursive = 1;
@@ -1768,11 +1786,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
strbuf_release(&buf);
}
- UNLEAK(opts);
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, &new_branch_info);
+ return checkout_paths(opts, new_branch_info);
else
- return checkout_branch(opts, &new_branch_info);
+ return checkout_branch(opts, new_branch_info);
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
@@ -1791,6 +1808,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1821,7 +1839,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, checkout_usage);
+ options, checkout_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
+ clear_pathspec(&opts.pathspec);
FREE_AND_NULL(options);
return ret;
}
@@ -1842,6 +1862,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1861,7 +1882,8 @@ 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);
+ options, switch_branch_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}
@@ -1883,6 +1905,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.accept_ref = 0;
@@ -1898,7 +1921,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, restore_usage);
+ options, restore_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 98a2860409..3ff02bbbff 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -36,6 +36,8 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
+static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
+static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
enum color_clean {
CLEAN_COLOR_RESET = 0,
@@ -153,6 +155,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
{
DIR *dir;
struct strbuf quoted = STRBUF_INIT;
+ struct strbuf realpath = STRBUF_INIT;
+ struct strbuf real_ocwd = STRBUF_INIT;
struct dirent *e;
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
struct string_list dels = STRING_LIST_INIT_DUP;
@@ -231,16 +235,36 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
strbuf_setlen(path, original_len);
if (*dir_gone) {
- res = dry_run ? 0 : rmdir(path->buf);
- if (!res)
- *dir_gone = 1;
- else {
- int saved_errno = errno;
- quote_path(path->buf, prefix, &quoted, 0);
- errno = saved_errno;
- warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ /*
+ * Normalize path components in path->buf, e.g. change '\' to
+ * '/' on Windows.
+ */
+ strbuf_realpath(&realpath, path->buf, 1);
+
+ /*
+ * path and realpath are absolute; for comparison, we would
+ * like to transform startup_info->original_cwd to an absolute
+ * path too.
+ */
+ if (startup_info->original_cwd)
+ strbuf_realpath(&real_ocwd,
+ startup_info->original_cwd, 1);
+
+ if (!strbuf_cmp(&realpath, &real_ocwd)) {
+ printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
*dir_gone = 0;
- ret = 1;
+ } else {
+ res = dry_run ? 0 : rmdir(path->buf);
+ if (!res)
+ *dir_gone = 1;
+ else {
+ int saved_errno = errno;
+ quote_path(path->buf, prefix, &quoted, 0);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ *dir_gone = 0;
+ ret = 1;
+ }
}
}
@@ -250,6 +274,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
out:
+ strbuf_release(&realpath);
+ strbuf_release(&real_ocwd);
strbuf_release(&quoted);
string_list_clear(&dels, 0);
return ret;
diff --git a/builtin/clone.c b/builtin/clone.c
index 559acf9e03..727e16e0ae 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -633,7 +633,7 @@ static int git_sparse_checkout_init(const char *repo)
{
struct strvec argv = STRVEC_INIT;
int result = 0;
- strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+ strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
/*
* We must apply the setting in the current process
@@ -900,10 +900,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_bare) {
if (option_origin)
- die(_("--bare and --origin %s options are incompatible."),
- option_origin);
+ die(_("options '%s' and '%s %s' cannot be used together"),
+ "--bare", "--origin", option_origin);
if (real_git_dir)
- die(_("--bare and --separate-git-dir are incompatible."));
+ die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir");
option_no_checkout = 1;
}
@@ -1040,8 +1040,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
INIT_DB_QUIET);
- if (real_git_dir)
+ if (real_git_dir) {
+ free((char *)git_dir);
git_dir = real_git_dir;
+ }
/*
* additional config can be injected with -c, make sure it's included
@@ -1288,7 +1290,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
*/
submodule_progress = transport->progress;
- transport_unlock_pack(transport);
+ transport_unlock_pack(transport, 0);
transport_disconnect(transport);
if (option_dissociate) {
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 3c3de3a156..4247fbde95 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -172,8 +172,8 @@ static int write_option_max_new_filters(const struct option *opt,
const char *s;
*to = strtol(arg, (char **)&s, 10);
if (*s)
- return error(_("%s expects a numerical value"),
- optname(opt, opt->flags));
+ return error(_("option `%s' expects a numerical value"),
+ "max-new-filters");
}
return 0;
}
@@ -263,7 +263,6 @@ static int graph_write(int argc, const char **argv)
git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
- read_replace_refs = 0;
odb = find_odb(the_repository, opts.obj_dir);
if (opts.reachable) {
@@ -318,6 +317,7 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
if (!argc)
goto usage;
+ read_replace_refs = 0;
save_commit_buffer = 0;
if (!strcmp(argv[0], "verify"))
diff --git a/builtin/commit.c b/builtin/commit.c
index 883c16256c..b9ed0374e3 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -355,19 +355,19 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (pathspec_from_file) {
if (interactive)
- die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
if (all)
- die(_("--pathspec-from-file with -a does not make sense"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "-a");
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (!pathspec.nr && (also || (only && !allow_empty &&
@@ -799,7 +799,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (!strcmp(fixup_prefix, "amend")) {
if (have_option_m)
- die(_("cannot combine -m with --fixup:%s"), fixup_message);
+ die(_("options '%s' and '%s:%s' cannot be used together"), "-m", "--fixup", fixup_message);
prepare_amend_commit(commit, &sb, &ctx);
}
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
@@ -1193,7 +1193,7 @@ static void finalize_deferred_config(struct wt_status *s)
status_format == STATUS_FORMAT_UNSPECIFIED)
status_format = STATUS_FORMAT_PORCELAIN;
else if (status_format == STATUS_FORMAT_LONG)
- die(_("--long and -z are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--long", "-z");
}
if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
@@ -1229,9 +1229,10 @@ static void check_fixup_reword_options(int argc, const char *argv[]) {
die(_("You are in the middle of a cherry-pick -- cannot reword."));
}
if (argc)
- die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
+ die(_("reword option of '%s' and path '%s' cannot be used together"), "--fixup", *argv);
if (patch_interactive || interactive || all || also || only)
- die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
+ die(_("reword option of '%s' and '%s' cannot be used together"),
+ "--fixup", "--patch/--interactive/--all/--include/--only");
}
static int parse_and_validate_options(int argc, const char *argv[],
diff --git a/builtin/config.c b/builtin/config.c
index 865fddd6ce..542d8d02b2 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -575,7 +575,7 @@ static int get_urlmatch(const char *var, const char *url)
int ret;
char *section_tail;
struct string_list_item *item;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct string_list values = STRING_LIST_INIT_DUP;
config.collect_fn = urlmatch_collect_fn;
diff --git a/builtin/credential.c b/builtin/credential.c
index d75dcdc64a..d7b304fa08 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -4,7 +4,7 @@
#include "config.h"
static const char usage_msg[] =
- "git credential [fill|approve|reject]";
+ "git credential (fill|approve|reject)";
int cmd_credential(int argc, const char **argv, const char *prefix)
{
diff --git a/builtin/describe.c b/builtin/describe.c
index e912ba50d7..42159cd26b 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -590,7 +590,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
if (longformat && abbrev == 0)
- die(_("--long is incompatible with --abbrev=0"));
+ die(_("options '%s' and '%s' cannot be used together"), "--long", "--abbrev=0");
if (contains) {
struct string_list_item *item;
@@ -670,9 +670,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
}
describe("HEAD", 1);
} else if (dirty) {
- die(_("--dirty is incompatible with commit-ishes"));
+ die(_("option '%s' and commit-ishes cannot be used together"), "--dirty");
} else if (broken) {
- die(_("--broken is incompatible with commit-ishes"));
+ die(_("option '%s' and commit-ishes cannot be used together"), "--broken");
} else {
while (argc-- > 0)
describe(*argv++, argc == 0);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index f33d30d57b..0e0ac1f167 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -152,7 +152,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
}
if (read_stdin && merge_base)
- die(_("--stdin and --merge-base are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base");
if (merge_base && opt->pending.nr != 2)
die(_("--merge-base only works with two commits"));
diff --git a/builtin/diff.c b/builtin/diff.c
index dd8ce688ba..fa4683377e 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -437,6 +437,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
+ if (!nongit) {
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ }
+
if (!no_index) {
/*
* Treat git diff with at least one path outside of the
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 4931c10845..c79fbbf67e 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -202,15 +202,10 @@ static void changed_files(struct hashmap *result, const char *index_path,
{
struct child_process update_index = CHILD_PROCESS_INIT;
struct child_process diff_files = CHILD_PROCESS_INIT;
- struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
- const char *git_dir = absolute_path(get_git_dir()), *env[] = {
- NULL, NULL
- };
+ struct strbuf buf = STRBUF_INIT;
+ const char *git_dir = absolute_path(get_git_dir());
FILE *fp;
- strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
- env[0] = index_env.buf;
-
strvec_pushl(&update_index.args,
"--git-dir", git_dir, "--work-tree", workdir,
"update-index", "--really-refresh", "-q",
@@ -222,7 +217,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
update_index.use_shell = 0;
update_index.clean_on_exit = 1;
update_index.dir = workdir;
- update_index.env = env;
+ strvec_pushf(&update_index.env_array, "GIT_INDEX_FILE=%s", index_path);
/* Ignore any errors of update-index */
run_command(&update_index);
@@ -235,7 +230,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
diff_files.clean_on_exit = 1;
diff_files.out = -1;
diff_files.dir = workdir;
- diff_files.env = env;
+ strvec_pushf(&diff_files.env_array, "GIT_INDEX_FILE=%s", index_path);
if (start_command(&diff_files))
die("could not obtain raw diff");
fp = xfdopen(diff_files.out, "r");
@@ -248,7 +243,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
fclose(fp);
if (finish_command(&diff_files))
die("diff-files did not exit properly");
- strbuf_release(&index_env);
strbuf_release(&buf);
}
@@ -736,10 +730,10 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
} else if (dir_diff)
- die(_("--dir-diff is incompatible with --no-index"));
+ die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
- die(_("--gui, --tool and --extcmd are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd");
if (use_gui_tool)
setenv("GIT_MERGETOOL_GUI", "true", 1);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 95e8e89e81..9f1c730e58 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -107,18 +107,6 @@ static int parse_opt_reencode_mode(const struct option *opt,
static struct decoration idnums;
static uint32_t last_idnum;
-
-static int has_unshown_parent(struct commit *commit)
-{
- struct commit_list *parent;
-
- for (parent = commit->parents; parent; parent = parent->next)
- if (!(parent->item->object.flags & SHOWN) &&
- !(parent->item->object.flags & UNINTERESTING))
- return 1;
- return 0;
-}
-
struct anonymized_entry {
struct hashmap_entry hash;
const char *anon;
@@ -312,7 +300,7 @@ static void export_blob(const struct object_id *oid)
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
- type_name(type)) < 0)
+ type_name(type), NULL) < 0)
die("oid mismatch in blob %s", oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
@@ -752,20 +740,6 @@ static char *anonymize_tag(void *data)
return strbuf_detach(&out, NULL);
}
-static void handle_tail(struct object_array *commits, struct rev_info *revs,
- struct string_list *paths_of_changed_objects)
-{
- struct commit *commit;
- while (commits->nr) {
- commit = (struct commit *)object_array_pop(commits);
- if (has_unshown_parent(commit)) {
- /* Queue again, to be handled later */
- add_object_array(&commit->object, NULL, commits);
- return;
- }
- handle_commit(commit, revs, paths_of_changed_objects);
- }
-}
static void handle_tag(const char *name, struct tag *tag)
{
@@ -1185,7 +1159,6 @@ static int parse_opt_anonymize_map(const struct option *opt,
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct object_array commits = OBJECT_ARRAY_INIT;
struct commit *commit;
char *export_filename = NULL,
*import_filename = NULL,
@@ -1254,7 +1227,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
usage_with_options (fast_export_usage, options);
if (anonymized_seeds.cmpfn && !anonymize)
- die(_("--anonymize-map without --anonymize does not make sense"));
+ die(_("the option '%s' requires '%s'"), "--anonymize-map", "--anonymize");
if (refspecs_list.nr) {
int i;
@@ -1269,7 +1242,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
printf("feature done\n");
if (import_filename && import_filename_if_exists)
- die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
+ die(_("options '%s' and '%s' cannot be used together"), "--import-marks", "--import-marks-if-exists");
if (import_filename)
import_marks(import_filename, 0);
else if (import_filename_if_exists)
@@ -1283,18 +1256,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
+
+ revs.reverse = 1;
revs.diffopt.format_callback = show_filemodify;
revs.diffopt.format_callback_data = &paths_of_changed_objects;
revs.diffopt.flags.recursive = 1;
- while ((commit = get_revision(&revs))) {
- if (has_unshown_parent(commit)) {
- add_object_array(&commit->object, NULL, &commits);
- }
- else {
- handle_commit(commit, &revs, &paths_of_changed_objects);
- handle_tail(&commits, &revs, &paths_of_changed_objects);
- }
- }
+ while ((commit = get_revision(&revs)))
+ handle_commit(commit, &revs, &paths_of_changed_objects);
handle_tags_and_duplicates(&extra_refs);
handle_tags_and_duplicates(&tag_refs);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 20406f6775..2b2e28bad7 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -401,16 +401,18 @@ static void dump_marks(void);
static NORETURN void die_nicely(const char *err, va_list params)
{
+ va_list cp;
static int zombie;
- char message[2 * PATH_MAX];
+ report_fn die_message_fn = get_die_message_routine();
- vsnprintf(message, sizeof(message), err, params);
- fputs("fatal: ", stderr);
- fputs(message, stderr);
- fputc('\n', stderr);
+ va_copy(cp, params);
+ die_message_fn(err, params);
if (!zombie) {
+ char message[2 * PATH_MAX];
+
zombie = 1;
+ vsnprintf(message, sizeof(message), err, cp);
write_crash_report(message);
end_packfile();
unkeep_all_packs();
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f7abbc31ff..5f06b21f8e 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -28,6 +28,7 @@
#include "promisor-remote.h"
#include "commit-graph.h"
#include "shallow.h"
+#include "worktree.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -222,17 +223,22 @@ static struct option builtin_fetch_options[] = {
OPT_END()
};
-static void unlock_pack(void)
+static void unlock_pack(unsigned int flags)
{
if (gtransport)
- transport_unlock_pack(gtransport);
+ transport_unlock_pack(gtransport, flags);
if (gsecondary)
- transport_unlock_pack(gsecondary);
+ transport_unlock_pack(gsecondary, flags);
+}
+
+static void unlock_pack_atexit(void)
+{
+ unlock_pack(0);
}
static void unlock_pack_on_signal(int signo)
{
- unlock_pack();
+ unlock_pack(TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
sigchain_pop(signo);
raise(signo);
}
@@ -552,7 +558,7 @@ static struct ref *get_ref_map(struct remote *remote,
for (i = 0; i < fetch_refspec->nr; i++)
get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
} else if (refmap.nr) {
- die("--refmap option is only meaningful with command-line refspec(s).");
+ die("--refmap option is only meaningful with command-line refspec(s)");
} else {
/* Use the defaults */
struct branch *branch = branch_get(NULL);
@@ -583,7 +589,7 @@ static struct ref *get_ref_map(struct remote *remote,
} else if (!prefetch) {
ref_map = get_remote_ref(remote_refs, "HEAD");
if (!ref_map)
- die(_("Couldn't find remote ref HEAD"));
+ die(_("couldn't find remote ref HEAD"));
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
tail = &ref_map->next;
}
@@ -848,13 +854,12 @@ static void format_display(struct strbuf *display, char code,
static int update_local_ref(struct ref *ref,
struct ref_transaction *transaction,
- const char *remote,
- const struct ref *remote_ref,
- struct strbuf *display,
- int summary_width)
+ const char *remote, const struct ref *remote_ref,
+ struct strbuf *display, int summary_width,
+ struct worktree **worktrees)
{
struct commit *current = NULL, *updated;
- struct branch *current_branch = branch_get(NULL);
+ const struct worktree *wt;
const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
@@ -868,16 +873,17 @@ static int update_local_ref(struct ref *ref,
return 0;
}
- if (current_branch &&
- !strcmp(ref->name, current_branch->name) &&
- !(update_head_ok || is_bare_repository()) &&
- !is_null_oid(&ref->old_oid)) {
+ if (!update_head_ok &&
+ (wt = find_shared_symref(worktrees, "HEAD", ref->name)) &&
+ !wt->is_bare && !is_null_oid(&ref->old_oid)) {
/*
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
format_display(display, '!', _("[rejected]"),
- _("can't fetch in current branch"),
+ wt->is_current ?
+ _("can't fetch in current branch") :
+ _("checked out in another worktree"),
remote, pretty_ref, summary_width);
return 1;
}
@@ -995,7 +1001,7 @@ static int open_fetch_head(struct fetch_head *fetch_head)
if (write_fetch_head) {
fetch_head->fp = fopen(filename, "a");
if (!fetch_head->fp)
- return error_errno(_("cannot open %s"), filename);
+ return error_errno(_("cannot open '%s'"), filename);
strbuf_init(&fetch_head->buf, 0);
} else {
fetch_head->fp = NULL;
@@ -1067,16 +1073,17 @@ static void close_fetch_head(struct fetch_head *fetch_head)
}
static const char warn_show_forced_updates[] =
-N_("Fetch normally indicates which branches had a forced update,\n"
- "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
- "flag or run 'git config fetch.showForcedUpdates true'.");
+N_("fetch normally indicates which branches had a forced update,\n"
+ "but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+ "flag or run 'git config fetch.showForcedUpdates true'");
static const char warn_time_show_forced_updates[] =
-N_("It took %.2f seconds to check forced updates. You can use\n"
+N_("it took %.2f seconds to check forced updates; you can use\n"
"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
- " to avoid this check.\n");
+ "to avoid this check\n");
static int store_updated_refs(const char *raw_url, const char *remote_name,
- int connectivity_checked, struct ref *ref_map)
+ int connectivity_checked, struct ref *ref_map,
+ struct worktree **worktrees)
{
struct fetch_head fetch_head;
int url_len, i, rc = 0;
@@ -1205,7 +1212,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
strbuf_reset(&note);
if (ref) {
rc |= update_local_ref(ref, transaction, what,
- rm, &note, summary_width);
+ rm, &note, summary_width,
+ worktrees);
free(ref);
} else if (write_fetch_head || dry_run) {
/*
@@ -1298,7 +1306,9 @@ static int check_exist_and_connected(struct ref *ref_map)
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_and_consume_refs(struct transport *transport,
+ struct ref *ref_map,
+ struct worktree **worktrees)
{
int connectivity_checked = 1;
int ret;
@@ -1319,14 +1329,12 @@ static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_m
}
trace2_region_enter("fetch", "consume_refs", the_repository);
- ret = store_updated_refs(transport->url,
- transport->remote->name,
- connectivity_checked,
- ref_map);
+ ret = store_updated_refs(transport->url, transport->remote->name,
+ connectivity_checked, ref_map, worktrees);
trace2_region_leave("fetch", "consume_refs", the_repository);
out:
- transport_unlock_pack(transport);
+ transport_unlock_pack(transport, 0);
return ret;
}
@@ -1385,18 +1393,18 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
return result;
}
-static void check_not_current_branch(struct ref *ref_map)
+static void check_not_current_branch(struct ref *ref_map,
+ struct worktree **worktrees)
{
- struct branch *current_branch = branch_get(NULL);
-
- if (is_bare_repository() || !current_branch)
- return;
-
+ const struct worktree *wt;
for (; ref_map; ref_map = ref_map->next)
- if (ref_map->peer_ref && !strcmp(current_branch->refname,
- ref_map->peer_ref->name))
- die(_("Refusing to fetch into current branch %s "
- "of non-bare repository"), current_branch->refname);
+ if (ref_map->peer_ref &&
+ (wt = find_shared_symref(worktrees, "HEAD",
+ ref_map->peer_ref->name)) &&
+ !wt->is_bare)
+ die(_("refusing to fetch into branch '%s' "
+ "checked out at '%s'"),
+ ref_map->peer_ref->name, wt->path);
}
static int truncate_fetch_head(void)
@@ -1405,7 +1413,7 @@ static int truncate_fetch_head(void)
FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error_errno(_("cannot open %s"), filename);
+ return error_errno(_("cannot open '%s'"), filename);
fclose(fp);
return 0;
}
@@ -1414,10 +1422,10 @@ static void set_option(struct transport *transport, const char *name, const char
{
int r = transport_set_option(transport, name, value);
if (r < 0)
- die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+ die(_("option \"%s\" value \"%s\" is not valid for %s"),
name, value, transport->url);
if (r > 0)
- warning(_("Option \"%s\" is ignored for %s\n"),
+ warning(_("option \"%s\" is ignored for %s\n"),
name, transport->url);
}
@@ -1451,7 +1459,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
old_nr = oids->nr;
for_each_glob_ref(add_oid, s, oids);
if (old_nr == oids->nr)
- warning("Ignoring --negotiation-tip=%s because it does not match any refs",
+ warning("ignoring --negotiation-tip=%s because it does not match any refs",
s);
}
smart_options->negotiation_tips = oids;
@@ -1489,12 +1497,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
if (transport->smart_options)
add_negotiation_tips(transport->smart_options);
else
- warning("Ignoring --negotiation-tip because the protocol does not support it.");
+ warning("ignoring --negotiation-tip because the protocol does not support it");
}
return transport;
}
-static void backfill_tags(struct transport *transport, struct ref *ref_map)
+static void backfill_tags(struct transport *transport, struct ref *ref_map,
+ struct worktree **worktrees)
{
int cannot_reuse;
@@ -1515,7 +1524,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- fetch_and_consume_refs(transport, ref_map);
+ fetch_and_consume_refs(transport, ref_map, worktrees);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1533,6 +1542,7 @@ static int do_fetch(struct transport *transport,
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
int must_list_refs = 1;
+ struct worktree **worktrees = get_worktrees();
if (tags == TAGS_DEFAULT) {
if (transport->remote->fetch_tags == 2)
@@ -1588,7 +1598,7 @@ static int do_fetch(struct transport *transport,
ref_map = get_ref_map(transport->remote, remote_refs, rs,
tags, &autotags);
if (!update_head_ok)
- check_not_current_branch(ref_map);
+ check_not_current_branch(ref_map, worktrees);
if (tags == TAGS_DEFAULT && autotags)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
@@ -1606,7 +1616,7 @@ static int do_fetch(struct transport *transport,
transport->url);
}
}
- if (fetch_and_consume_refs(transport, ref_map)) {
+ if (fetch_and_consume_refs(transport, ref_map, worktrees)) {
free_refs(ref_map);
retcode = 1;
goto cleanup;
@@ -1638,6 +1648,16 @@ static int do_fetch(struct transport *transport,
}
}
if (source_ref) {
+ if (!branch) {
+ const char *shortname = source_ref->name;
+ skip_prefix(shortname, "refs/heads/", &shortname);
+
+ warning(_("could not set upstream of HEAD to '%s' from '%s' when "
+ "it does not point to any branch."),
+ shortname, transport->remote->name);
+ goto skip;
+ }
+
if (!strcmp(source_ref->name, "HEAD") ||
starts_with(source_ref->name, "refs/heads/"))
install_branch_config(0,
@@ -1651,11 +1671,11 @@ static int do_fetch(struct transport *transport,
else
warning(_("unknown branch type"));
} else {
- warning(_("no source branch found.\n"
- "you need to specify exactly one branch with the --set-upstream option."));
+ warning(_("no source branch found;\n"
+ "you need to specify exactly one branch with the --set-upstream option"));
}
}
- skip:
+skip:
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -1665,11 +1685,12 @@ static int do_fetch(struct transport *transport,
ref_map = NULL;
find_non_local_tags(remote_refs, &ref_map, &tail);
if (ref_map)
- backfill_tags(transport, ref_map);
+ backfill_tags(transport, ref_map, worktrees);
free_refs(ref_map);
}
- cleanup:
+cleanup:
+ free_worktrees(worktrees);
return retcode;
}
@@ -1790,7 +1811,7 @@ static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
struct parallel_fetch_state *state = cb;
const char *remote = task_cb;
- state->result = error(_("Could not fetch %s"), remote);
+ state->result = error(_("could not fetch %s"), remote);
return 0;
}
@@ -1845,7 +1866,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
if (verbosity >= 0)
printf(_("Fetching %s\n"), name);
if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
- error(_("Could not fetch %s"), name);
+ error(_("could not fetch %s"), name);
result = 1;
}
strvec_pop(&argv);
@@ -1906,8 +1927,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
int remote_via_config = remote_is_configured(remote, 0);
if (!remote)
- die(_("No remote repository specified. Please, specify either a URL or a\n"
- "remote name from which new revisions should be fetched."));
+ die(_("no remote repository specified; please specify either a URL or a\n"
+ "remote name from which new revisions should be fetched"));
gtransport = prepare_transport(remote, 1);
@@ -1942,7 +1963,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
if (!strcmp(argv[i], "tag")) {
i++;
if (i >= argc)
- die(_("You need to specify a tag name."));
+ die(_("you need to specify a tag name"));
refspec_appendf(&rs, "refs/tags/%s:refs/tags/%s",
argv[i], argv[i]);
@@ -1962,7 +1983,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
gtransport->server_options = &server_options;
sigchain_push_common(unlock_pack_on_signal);
- atexit(unlock_pack);
+ atexit(unlock_pack_atexit);
sigchain_push(SIGPIPE, SIG_IGN);
exit_code = do_fetch(gtransport, &rs);
sigchain_pop(SIGPIPE);
@@ -1993,6 +2014,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
git_config(git_fetch_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
@@ -2010,14 +2033,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (deepen_relative) {
if (deepen_relative < 0)
- die(_("Negative depth in --deepen is not supported"));
+ die(_("negative depth in --deepen is not supported"));
if (depth)
- die(_("--deepen and --depth are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--deepen", "--depth");
depth = xstrfmt("%d", deepen_relative);
}
if (unshallow) {
if (depth)
- die(_("--depth and --unshallow cannot be used together"));
+ die(_("options '%s' and '%s' cannot be used together"), "--depth", "--unshallow");
else if (!is_repository_shallow(the_repository))
die(_("--unshallow on a complete repository does not make sense"));
else
@@ -2047,14 +2070,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
/* All arguments are assumed to be remotes or groups */
for (i = 0; i < argc; i++)
if (!add_remote_or_group(argv[i], &list))
- die(_("No such remote or remote group: %s"), argv[i]);
+ die(_("no such remote or remote group: %s"),
+ argv[i]);
} else {
/* Single remote or group */
(void) add_remote_or_group(argv[0], &list);
if (list.nr > 1) {
/* More than one remote */
if (argc > 1)
- die(_("Fetching a group and specifying refspecs does not make sense"));
+ die(_("fetching a group and specifying refspecs does not make sense"));
} else {
/* Zero or one remotes */
remote = remote_get(argv[0]);
@@ -2075,7 +2099,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (gtransport->smart_options) {
gtransport->smart_options->acked_commits = &acked_commits;
} else {
- warning(_("Protocol does not support --negotiate-only, exiting."));
+ warning(_("protocol does not support --negotiate-only, exiting"));
return 1;
}
if (server_options.nr)
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 48a8699de7..8d8fd393f8 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -12,6 +12,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
const char *message = NULL;
+ char *into_name = NULL;
int shortlog_len = -1;
struct option options[] = {
{ OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
@@ -23,6 +24,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
DEFAULT_MERGE_LOG_LEN },
OPT_STRING('m', "message", &message, N_("text"),
N_("use <text> as start of message")),
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target branch")),
OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
OPT_END()
};
@@ -56,6 +59,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
opts.add_title = !message;
opts.credit_people = 1;
opts.shortlog_len = shortlog_len;
+ opts.into_name = into_name;
ret = fmt_merge_msg(&input, &output, &opts);
if (ret)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 642b4b888f..6f62f40d12 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -17,7 +17,8 @@ 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 = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
int maxcount = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
@@ -39,7 +40,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_INTEGER( 0 , "count", &maxcount, 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_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
parse_opt_object_name),
@@ -70,8 +71,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
@@ -96,6 +96,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
ref_array_clear(&array);
free_commit_list(filter.with_commit);
free_commit_list(filter.no_commit);
- UNLEAK(sorting);
+ ref_sorting_release(sorting);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b42b6fe21f..9e54892311 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -593,18 +593,44 @@ static void get_default_heads(void)
}
}
+struct for_each_loose_cb
+{
+ struct progress *progress;
+ struct strbuf obj_type;
+};
+
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
struct object *obj;
- enum object_type type;
+ enum object_type type = OBJ_NONE;
unsigned long size;
- void *contents;
+ void *contents = NULL;
int eaten;
+ struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id real_oid = *null_oid();
+ int err = 0;
- if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+ strbuf_reset(&cb_data->obj_type);
+ oi.type_name = &cb_data->obj_type;
+ oi.sizep = &size;
+ oi.typep = &type;
+
+ if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) {
+ if (contents && !oideq(&real_oid, oid))
+ err = error(_("%s: hash-path mismatch, found at: %s"),
+ oid_to_hex(&real_oid), path);
+ else
+ err = error(_("%s: object corrupt or missing: %s"),
+ oid_to_hex(oid), path);
+ }
+ if (type != OBJ_NONE && type < 0)
+ err = error(_("%s: object is of unknown type '%s': %s"),
+ oid_to_hex(&real_oid), cb_data->obj_type.buf,
+ path);
+ if (err < 0) {
errors_found |= ERROR_OBJECT;
- error(_("%s: object corrupt or missing: %s"),
- oid_to_hex(oid), path);
+ free(contents);
return 0; /* keep checking other objects */
}
@@ -640,8 +666,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int fsck_subdir(unsigned int nr, const char *path, void *progress)
+static int fsck_subdir(unsigned int nr, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
+ struct progress *progress = cb_data->progress;
display_progress(progress, nr + 1);
return 0;
}
@@ -649,6 +677,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress)
static void fsck_object_dir(const char *path)
{
struct progress *progress = NULL;
+ struct for_each_loose_cb cb_data = {
+ .obj_type = STRBUF_INIT,
+ .progress = progress,
+ };
if (verbose)
fprintf_ln(stderr, _("Checking object directory"));
@@ -657,9 +689,10 @@ static void fsck_object_dir(const char *path)
progress = start_progress(_("Checking object directories"), 256);
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
- progress);
+ &cb_data);
display_progress(progress, 256);
stop_progress(&progress);
+ strbuf_release(&cb_data.obj_type);
}
static int fsck_head_link(const char *head_ref_name,
@@ -803,6 +836,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
fsck_enable_object_names(&fsck_walk_options);
git_config(git_fsck_config, &fsck_obj_options);
+ prepare_repo_settings(the_repository);
if (connectivity_only) {
for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
@@ -908,33 +942,29 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
check_connectivity();
- if (!git_config_get_bool("core.commitgraph", &i) && i) {
+ if (the_repository->settings.core_commit_graph) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
- const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&commit_graph_verify);
- commit_graph_verify.argv = verify_argv;
commit_graph_verify.git_cmd = 1;
- verify_argv[2] = "--object-dir";
- verify_argv[3] = odb->path;
+ strvec_pushl(&commit_graph_verify.args, "commit-graph",
+ "verify", "--object-dir", odb->path, NULL);
if (run_command(&commit_graph_verify))
errors_found |= ERROR_COMMIT_GRAPH;
}
}
- if (!git_config_get_bool("core.multipackindex", &i) && i) {
+ if (the_repository->settings.core_multi_pack_index) {
struct child_process midx_verify = CHILD_PROCESS_INIT;
- const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&midx_verify);
- midx_verify.argv = midx_argv;
midx_verify.git_cmd = 1;
- midx_argv[2] = "--object-dir";
- midx_argv[3] = odb->path;
+ strvec_pushl(&midx_verify.args, "multi-pack-index",
+ "verify", "--object-dir", odb->path, NULL);
if (run_command(&midx_verify))
errors_found |= ERROR_MULTI_PACK_INDEX;
}
diff --git a/builtin/gc.c b/builtin/gc.c
index 6b3de3dd51..8e60ef1eab 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -470,7 +470,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
/*
* Returns 0 if there was no previous error and gc can proceed, 1 if
* gc should not proceed due to an error in the last run. Prints a
- * message and returns -1 if an error occurred while reading gc.log
+ * message and returns with a non-[01] status code if an error occurred
+ * while reading gc.log
*/
static int report_last_gc_error(void)
{
@@ -484,7 +485,7 @@ static int report_last_gc_error(void)
if (errno == ENOENT)
goto done;
- ret = error_errno(_("cannot stat '%s'"), gc_log_path);
+ ret = die_message_errno(_("cannot stat '%s'"), gc_log_path);
goto done;
}
@@ -493,7 +494,7 @@ static int report_last_gc_error(void)
len = strbuf_read_file(&sb, gc_log_path, 0);
if (len < 0)
- ret = error_errno(_("cannot read '%s'"), gc_log_path);
+ ret = die_message_errno(_("cannot read '%s'"), gc_log_path);
else if (len > 0) {
/*
* A previous gc failed. Report the error, and don't
@@ -611,12 +612,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
}
if (detach_auto) {
int ret = report_last_gc_error();
- if (ret < 0)
- /* an I/O error occurred, already reported */
- exit(128);
+
if (ret == 1)
/* Last gc --auto failed. Skip this one. */
return 0;
+ else if (ret)
+ /* an I/O error occurred, already reported */
+ return ret;
if (lock_repo_for_gc(force, &pid))
return 0;
@@ -1049,12 +1051,11 @@ static int maintenance_task_loose_objects(struct maintenance_run_opts *opts)
static int incremental_repack_auto_condition(void)
{
struct packed_git *p;
- int enabled;
int incremental_repack_auto_limit = 10;
int count = 0;
- if (git_config_get_bool("core.multiPackIndex", &enabled) ||
- !enabled)
+ prepare_repo_settings(the_repository);
+ if (!the_repository->settings.core_multi_pack_index)
return 0;
git_config_get_int("maintenance.incremental-repack.auto",
@@ -2000,15 +2001,11 @@ static int schtasks_update_schedule(int run_maintenance, int fd)
return schtasks_remove_tasks();
}
-static int is_crontab_available(void)
+MAYBE_UNUSED
+static int check_crontab_process(const char *cmd)
{
- const char *cmd = "crontab";
- int is_available;
struct child_process child = CHILD_PROCESS_INIT;
- if (get_schedule_cmd(&cmd, &is_available))
- return is_available;
-
strvec_split(&child.args, cmd);
strvec_push(&child.args, "-l");
child.no_stdin = 1;
@@ -2023,6 +2020,25 @@ static int is_crontab_available(void)
return 1;
}
+static int is_crontab_available(void)
+{
+ const char *cmd = "crontab";
+ int is_available;
+
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef __APPLE__
+ /*
+ * macOS has cron, but it requires special permissions and will
+ * create a UI alert when attempting to run this command.
+ */
+ return 0;
+#else
+ return check_crontab_process(cmd);
+#endif
+}
+
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
diff --git a/builtin/grep.c b/builtin/grep.c
index 8af5249a7b..9e34a820ad 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -199,8 +199,8 @@ static void *run(void *arg)
grep_source_clear_data(&w->source);
work_done(w);
}
- free_grep_patterns(arg);
- free(arg);
+ free_grep_patterns(opt);
+ free(opt);
return (void*) (intptr_t) hit;
}
@@ -401,7 +401,7 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len)
if (len == 1 && *(const char *)data == '\0')
return;
- string_list_append(path_list, xstrndup(data, len));
+ string_list_append_nodup(path_list, xstrndup(data, len));
}
static void run_pager(struct grep_opt *opt, const char *prefix)
@@ -839,7 +839,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
struct grep_opt opt;
struct object_array list = OBJECT_ARRAY_INIT;
struct pathspec pathspec;
- struct string_list path_list = STRING_LIST_INIT_NODUP;
+ struct string_list path_list = STRING_LIST_INIT_DUP;
int i;
int dummy;
int use_index = 1;
@@ -1159,8 +1159,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf, "+/%s%s",
strcmp("less", pager) ? "" : "*",
opt.pattern_list->pattern);
- string_list_append(&path_list,
- strbuf_detach(&buf, NULL));
+ string_list_append_nodup(&path_list,
+ strbuf_detach(&buf, NULL));
}
}
@@ -1195,7 +1195,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (hit && show_in_pager)
run_pager(&opt, prefix);
clear_pathspec(&pathspec);
+ string_list_clear(&path_list, 0);
free_grep_patterns(&opt);
+ object_array_clear(&list);
free_repos();
return !hit;
}
diff --git a/builtin/help.c b/builtin/help.c
index 75cd2fb407..d387131dd8 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -212,11 +212,10 @@ static int check_emacsclient_version(void)
{
struct strbuf buffer = STRBUF_INIT;
struct child_process ec_process = CHILD_PROCESS_INIT;
- const char *argv_ec[] = { "emacsclient", "--version", NULL };
int version;
/* emacsclient prints its version number on stderr */
- ec_process.argv = argv_ec;
+ strvec_pushl(&ec_process.args, "emacsclient", "--version", NULL);
ec_process.err = -1;
ec_process.stdout_to_stderr = 1;
if (start_command(&ec_process))
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 7ce69c087e..3c2e6aee3c 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1415,7 +1415,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
if (check_object_signature(the_repository, &d->oid,
data, size,
- type_name(type)))
+ type_name(type), NULL))
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
/*
@@ -1486,7 +1486,7 @@ static void rename_tmp_packfile(const char **final_name,
if (!*final_name)
*final_name = odb_pack_name(name, hash, ext);
if (finalize_object_file(curr_name, *final_name))
- die(_("unable to rename temporary '*.%s' file to '%s"),
+ die(_("unable to rename temporary '*.%s' file to '%s'"),
ext, *final_name);
} else if (make_read_only_if_same) {
chmod(*final_name, 0444);
@@ -1845,11 +1845,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
if (!pack_name && !from_stdin)
usage(index_pack_usage);
if (fix_thin_pack && !from_stdin)
- die(_("--fix-thin cannot be used without --stdin"));
+ die(_("the option '%s' requires '%s'"), "--fix-thin", "--stdin");
if (from_stdin && !startup_info->have_repository)
die(_("--stdin requires a git repository"));
if (from_stdin && hash_algo)
- die(_("--object-format cannot be used with --stdin"));
+ die(_("options '%s' and '%s' cannot be used together"), "--object-format", "--stdin");
if (!index_name && pack_name)
index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 2167796ff2..546f9c595e 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -557,7 +557,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
if (real_git_dir && is_bare_repository_cfg == 1)
- die(_("--separate-git-dir and --bare are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare");
if (real_git_dir && !is_absolute_path(real_git_dir))
real_git_dir = real_pathdup(real_git_dir, 1);
diff --git a/builtin/log.c b/builtin/log.c
index f75d87e8d7..4b493408cc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -245,10 +245,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
rev->abbrev_commit = 0;
}
- if (rev->commit_format == CMIT_FMT_USERFORMAT && !w.decorate)
- decoration_style = 0;
+ if (rev->commit_format == CMIT_FMT_USERFORMAT) {
+ if (!w.decorate) {
+ /*
+ * Disable decoration loading if the format will not
+ * show them anyway.
+ */
+ decoration_style = 0;
+ } else if (!decoration_style) {
+ /*
+ * If we are going to show them, make sure we do load
+ * them here, but taking care not to override a
+ * specific style set by config or --decorate.
+ */
+ decoration_style = DECORATE_SHORT_REFS;
+ }
+ }
- if (decoration_style) {
+ if (decoration_style || rev->simplify_by_decoration) {
const struct string_list *config_exclude =
repo_config_get_value_multi(the_repository,
"log.excludeDecoration");
@@ -260,7 +274,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
item->string);
}
- rev->show_decorations = 1;
+ if (decoration_style)
+ rev->show_decorations = 1;
load_ref_decorations(&decoration_filter, decoration_style);
}
@@ -1928,9 +1943,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
numbered = 0;
if (numbered && keep_subject)
- die(_("-n and -k are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
if (keep_subject && subject_prefix)
- die(_("--subject-prefix/--rfc and -k are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
rev.preserve_subject = keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
@@ -1964,7 +1979,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
load_display_notes(&rev.notes_opt);
if (use_stdout + rev.diffopt.close_file + !!output_directory > 1)
- die(_("--stdout, --output, and --output-directory are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory");
if (use_stdout) {
setup_pager();
@@ -2097,7 +2112,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (creation_factor < 0)
creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
else if (!rdiff_prev)
- die(_("--creation-factor requires --range-diff"));
+ die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
if (rdiff_prev) {
if (!cover_letter && total != 1)
@@ -2241,6 +2256,7 @@ done:
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
+ UNLEAK(rev);
return 0;
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a2000ed6bf..f7ea56cc63 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -37,6 +37,7 @@ static int debug_mode;
static int show_eol;
static int recurse_submodules;
static int skipping_duplicates;
+static int show_sparse_dirs;
static const char *prefix;
static int max_prefix_len;
@@ -315,8 +316,10 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
if (!(show_cached || show_stage || show_deleted || show_modified))
return;
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(repo->index);
+
+ if (!show_sparse_dirs)
+ ensure_full_index(repo->index);
+
for (i = 0; i < repo->index->cache_nr; i++) {
const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
@@ -670,12 +673,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_BOOL(0, "deduplicate", &skipping_duplicates,
N_("suppress duplicate entries")),
+ OPT_BOOL(0, "sparse", &show_sparse_dirs,
+ N_("show sparse directories in the presence of a sparse index")),
OPT_END()
};
+ int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
@@ -766,7 +775,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
* would not make any sense with this option.
*/
if (show_stage || show_unmerged)
- die("ls-files --with-tree is incompatible with -s or -u");
+ die(_("options '%s' and '%s' cannot be used together"), "ls-files --with-tree", "-s/-u");
overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
}
@@ -775,16 +784,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (show_resolve_undo)
show_ru_info(the_repository->index);
- if (ps_matched) {
- int bad;
- bad = report_path_error(ps_matched, &pathspec);
- if (bad)
- fprintf(stderr, "Did you forget to 'git add'?\n");
-
- return bad ? 1 : 0;
+ if (ps_matched && report_path_error(ps_matched, &pathspec)) {
+ fprintf(stderr, "Did you forget to 'git add'?\n");
+ ret = 1;
}
+ string_list_clear(&exclude_list, 0);
dir_clear(&dir);
free(max_prefix);
- return 0;
+ return ret;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 318949c3d7..44448fa61d 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -54,7 +54,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;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print remote URL")),
@@ -68,7 +68,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_SET_INT_F(0, "exit-code", &status,
N_("exit with exit code 2 if no matching refs are found"),
2, PARSE_OPT_NOCOMPLETE),
@@ -86,8 +86,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
packet_trace_identity("ls-remote");
- UNLEAK(sorting);
-
if (argc > 1) {
int i;
CALLOC_ARRAY(pattern, argc);
@@ -139,8 +137,13 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
item->symref = xstrdup_or_null(ref->symref);
}
- if (sorting)
+ if (sorting_options.nr) {
+ struct ref_sorting *sorting;
+
+ sorting = ref_sorting_options(&sorting_options);
ref_array_sort(sorting, &ref_array);
+ ref_sorting_release(sorting);
+ }
for (i = 0; i < ref_array.nr; i++) {
const struct ref_array_item *ref = ref_array.items[i];
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 06a2f90c48..e695867ee5 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+ OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
+ XDL_MERGE_ZEALOUS_DIFF3),
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
XDL_MERGE_FAVOR_OURS),
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
diff --git a/builtin/merge.c b/builtin/merge.c
index cc4a910c69..74e53cf20a 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -87,6 +87,7 @@ static int signoff;
static const char *sign_commit;
static int autostash;
static int no_verify;
+static char *into_name;
static struct strategy all_strategy[] = {
{ "recursive", NO_TRIVIAL },
@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {
{ OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
N_("read message from file"), PARSE_OPT_NONEG,
NULL, 0, option_read_message },
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target")),
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
@@ -310,10 +313,9 @@ static int save_state(struct object_id *stash)
int len;
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
- const char *argv[] = {"stash", "create", NULL};
int rc = -1;
- cp.argv = argv;
+ strvec_pushl(&cp.args, "stash", "create", NULL);
cp.out = -1;
cp.git_cmd = 1;
@@ -1122,6 +1124,7 @@ static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *mer
opts.add_title = !have_message;
opts.shortlog_len = shortlog_len;
opts.credit_people = (0 < option_edit);
+ opts.into_name = into_name;
fmt_merge_msg(merge_names, merge_msg, &opts);
if (merge_msg->len)
@@ -1397,9 +1400,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (squash) {
if (fast_forward == FF_NO)
- die(_("You cannot combine --squash with --no-ff."));
+ die(_("options '%s' and '%s' cannot be used together"), "--squash", "--no-ff.");
if (option_commit > 0)
- die(_("You cannot combine --squash with --commit."));
+ die(_("options '%s' and '%s' cannot be used together"), "--squash", "--commit.");
/*
* squash can now silently disable option_commit - this is not
* a problem as it is only overriding the default, not a user
@@ -1578,6 +1581,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
remove_merge_branch_state(the_repository);
+ strbuf_release(&msg);
goto done;
} else if (!remoteheads->next && common->next)
;
@@ -1748,6 +1752,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = suggest_conflicts();
done:
+ strbuf_release(&buf);
free(branch_to_free);
return ret;
}
diff --git a/builtin/mktag.c b/builtin/mktag.c
index dddcccdd36..3b2dbbb37e 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
repl = lookup_replace_object(the_repository, tagged_oid);
ret = check_object_signature(the_repository, repl,
- buffer, size, type_name(*tagged_type));
+ buffer, size, type_name(*tagged_type),
+ NULL);
free(buffer);
return ret;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 6426bbdace..4480ba3982 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -7,7 +7,8 @@
#include "object-store.h"
#define BUILTIN_MIDX_WRITE_USAGE \
- N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]")
+ N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
+ "[--refs-snapshot=<path>]")
#define BUILTIN_MIDX_VERIFY_USAGE \
N_("git multi-pack-index [<options>] verify")
@@ -45,8 +46,10 @@ static char const * const builtin_multi_pack_index_usage[] = {
static struct opts_multi_pack_index {
const char *object_dir;
const char *preferred_pack;
+ const char *refs_snapshot;
unsigned long batch_size;
unsigned flags;
+ int stdin_packs;
} opts;
static struct option common_opts[] = {
@@ -77,6 +80,16 @@ static int git_multi_pack_index_write_config(const char *var, const char *value,
return 0;
}
+static void read_packs_from_stdin(struct string_list *to)
+{
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF)
+ string_list_append(to, buf.buf);
+ string_list_sort(to);
+
+ strbuf_release(&buf);
+}
+
static int cmd_multi_pack_index_write(int argc, const char **argv)
{
struct option *options;
@@ -88,6 +101,10 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
OPT_BIT(0, "progress", &opts.flags,
N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
+ N_("write multi-pack index containing only given indexes")),
+ OPT_FILENAME(0, "refs-snapshot", &opts.refs_snapshot,
+ N_("refs snapshot for selecting bitmap commits")),
OPT_END(),
};
@@ -110,8 +127,23 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
FREE_AND_NULL(options);
+ if (opts.stdin_packs) {
+ struct string_list packs = STRING_LIST_INIT_DUP;
+ int ret;
+
+ read_packs_from_stdin(&packs);
+
+ ret = write_midx_file_only(opts.object_dir, &packs,
+ opts.preferred_pack,
+ opts.refs_snapshot, opts.flags);
+
+ string_list_clear(&packs, 0);
+
+ return ret;
+
+ }
return write_midx_file(opts.object_dir, opts.preferred_pack,
- opts.flags);
+ opts.refs_snapshot, opts.flags);
}
static int cmd_multi_pack_index_verify(int argc, const char **argv)
@@ -135,6 +167,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
usage_with_options(builtin_multi_pack_index_verify_usage,
options);
+ FREE_AND_NULL(options);
+
return verify_midx_file(the_repository, opts.object_dir, opts.flags);
}
@@ -159,6 +193,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv)
usage_with_options(builtin_multi_pack_index_expire_usage,
options);
+ FREE_AND_NULL(options);
+
return expire_midx_packs(the_repository, opts.object_dir, opts.flags);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index b221d30014..27f60153a6 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -44,11 +44,20 @@ static struct rev_name *get_commit_rev_name(const struct commit *commit)
return is_valid_rev_name(name) ? name : NULL;
}
+static int effective_distance(int distance, int generation)
+{
+ return distance + (generation > 0 ? MERGE_TRAVERSAL_WEIGHT : 0);
+}
+
static int is_better_name(struct rev_name *name,
timestamp_t taggerdate,
+ int generation,
int distance,
int from_tag)
{
+ int name_distance = effective_distance(name->distance, name->generation);
+ int new_distance = effective_distance(distance, generation);
+
/*
* When comparing names based on tags, prefer names
* based on the older tag, even if it is farther away.
@@ -56,7 +65,7 @@ static int is_better_name(struct rev_name *name,
if (from_tag && name->from_tag)
return (name->taggerdate > taggerdate ||
(name->taggerdate == taggerdate &&
- name->distance > distance));
+ name_distance > new_distance));
/*
* We know that at least one of them is a non-tag at this point.
@@ -69,8 +78,8 @@ static int is_better_name(struct rev_name *name,
* We are now looking at two non-tags. Tiebreak to favor
* shorter hops.
*/
- if (name->distance != distance)
- return name->distance > distance;
+ if (name_distance != new_distance)
+ return name_distance > new_distance;
/* ... or tiebreak to favor older date */
if (name->taggerdate != taggerdate)
@@ -88,7 +97,7 @@ static struct rev_name *create_or_update_name(struct commit *commit,
struct rev_name *name = commit_rev_name_at(&rev_names, commit);
if (is_valid_rev_name(name)) {
- if (!is_better_name(name, taggerdate, distance, from_tag))
+ if (!is_better_name(name, taggerdate, generation, distance, from_tag))
return NULL;
/*
diff --git a/builtin/notes.c b/builtin/notes.c
index 71c59583a1..05d60483e8 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -134,14 +134,13 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid)
static void write_commented_object(int fd, const struct object_id *object)
{
- const char *show_args[5] =
- {"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
struct child_process show = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
struct strbuf cbuf = STRBUF_INIT;
/* Invoke "git show --stat --no-notes $object" */
- show.argv = show_args;
+ strvec_pushl(&show.args, "show", "--stat", "--no-notes",
+ oid_to_hex(object), NULL);
show.no_stdin = 1;
show.out = -1;
show.err = 0;
@@ -201,11 +200,12 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
static void write_note_data(struct note_data *d, struct object_id *oid)
{
if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
- error(_("unable to write note object"));
+ int status = die_message(_("unable to write note object"));
+
if (d->edit_path)
- error(_("the note contents have been left in %s"),
- d->edit_path);
- exit(128);
+ die_message(_("the note contents have been left in %s"),
+ d->edit_path);
+ exit(status);
}
}
@@ -861,15 +861,19 @@ static int merge(int argc, const char **argv, const char *prefix)
update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
+ struct worktree **worktrees;
const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
- wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+ worktrees = get_worktrees();
+ wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
+ default_notes_ref());
if (wt)
die(_("a notes merge into %s is already in-progress at %s"),
default_notes_ref(), wt->path);
+ free_worktrees(worktrees);
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
die(_("failed to store link to current notes ref (%s)"),
default_notes_ref());
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1a3dd445f8..ba2006f221 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3397,7 +3397,7 @@ static void read_object_list_from_stdin(void)
if (feof(stdin))
break;
if (!ferror(stdin))
- die("BUG: fgets returned NULL, not EOF, not error!");
+ BUG("fgets returned NULL, not EOF, not error!");
if (errno != EINTR)
die_errno("fgets");
clearerr(stdin);
@@ -4070,7 +4070,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
die(_("--thin cannot be used to build an indexable pack"));
if (keep_unreachable && unpack_unreachable)
- die(_("--keep-unreachable and --unpack-unreachable are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "--unpack-unreachable");
if (!rev_list_all || !rev_list_reflog || !rev_list_index)
unpack_unreachable_expiration = 0;
@@ -4148,11 +4148,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
read_packs_list_from_stdin();
if (rev_list_unpacked)
add_unreachable_loose_objects();
- } else if (!use_internal_rev_list)
+ } else if (!use_internal_rev_list) {
read_object_list_from_stdin();
- else {
+ } else {
get_object_list(rp.nr, rp.v);
- strvec_clear(&rp);
}
cleanup_preferred_base();
if (include_tag && nr_result)
@@ -4162,7 +4161,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
the_repository);
if (non_empty && !nr_result)
- return 0;
+ goto cleanup;
if (nr_result) {
trace2_region_enter("pack-objects", "prepare-pack",
the_repository);
@@ -4183,5 +4182,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
" pack-reused %"PRIu32),
written, written_delta, reused, reused_delta,
reuse_packfile_objects);
+
+cleanup:
+ strvec_clear(&rp);
+
return 0;
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 485c9a3c56..c2bcdc07db 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -26,10 +26,22 @@ static int prune_tmp_file(const char *fullpath)
return error("Could not stat '%s'", fullpath);
if (st.st_mtime > expire)
return 0;
- if (show_only || verbose)
- printf("Removing stale temporary file %s\n", fullpath);
- if (!show_only)
- unlink_or_warn(fullpath);
+ if (S_ISDIR(st.st_mode)) {
+ if (show_only || verbose)
+ printf("Removing stale temporary directory %s\n", fullpath);
+ if (!show_only) {
+ struct strbuf remove_dir_buf = STRBUF_INIT;
+
+ strbuf_addstr(&remove_dir_buf, fullpath);
+ remove_dir_recursively(&remove_dir_buf, 0);
+ strbuf_release(&remove_dir_buf);
+ }
+ } else {
+ if (show_only || verbose)
+ printf("Removing stale temporary file %s\n", fullpath);
+ if (!show_only)
+ unlink_or_warn(fullpath);
+ }
return 0;
}
diff --git a/builtin/pull.c b/builtin/pull.c
index cf6c56e2d8..100cbf9fb8 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -31,9 +31,8 @@
/**
* Parses the value of --rebase. If value is a false value, returns
* REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
- * "merges", returns REBASE_MERGES. If value is "preserve", returns
- * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
- * fatal is true, otherwise returns REBASE_INVALID.
+ * "merges", returns REBASE_MERGES. If value is a invalid value, dies with
+ * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
*/
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
@@ -85,6 +84,7 @@ static char *opt_edit;
static char *cleanup_arg;
static char *opt_ff;
static char *opt_verify_signatures;
+static char *opt_verify;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
@@ -127,7 +127,7 @@ static struct option pull_options[] = {
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
OPT_CALLBACK_F('r', "rebase", &opt_rebase,
- "(false|true|merges|preserve|interactive)",
+ "(false|true|merges|interactive)",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
@@ -161,6 +161,9 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
N_("abort if fast-forward is not possible"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+ OPT_PASSTHRU(0, "verify", &opt_verify, NULL,
+ N_("control use of pre-merge-commit and commit-msg hooks"),
+ PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
@@ -676,6 +679,8 @@ static int run_merge(void)
strvec_pushf(&args, "--cleanup=%s", cleanup_arg);
if (opt_ff)
strvec_push(&args, opt_ff);
+ if (opt_verify)
+ strvec_push(&args, opt_verify);
if (opt_verify_signatures)
strvec_push(&args, opt_verify_signatures);
strvec_pushv(&args, opt_strategies.v);
@@ -884,8 +889,6 @@ static int run_rebase(const struct object_id *newbase,
/* Options passed to git-rebase */
if (opt_rebase == REBASE_MERGES)
strvec_push(&args, "--rebase-merges");
- else if (opt_rebase == REBASE_PRESERVE)
- strvec_push(&args, "--preserve-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
strvec_push(&args, "--interactive");
if (opt_diffstat)
@@ -934,13 +937,40 @@ static int get_can_ff(struct object_id *orig_head,
return ret;
}
+/*
+ * Is orig_head a descendant of _all_ merge_heads?
+ * Unfortunately is_descendant_of() cannot be used as it asks
+ * if orig_head is a descendant of at least one of them.
+ */
+static int already_up_to_date(struct object_id *orig_head,
+ struct oid_array *merge_heads)
+{
+ int i;
+ struct commit *ours;
+
+ ours = lookup_commit_reference(the_repository, orig_head);
+ for (i = 0; i < merge_heads->nr; i++) {
+ struct commit_list *list = NULL;
+ struct commit *theirs;
+ int ok;
+
+ theirs = lookup_commit_reference(the_repository, &merge_heads->oid[i]);
+ commit_list_insert(theirs, &list);
+ ok = repo_is_descendant_of(the_repository, ours, list);
+ free_commit_list(list);
+ if (!ok)
+ return 0;
+ }
+ return 1;
+}
+
static void show_advice_pull_non_ff(void)
{
advise(_("You have divergent branches and need to specify how to reconcile them.\n"
"You can do so by running one of the following commands sometime before\n"
"your next pull:\n"
"\n"
- " git config pull.rebase false # merge (the default strategy)\n"
+ " git config pull.rebase false # merge\n"
" git config pull.rebase true # rebase\n"
" git config pull.ff only # fast-forward only\n"
"\n"
@@ -958,11 +988,14 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
struct object_id rebase_fork_point;
int rebase_unspecified = 0;
int can_ff;
+ int divergent;
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
git_config(git_pull_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
@@ -1072,15 +1105,16 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
}
can_ff = get_can_ff(&orig_head, &merge_heads);
+ divergent = !can_ff && !already_up_to_date(&orig_head, &merge_heads);
/* ff-only takes precedence over rebase */
if (opt_ff && !strcmp(opt_ff, "--ff-only")) {
- if (!can_ff)
+ if (divergent)
die_ff_impossible();
opt_rebase = REBASE_FALSE;
}
/* If no action specified and we can't fast forward, then warn. */
- if (!opt_ff && rebase_unspecified && !can_ff) {
+ if (!opt_ff && rebase_unspecified && divergent) {
show_advice_pull_non_ff();
die(_("Need to specify how to reconcile divergent branches."));
}
diff --git a/builtin/push.c b/builtin/push.c
index 4b026ce6c6..359db90321 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -589,7 +589,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
- die(_("--delete is incompatible with --all, --mirror and --tags"));
+ die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
@@ -627,18 +627,18 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (flags & TRANSPORT_PUSH_ALL) {
if (tags)
- die(_("--all and --tags are incompatible"));
+ 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(_("--mirror and --tags are incompatible"));
+ 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(_("--all and --mirror are incompatible"));
+ 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/rebase.c b/builtin/rebase.c
index 8c6393f6d7..36490d06c8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -48,8 +48,7 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
enum rebase_type {
REBASE_UNSPECIFIED = -1,
REBASE_APPLY,
- REBASE_MERGE,
- REBASE_PRESERVE_MERGES
+ REBASE_MERGE
};
enum empty_type {
@@ -163,12 +162,7 @@ enum action {
ACTION_ABORT,
ACTION_QUIT,
ACTION_EDIT_TODO,
- ACTION_SHOW_CURRENT_PATCH,
- ACTION_SHORTEN_OIDS,
- ACTION_EXPAND_OIDS,
- ACTION_CHECK_TODO_LIST,
- ACTION_REARRANGE_SQUASH,
- ACTION_ADD_EXEC
+ ACTION_SHOW_CURRENT_PATCH
};
static const char *action_names[] = { "undefined",
@@ -179,81 +173,6 @@ static const char *action_names[] = { "undefined",
"edit_todo",
"show_current_patch" };
-static int add_exec_commands(struct string_list *commands)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- todo_list_add_exec_commands(&todo_list, commands);
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int rearrange_squash_in_todo_file(void)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res = 0;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_rearrange_squash(&todo_list);
- if (!res)
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
-
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int transform_todo_file(unsigned flags)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
- NULL, NULL, -1, flags);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
static int edit_todo_file(unsigned flags)
{
const char *todo_file = rebase_path_todo();
@@ -403,7 +322,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
- flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
@@ -439,24 +357,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
break;
}
- case ACTION_SHORTEN_OIDS:
- case ACTION_EXPAND_OIDS:
- ret = transform_todo_file(flags);
- break;
- case ACTION_CHECK_TODO_LIST:
- ret = check_todo_list_from_file(the_repository);
- break;
- case ACTION_REARRANGE_SQUASH:
- ret = rearrange_squash_in_todo_file();
- break;
- case ACTION_ADD_EXEC: {
- struct string_list commands = STRING_LIST_INIT_DUP;
-
- split_exec_commands(opts->cmd, &commands);
- ret = add_exec_commands(&commands);
- string_list_clear(&commands, 0);
- break;
- }
default:
BUG("invalid command '%d'", command);
}
@@ -478,105 +378,9 @@ static int parse_opt_keep_empty(const struct option *opt, const char *arg,
return 0;
}
-static const char * const builtin_rebase_interactive_usage[] = {
- N_("git rebase--interactive [<options>]"),
- NULL
-};
-
-int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
-{
- struct rebase_options opts = REBASE_OPTIONS_INIT;
- struct object_id squash_onto = *null_oid();
- enum action command = ACTION_NONE;
- struct option options[] = {
- OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
- REBASE_FORCE),
- OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
- N_("keep commits which start empty"),
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
- parse_opt_keep_empty),
- OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
- N_("allow commits with empty messages"),
- PARSE_OPT_HIDDEN),
- OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
- OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
- N_("keep original branch points of cousins")),
- OPT_BOOL(0, "autosquash", &opts.autosquash,
- N_("move commits that begin with squash!/fixup!")),
- OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
- OPT_BIT('v', "verbose", &opts.flags,
- N_("display a diffstat of what changed upstream"),
- REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
- OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
- ACTION_CONTINUE),
- OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
- OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
- ACTION_EDIT_TODO),
- OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
- ACTION_SHOW_CURRENT_PATCH),
- OPT_CMDMODE(0, "shorten-ids", &command,
- N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
- OPT_CMDMODE(0, "expand-ids", &command,
- N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
- OPT_CMDMODE(0, "check-todo-list", &command,
- N_("check the todo list"), ACTION_CHECK_TODO_LIST),
- OPT_CMDMODE(0, "rearrange-squash", &command,
- N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
- OPT_CMDMODE(0, "add-exec-commands", &command,
- N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
- { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
- N_("restrict-revision"), N_("restrict revision"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
- N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
- { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
- N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
- 0 },
- OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
- { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
- N_("GPG-sign commits"),
- PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
- N_("rebase strategy")),
- OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
- N_("strategy options")),
- OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
- N_("the branch or commit to checkout")),
- OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
- OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
- OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
- OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
- N_("automatically re-schedule any `exec` that fails")),
- OPT_END()
- };
-
- opts.rebase_cousins = -1;
-
- if (argc == 1)
- usage_with_options(builtin_rebase_interactive_usage, options);
-
- argc = parse_options(argc, argv, prefix, options,
- builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
-
- prepare_repo_settings(the_repository);
- the_repository->settings.command_requires_full_index = 0;
-
- if (!is_null_oid(&squash_onto))
- opts.squash_onto = &squash_onto;
-
- if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
- warning(_("--[no-]rebase-cousins has no effect without "
- "--rebase-merges"));
-
- return !!run_sequencer_rebase(&opts, command);
-}
-
static int is_merge(struct rebase_options *opts)
{
- return opts->type == REBASE_MERGE ||
- opts->type == REBASE_PRESERVE_MERGES;
+ return opts->type == REBASE_MERGE;
}
static void imply_merge(struct rebase_options *opts, const char *option)
@@ -586,7 +390,6 @@ static void imply_merge(struct rebase_options *opts, const char *option)
die(_("%s requires the merge backend"), option);
break;
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
break;
default:
opts->type = REBASE_MERGE; /* implied */
@@ -765,17 +568,6 @@ static int finish_rebase(struct rebase_options *opts)
return ret;
}
-static void add_var(struct strbuf *buf, const char *name, const char *value)
-{
- if (!value)
- strbuf_addf(buf, "unset %s; ", name);
- else {
- strbuf_addf(buf, "%s=", name);
- sq_quote_buf(buf, value);
- strbuf_addstr(buf, "; ");
- }
-}
-
static int move_to_original_branch(struct rebase_options *opts)
{
struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
@@ -932,10 +724,7 @@ static int run_am(struct rebase_options *opts)
static int run_specific_rebase(struct rebase_options *opts, enum action action)
{
- const char *argv[] = { NULL, NULL };
- struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
int status;
- const char *backend, *backend_func;
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
@@ -952,87 +741,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
}
status = run_sequencer_rebase(opts, action);
- goto finished_rebase;
- }
-
- if (opts->type == REBASE_APPLY) {
+ } else if (opts->type == REBASE_APPLY)
status = run_am(opts);
- goto finished_rebase;
- }
-
- add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
- add_var(&script_snippet, "state_dir", opts->state_dir);
-
- add_var(&script_snippet, "upstream_name", opts->upstream_name);
- add_var(&script_snippet, "upstream", opts->upstream ?
- oid_to_hex(&opts->upstream->object.oid) : NULL);
- add_var(&script_snippet, "head_name",
- opts->head_name ? opts->head_name : "detached HEAD");
- add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
- add_var(&script_snippet, "onto", opts->onto ?
- oid_to_hex(&opts->onto->object.oid) : NULL);
- add_var(&script_snippet, "onto_name", opts->onto_name);
- add_var(&script_snippet, "revisions", opts->revisions);
- add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
- oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- sq_quote_argv_pretty(&buf, opts->git_am_opts.v);
- add_var(&script_snippet, "git_am_opt", buf.buf);
- strbuf_release(&buf);
- add_var(&script_snippet, "verbose",
- opts->flags & REBASE_VERBOSE ? "t" : "");
- add_var(&script_snippet, "diffstat",
- opts->flags & REBASE_DIFFSTAT ? "t" : "");
- add_var(&script_snippet, "force_rebase",
- opts->flags & REBASE_FORCE ? "t" : "");
- if (opts->switch_to)
- add_var(&script_snippet, "switch_to", opts->switch_to);
- add_var(&script_snippet, "action", opts->action ? opts->action : "");
- add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
- add_var(&script_snippet, "allow_rerere_autoupdate",
- opts->allow_rerere_autoupdate ?
- opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
- "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
- add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
- add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
- add_var(&script_snippet, "cmd", opts->cmd);
- add_var(&script_snippet, "allow_empty_message",
- opts->allow_empty_message ? "--allow-empty-message" : "");
- add_var(&script_snippet, "rebase_merges",
- opts->rebase_merges ? "t" : "");
- add_var(&script_snippet, "rebase_cousins",
- opts->rebase_cousins ? "t" : "");
- add_var(&script_snippet, "strategy", opts->strategy);
- add_var(&script_snippet, "strategy_opts", opts->strategy_opts);
- add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
- add_var(&script_snippet, "squash_onto",
- opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
- add_var(&script_snippet, "git_format_patch_opt",
- opts->git_format_patch_opt.buf);
-
- if (is_merge(opts) &&
- !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
- strbuf_addstr(&script_snippet,
- "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
- opts->autosquash = 0;
- }
-
- switch (opts->type) {
- case REBASE_PRESERVE_MERGES:
- backend = "git-rebase--preserve-merges";
- backend_func = "git_rebase__preserve_merges";
- break;
- default:
+ else
BUG("Unhandled rebase type %d", opts->type);
- break;
- }
-
- strbuf_addf(&script_snippet,
- ". git-sh-setup && . %s && %s", backend, backend_func);
- argv[0] = script_snippet.buf;
- status = run_command_v_opt(argv, RUN_USING_SHELL);
-finished_rebase:
if (opts->dont_finish_rebase)
; /* do nothing */
else if (opts->type == REBASE_MERGE)
@@ -1050,8 +763,6 @@ finished_rebase:
die("Nothing to do");
}
- strbuf_release(&script_snippet);
-
return status ? -1 : 0;
}
@@ -1187,7 +898,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
return 0;
}
-/* -i followed by -p is still explicitly interactive, but -p alone is not */
+/* -i followed by -r is still explicitly interactive, but -r alone is not */
static int parse_opt_interactive(const struct option *opt, const char *arg,
int unset)
{
@@ -1305,6 +1016,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
+ int preserve_merges_selected = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1369,10 +1081,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive),
- OPT_SET_INT_F('p', "preserve-merges", &options.type,
+ OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"),
- REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
+ 1, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
N_("how to handle commits that become empty"),
@@ -1443,8 +1155,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/rewritten", merge_dir());
if (is_directory(buf.buf)) {
- options.type = REBASE_PRESERVE_MERGES;
- options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+ die("`rebase -p` is no longer supported");
} else {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
@@ -1465,6 +1176,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
builtin_rebase_options,
builtin_rebase_usage, 0);
+ if (preserve_merges_selected)
+ die(_("--preserve-merges was replaced by --rebase-merges"));
+
if (action != ACTION_NONE && total_argc != 2) {
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
@@ -1474,19 +1188,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
- if (options.type == REBASE_PRESERVE_MERGES)
- warning(_("git rebase --preserve-merges is deprecated. "
- "Use --rebase-merges instead."));
-
if (keep_base) {
if (options.onto_name)
- die(_("cannot combine '--keep-base' with '--onto'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
if (options.root)
- die(_("cannot combine '--keep-base' with '--root'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
}
if (options.root && options.fork_point > 0)
- die(_("cannot combine '--root' with '--fork-point'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
@@ -1697,7 +1407,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.ignore_date)
strvec_push(&options.git_am_opts, "--ignore-date");
} else {
- /* REBASE_MERGE and PRESERVE_MERGES */
+ /* REBASE_MERGE */
if (ignore_whitespace) {
string_list_append(&strategy_options,
"ignore-space-change");
@@ -1723,7 +1433,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
case REBASE_APPLY:
die(_("--strategy requires --merge or --interactive"));
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
/* compatible */
break;
case REBASE_UNSPECIFIED:
@@ -1751,8 +1460,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (i >= 0) {
if (is_merge(&options))
- die(_("cannot combine apply options with "
- "merge options"));
+ die(_("apply options and merge options "
+ "cannot be used together"));
else
options.type = REBASE_APPLY;
}
@@ -1775,7 +1484,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
switch (options.type) {
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
options.state_dir = merge_dir();
break;
case REBASE_APPLY:
@@ -1800,28 +1508,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.signoff) {
- if (options.type == REBASE_PRESERVE_MERGES)
- die("cannot combine '--signoff' with "
- "'--preserve-merges'");
strvec_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}
- if (options.type == REBASE_PRESERVE_MERGES) {
- /*
- * Note: incompatibility with --signoff handled in signoff block above
- * Note: incompatibility with --interactive is just a strong warning;
- * git-rebase.txt caveats with "unless you know what you are doing"
- */
- if (options.rebase_merges)
- die(_("cannot combine '--preserve-merges' with "
- "'--rebase-merges'"));
-
- if (options.reschedule_failed_exec)
- die(_("error: cannot combine '--preserve-merges' with "
- "'--reschedule-failed-exec'"));
- }
-
if (!options.root) {
if (argc < 1) {
struct branch *branch;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 25cc0c907e..9f4a0b816c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -135,6 +135,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
if (status)
return status;
+ status = git_gpg_config(var, value, NULL);
+ if (status)
+ return status;
+
if (strcmp(var, "receive.denydeletes") == 0) {
deny_deletes = git_config_bool(var, value);
return 0;
@@ -171,7 +175,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
strbuf_addf(&fsck_msg_types, "%c%s=%s",
fsck_msg_types.len ? ',' : '=', var, value);
else
- warning("Skipping unknown msg id '%s'", var);
+ warning("skipping unknown msg id '%s'", var);
return 0;
}
@@ -765,8 +769,10 @@ static void prepare_push_cert_sha1(struct child_process *proc)
memset(&sigcheck, '\0', sizeof(sigcheck));
bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
- check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
- push_cert.len - bogs, &sigcheck);
+ sigcheck.payload = xmemdupz(push_cert.buf, bogs);
+ sigcheck.payload_len = bogs;
+ check_signature(&sigcheck, push_cert.buf + bogs,
+ push_cert.len - bogs);
nonce_status = check_nonce(push_cert.buf, bogs);
}
@@ -808,16 +814,13 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
- const char *argv[2];
int code;
+ const char *hook_path = find_hook(hook_name);
- argv[0] = find_hook(hook_name);
- if (!argv[0])
+ if (!hook_path)
return 0;
- argv[1] = NULL;
-
- proc.argv = argv;
+ strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = hook_name;
@@ -939,23 +942,21 @@ static int run_receive_hook(struct command *commands,
static int run_update_hook(struct command *cmd)
{
- const char *argv[5];
struct child_process proc = CHILD_PROCESS_INIT;
int code;
+ const char *hook_path = find_hook("update");
- argv[0] = find_hook("update");
- if (!argv[0])
+ if (!hook_path)
return 0;
- argv[1] = cmd->ref_name;
- argv[2] = oid_to_hex(&cmd->old_oid);
- argv[3] = oid_to_hex(&cmd->new_oid);
- argv[4] = NULL;
+ strvec_push(&proc.args, hook_path);
+ strvec_push(&proc.args, cmd->ref_name);
+ strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
+ strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
- proc.argv = argv;
proc.trace2_hook_name = "update";
code = start_command(&proc);
@@ -1113,22 +1114,20 @@ static int run_proc_receive_hook(struct command *commands,
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
struct command *cmd;
- const char *argv[2];
struct packet_reader reader;
struct strbuf cap = STRBUF_INIT;
struct strbuf errmsg = STRBUF_INIT;
int hook_use_push_options = 0;
int version = 0;
int code;
+ const char *hook_path = find_hook("proc-receive");
- argv[0] = find_hook("proc-receive");
- if (!argv[0]) {
+ if (!hook_path) {
rp_error("cannot find hook 'proc-receive'");
return -1;
}
- argv[1] = NULL;
- proc.argv = argv;
+ strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.out = -1;
proc.trace2_hook_name = "proc-receive";
@@ -1366,23 +1365,11 @@ static const char *push_to_deploy(unsigned char *sha1,
struct strvec *env,
const char *work_tree)
{
- const char *update_refresh[] = {
- "update-index", "-q", "--ignore-submodules", "--refresh", NULL
- };
- const char *diff_files[] = {
- "diff-files", "--quiet", "--ignore-submodules", "--", NULL
- };
- const char *diff_index[] = {
- "diff-index", "--quiet", "--cached", "--ignore-submodules",
- NULL, "--", NULL
- };
- const char *read_tree[] = {
- "read-tree", "-u", "-m", NULL, NULL
- };
struct child_process child = CHILD_PROCESS_INIT;
- child.argv = update_refresh;
- child.env = env->v;
+ strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
+ "--refresh", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1392,8 +1379,9 @@ static const char *push_to_deploy(unsigned char *sha1,
/* run_command() does not clean up completely; reinitialize */
child_process_init(&child);
- child.argv = diff_files;
- child.env = env->v;
+ strvec_pushl(&child.args, "diff-files", "--quiet",
+ "--ignore-submodules", "--", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1401,12 +1389,13 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has unstaged changes";
- /* diff-index with either HEAD or an empty tree */
- diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
-
child_process_init(&child);
- child.argv = diff_index;
- child.env = env->v;
+ strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
+ "--ignore-submodules",
+ /* diff-index with either HEAD or an empty tree */
+ head_has_history() ? "HEAD" : empty_tree_oid_hex(),
+ "--", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
@@ -1414,10 +1403,10 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has staged changes";
- read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
- child.argv = read_tree;
- child.env = env->v;
+ strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
+ NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
@@ -1445,29 +1434,22 @@ static const char *push_to_checkout(unsigned char *hash,
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
- const char *retval, *work_tree, *git_dir = NULL;
+ const char *retval, *git_dir;
struct strvec env = STRVEC_INIT;
- if (worktree && worktree->path)
- work_tree = worktree->path;
- else if (git_work_tree_cfg)
- work_tree = git_work_tree_cfg;
- else
- work_tree = "..";
+ if (!worktree || !worktree->path)
+ BUG("worktree->path must be non-NULL");
- if (is_bare_repository())
+ if (worktree->is_bare)
return "denyCurrentBranch = updateInstead needs a worktree";
- if (worktree)
- git_dir = get_worktree_git_dir(worktree);
- if (!git_dir)
- git_dir = get_git_dir();
+ git_dir = get_worktree_git_dir(worktree);
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
if (!hook_exists(push_to_checkout_hook))
- retval = push_to_deploy(sha1, &env, work_tree);
+ retval = push_to_deploy(sha1, &env, worktree->path);
else
- retval = push_to_checkout(sha1, &env, work_tree);
+ retval = push_to_checkout(sha1, &env, worktree->path);
strvec_clear(&env);
return retval;
@@ -1482,19 +1464,22 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object_id *old_oid = &cmd->old_oid;
struct object_id *new_oid = &cmd->new_oid;
int do_update_worktree = 0;
- const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
+ struct worktree **worktrees = get_worktrees();
+ const struct worktree *worktree =
+ find_shared_symref(worktrees, "HEAD", name);
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
- return "funny refname";
+ ret = "funny refname";
+ goto out;
}
strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
free(namespaced_name);
namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
- if (worktree) {
+ if (worktree && !worktree->is_bare) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
@@ -1506,7 +1491,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
rp_error("refusing to update checked out branch: %s", name);
if (deny_current_branch == DENY_UNCONFIGURED)
refuse_unconfigured_deny();
- return "branch is currently checked out";
+ ret = "branch is currently checked out";
+ goto out;
case DENY_UPDATE_INSTEAD:
/* pass -- let other checks intervene first */
do_update_worktree = 1;
@@ -1517,13 +1503,15 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
error("unpack should have generated %s, "
"but I can't find it!", oid_to_hex(new_oid));
- return "bad pack";
+ ret = "bad pack";
+ goto out;
}
if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
if (deny_deletes && starts_with(name, "refs/heads/")) {
rp_error("denying ref deletion for %s", name);
- return "deletion prohibited";
+ ret = "deletion prohibited";
+ goto out;
}
if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
@@ -1539,9 +1527,11 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (deny_delete_current == DENY_UNCONFIGURED)
refuse_unconfigured_deny_delete_current();
rp_error("refusing to delete the current branch: %s", name);
- return "deletion of the current branch prohibited";
+ ret = "deletion of the current branch prohibited";
+ goto out;
default:
- return "Invalid denyDeleteCurrent setting";
+ ret = "Invalid denyDeleteCurrent setting";
+ goto out;
}
}
}
@@ -1559,25 +1549,28 @@ static const char *update(struct command *cmd, struct shallow_info *si)
old_object->type != OBJ_COMMIT ||
new_object->type != OBJ_COMMIT) {
error("bad sha1 objects for %s", name);
- return "bad ref";
+ ret = "bad ref";
+ goto out;
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
if (!in_merge_bases(old_commit, new_commit)) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
- return "non-fast-forward";
+ ret = "non-fast-forward";
+ goto out;
}
}
if (run_update_hook(cmd)) {
rp_error("hook declined to update %s", name);
- return "hook declined";
+ ret = "hook declined";
+ goto out;
}
if (do_update_worktree) {
- ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
+ ret = update_worktree(new_oid->hash, worktree);
if (ret)
- return ret;
+ goto out;
}
if (is_null_oid(new_oid)) {
@@ -1585,9 +1578,9 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (!parse_object(the_repository, old_oid)) {
old_oid = NULL;
if (ref_exists(name)) {
- rp_warning("Allowing deletion of corrupt ref.");
+ rp_warning("allowing deletion of corrupt ref");
} else {
- rp_warning("Deleting a non-existent ref.");
+ rp_warning("deleting a non-existent ref");
cmd->did_not_exist = 1;
}
}
@@ -1596,17 +1589,19 @@ static const char *update(struct command *cmd, struct shallow_info *si)
old_oid,
0, "push", &err)) {
rp_error("%s", err.buf);
- strbuf_release(&err);
- return "failed to delete";
+ ret = "failed to delete";
+ } else {
+ ret = NULL; /* good */
}
strbuf_release(&err);
- return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
if (shallow_update && si->shallow_ref[cmd->index] &&
- update_shallow_ref(cmd, si))
- return "shallow error";
+ update_shallow_ref(cmd, si)) {
+ ret = "shallow error";
+ goto out;
+ }
if (ref_transaction_update(transaction,
namespaced_name,
@@ -1614,14 +1609,16 @@ static const char *update(struct command *cmd, struct shallow_info *si)
0, "push",
&err)) {
rp_error("%s", err.buf);
- strbuf_release(&err);
-
- return "failed to update ref";
+ ret = "failed to update ref";
+ } else {
+ ret = NULL; /* good */
}
strbuf_release(&err);
-
- return NULL; /* good */
}
+
+out:
+ free_worktrees(worktrees);
+ return ret;
}
static void run_update_post_hook(struct command *commands)
@@ -2209,13 +2206,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
strvec_push(&child.args, alt_shallow_file);
}
- tmp_objdir = tmp_objdir_create();
+ tmp_objdir = tmp_objdir_create("incoming");
if (!tmp_objdir) {
if (err_fd > 0)
close(err_fd);
return "unable to create temporary object directory";
}
- child.env = tmp_objdir_env(tmp_objdir);
+ if (tmp_objdir)
+ strvec_pushv(&child.env_array, tmp_objdir_env(tmp_objdir));
/*
* Normally we just pass the tmp_objdir environment to the child
@@ -2486,9 +2484,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
if (argc > 1)
- usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+ usage_msg_opt(_("too many arguments"), receive_pack_usage, options);
if (argc == 0)
- usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
+ usage_msg_opt(_("you must specify a directory"), receive_pack_usage, options);
service_dir = argv[0];
@@ -2562,25 +2560,25 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
&push_options);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
+ sigchain_push(SIGPIPE, SIG_IGN);
if (report_status_v2)
report_v2(commands, unpack_status);
else if (report_status)
report(commands, unpack_status);
+ sigchain_pop(SIGPIPE);
run_receive_hook(commands, "post-receive", 1,
&push_options);
run_update_post_hook(commands);
string_list_clear(&push_options, 0);
if (auto_gc) {
- const char *argv_gc_auto[] = {
- "gc", "--auto", "--quiet", NULL,
- };
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;
- proc.argv = argv_gc_auto;
+ strvec_pushl(&proc.args, "gc", "--auto", "--quiet",
+ NULL);
if (!start_command(&proc)) {
if (use_sideband)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index bd4c669918..a4b1dd27e1 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -28,7 +28,6 @@ static timestamp_t default_reflog_expire;
static timestamp_t default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
- struct rev_info revs;
int stalefix;
timestamp_t expire_total;
timestamp_t expire_unreachable;
@@ -46,18 +45,12 @@ struct expire_reflog_policy_cb {
struct cmd_reflog_expire_cb cmd;
struct commit *tip_commit;
struct commit_list *tips;
+ unsigned int dry_run:1;
};
-struct collected_reflog {
- struct object_id oid;
- char reflog[FLEX_ARRAY];
-};
-
-struct collect_reflog_cb {
- struct collected_reflog **e;
- int alloc;
- int nr;
- struct worktree *wt;
+struct worktree_reflogs {
+ struct worktree *worktree;
+ struct string_list reflogs;
};
/* Remember to update object flag allocation in object.h */
@@ -310,10 +303,15 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no
return 1;
if (timestamp < cb->cmd.expire_unreachable) {
- if (cb->unreachable_expire_kind == UE_ALWAYS)
- return 1;
- if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
return 1;
+ case UE_NORMAL:
+ case UE_HEAD:
+ if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+ return 1;
+ break;
+ }
}
if (cb->cmd.recno && --(cb->cmd.recno) == 0)
@@ -322,6 +320,28 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no
return 0;
}
+static int should_expire_reflog_ent_verbose(struct object_id *ooid,
+ struct object_id *noid,
+ const char *email,
+ timestamp_t timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct expire_reflog_policy_cb *cb = cb_data;
+ int expire;
+
+ expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
+ message, cb);
+
+ if (!expire)
+ printf("keep %s", message);
+ else if (cb->dry_run)
+ printf("would prune %s", message);
+ else
+ printf("prune %s", message);
+
+ return expire;
+}
+
static int push_tip_to_list(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
@@ -355,75 +375,71 @@ static void reflog_expiry_prepare(const char *refname,
void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
+ struct commit_list *elem;
+ struct commit *commit = NULL;
if (!cb->cmd.expire_unreachable || is_head(refname)) {
- cb->tip_commit = NULL;
cb->unreachable_expire_kind = UE_HEAD;
} else {
- cb->tip_commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
- if (!cb->tip_commit)
- cb->unreachable_expire_kind = UE_ALWAYS;
- else
- cb->unreachable_expire_kind = UE_NORMAL;
+ commit = lookup_commit(the_repository, oid);
+ cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
}
if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
cb->unreachable_expire_kind = UE_ALWAYS;
- cb->mark_list = NULL;
- cb->tips = NULL;
- if (cb->unreachable_expire_kind != UE_ALWAYS) {
- if (cb->unreachable_expire_kind == UE_HEAD) {
- struct commit_list *elem;
-
- for_each_ref(push_tip_to_list, &cb->tips);
- for (elem = cb->tips; elem; elem = elem->next)
- commit_list_insert(elem->item, &cb->mark_list);
- } else {
- commit_list_insert(cb->tip_commit, &cb->mark_list);
- }
- cb->mark_limit = cb->cmd.expire_total;
- mark_reachable(cb);
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
+ return;
+ case UE_HEAD:
+ for_each_ref(push_tip_to_list, &cb->tips);
+ for (elem = cb->tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb->mark_list);
+ break;
+ case UE_NORMAL:
+ commit_list_insert(commit, &cb->mark_list);
+ /* For reflog_expiry_cleanup() below */
+ cb->tip_commit = commit;
}
+ cb->mark_limit = cb->cmd.expire_total;
+ mark_reachable(cb);
}
static void reflog_expiry_cleanup(void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
+ struct commit_list *elem;
- if (cb->unreachable_expire_kind != UE_ALWAYS) {
- if (cb->unreachable_expire_kind == UE_HEAD) {
- struct commit_list *elem;
- for (elem = cb->tips; elem; elem = elem->next)
- clear_commit_marks(elem->item, REACHABLE);
- free_commit_list(cb->tips);
- } else {
- clear_commit_marks(cb->tip_commit, REACHABLE);
- }
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
+ return;
+ case UE_HEAD:
+ for (elem = cb->tips; elem; elem = elem->next)
+ clear_commit_marks(elem->item, REACHABLE);
+ free_commit_list(cb->tips);
+ break;
+ case UE_NORMAL:
+ clear_commit_marks(cb->tip_commit, REACHABLE);
+ break;
}
}
static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
{
- struct collected_reflog *e;
- struct collect_reflog_cb *cb = cb_data;
+ struct worktree_reflogs *cb = cb_data;
+ struct worktree *worktree = cb->worktree;
struct strbuf newref = STRBUF_INIT;
/*
* Avoid collecting the same shared ref multiple times because
* they are available via all worktrees.
*/
- if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+ if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
return 0;
- strbuf_worktree_ref(cb->wt, &newref, ref);
- FLEX_ALLOC_STR(e, reflog, newref.buf);
- strbuf_release(&newref);
+ strbuf_worktree_ref(worktree, &newref, ref);
+ string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
- oidcpy(&e->oid, oid);
- ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
- cb->e[cb->nr++] = e;
return 0;
}
@@ -541,11 +557,13 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
- struct expire_reflog_policy_cb cb;
+ struct cmd_reflog_expire_cb cmd = { 0 };
timestamp_t now = time(NULL);
int i, status, do_all, all_worktrees = 1;
int explicit_expiry = 0;
unsigned int flags = 0;
+ int verbose = 0;
+ reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
default_reflog_expire = now - 90 * 24 * 3600;
@@ -553,10 +571,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
do_all = status = 0;
- memset(&cb, 0, sizeof(cb));
- cb.cmd.expire_total = default_reflog_expire;
- cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
+ cmd.expire_total = default_reflog_expire;
+ cmd.expire_unreachable = default_reflog_expire_unreachable;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -564,17 +581,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
flags |= EXPIRE_REFLOGS_DRY_RUN;
else if (skip_prefix(arg, "--expire=", &arg)) {
- if (parse_expiry_date(arg, &cb.cmd.expire_total))
+ if (parse_expiry_date(arg, &cmd.expire_total))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_TOTAL;
}
else if (skip_prefix(arg, "--expire-unreachable=", &arg)) {
- if (parse_expiry_date(arg, &cb.cmd.expire_unreachable))
+ if (parse_expiry_date(arg, &cmd.expire_unreachable))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_UNREACH;
}
else if (!strcmp(arg, "--stale-fix"))
- cb.cmd.stalefix = 1;
+ cmd.stalefix = 1;
else if (!strcmp(arg, "--rewrite"))
flags |= EXPIRE_REFLOGS_REWRITE;
else if (!strcmp(arg, "--updateref"))
@@ -584,7 +601,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
else if (!strcmp(arg, "--single-worktree"))
all_worktrees = 0;
else if (!strcmp(arg, "--verbose"))
- flags |= EXPIRE_REFLOGS_VERBOSE;
+ verbose = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -595,54 +612,65 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
break;
}
+ if (verbose)
+ should_prune_fn = should_expire_reflog_ent_verbose;
+
/*
* We can trust the commits and objects reachable from refs
* even in older repository. We cannot trust what's reachable
* from reflog if the repository was pruned with older git.
*/
- if (cb.cmd.stalefix) {
- repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
- cb.cmd.revs.do_not_die_on_missing_tree = 1;
- cb.cmd.revs.ignore_missing = 1;
- cb.cmd.revs.ignore_missing_links = 1;
- if (flags & EXPIRE_REFLOGS_VERBOSE)
+ if (cmd.stalefix) {
+ struct rev_info revs;
+
+ repo_init_revisions(the_repository, &revs, prefix);
+ revs.do_not_die_on_missing_tree = 1;
+ revs.ignore_missing = 1;
+ revs.ignore_missing_links = 1;
+ if (verbose)
printf(_("Marking reachable objects..."));
- mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
- if (flags & EXPIRE_REFLOGS_VERBOSE)
+ mark_reachable_objects(&revs, 0, 0, NULL);
+ if (verbose)
putchar('\n');
}
if (do_all) {
- struct collect_reflog_cb collected;
+ struct worktree_reflogs collected = {
+ .reflogs = STRING_LIST_INIT_DUP,
+ };
+ struct string_list_item *item;
struct worktree **worktrees, **p;
- int i;
- memset(&collected, 0, sizeof(collected));
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
if (!all_worktrees && !(*p)->is_current)
continue;
- collected.wt = *p;
+ collected.worktree = *p;
refs_for_each_reflog(get_worktree_ref_store(*p),
collect_reflog, &collected);
}
free_worktrees(worktrees);
- for (i = 0; i < collected.nr; i++) {
- struct collected_reflog *e = collected.e[i];
- set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
- status |= reflog_expire(e->reflog, flags,
+ for_each_string_list_item(item, &collected.reflogs) {
+ struct expire_reflog_policy_cb cb = {
+ .cmd = cmd,
+ .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+ };
+
+ set_reflog_expiry_param(&cb.cmd, explicit_expiry, item->string);
+ status |= reflog_expire(item->string, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
- free(e);
}
- free(collected.e);
+ string_list_clear(&collected.reflogs, 0);
}
for (; i < argc; i++) {
char *ref;
+ struct expire_reflog_policy_cb cb = { .cmd = cmd };
+
if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
@@ -650,9 +678,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
+ free(ref);
}
return status;
}
@@ -661,19 +690,19 @@ static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
- struct expire_reflog_policy_cb *cb = cb_data;
- if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
- cb->cmd.recno++;
+ struct cmd_reflog_expire_cb *cb = cb_data;
+ if (!cb->expire_total || timestamp < cb->expire_total)
+ cb->recno++;
return 0;
}
static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
{
- struct expire_reflog_policy_cb cb;
+ struct cmd_reflog_expire_cb cmd = { 0 };
int i, status = 0;
unsigned int flags = 0;
-
- memset(&cb, 0, sizeof(cb));
+ int verbose = 0;
+ reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -684,7 +713,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
else if (!strcmp(arg, "--updateref"))
flags |= EXPIRE_REFLOGS_UPDATE_REF;
else if (!strcmp(arg, "--verbose"))
- flags |= EXPIRE_REFLOGS_VERBOSE;
+ verbose = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -695,6 +724,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
break;
}
+ if (verbose)
+ should_prune_fn = should_expire_reflog_ent_verbose;
+
if (argc - i < 1)
return error(_("no reflog specified to delete"));
@@ -702,6 +734,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
const char *spec = strstr(argv[i], "@{");
char *ep, *ref;
int recno;
+ struct expire_reflog_policy_cb cb = {
+ .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+ };
if (!spec) {
status |= error(_("not a reflog: %s"), argv[i]);
@@ -715,17 +750,18 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
- cb.cmd.recno = -recno;
- for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ cmd.recno = -recno;
+ for_each_reflog_ent(ref, count_reflog_ent, &cmd);
} else {
- cb.cmd.expire_total = approxidate(spec + 2);
- for_each_reflog_ent(ref, count_reflog_ent, &cb);
- cb.cmd.expire_total = 0;
+ cmd.expire_total = approxidate(spec + 2);
+ for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ cmd.expire_total = 0;
}
+ cb.cmd = cmd;
status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
free(ref);
diff --git a/builtin/remote.c b/builtin/remote.c
index 7f88e6ce9d..299c466116 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -318,6 +318,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
* truth value with >= REBASE_TRUE.
*/
info->rebase = rebase_parse_value(value);
+ if (info->rebase == REBASE_INVALID)
+ warning(_("unhandled branch.%s.rebase=%s; assuming "
+ "'true'"), name, value);
break;
case PUSH_REMOTE:
if (info->push_remote_name)
@@ -344,6 +347,14 @@ struct ref_states {
int queried;
};
+#define REF_STATES_INIT { \
+ .new_refs = STRING_LIST_INIT_DUP, \
+ .stale = STRING_LIST_INIT_DUP, \
+ .tracked = STRING_LIST_INIT_DUP, \
+ .heads = STRING_LIST_INIT_DUP, \
+ .push = STRING_LIST_INIT_DUP, \
+}
+
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
{
struct ref *fetch_map = NULL, **tail = &fetch_map;
@@ -355,9 +366,6 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
die(_("Could not get fetch map for refspec %s"),
states->remote->fetch.raw[i]);
- states->new_refs.strdup_strings = 1;
- states->tracked.strdup_strings = 1;
- states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
@@ -406,7 +414,6 @@ static int get_push_ref_states(const struct ref *remote_refs,
match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
- states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
struct string_list_item *item;
struct push_info *info;
@@ -449,7 +456,6 @@ static int get_push_ref_states_noquery(struct ref_states *states)
if (remote->mirror)
return 0;
- states->push.strdup_strings = 1;
if (!remote->push.nr) {
item = string_list_append(&states->push, _("(matching)"));
info = item->util = xcalloc(1, sizeof(struct push_info));
@@ -483,7 +489,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
refspec.force = 0;
refspec.pattern = 1;
refspec.src = refspec.dst = "refs/heads/*";
- states->heads.strdup_strings = 1;
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
@@ -970,26 +975,31 @@ static int get_remote_ref_states(const char *name,
}
struct show_info {
- struct string_list *list;
- struct ref_states *states;
+ struct string_list list;
+ struct ref_states states;
int width, width2;
int any_rebase;
};
+#define SHOW_INFO_INIT { \
+ .list = STRING_LIST_INIT_DUP, \
+ .states = REF_STATES_INIT, \
+}
+
static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
int n = strlen(item->string);
if (n > info->width)
info->width = n;
- string_list_insert(info->list, item->string);
+ string_list_insert(&info->list, item->string);
return 0;
}
static int show_remote_info_item(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
- struct ref_states *states = info->states;
+ struct ref_states *states = &info->states;
const char *name = item->string;
if (states->queried) {
@@ -1016,7 +1026,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
{
struct show_info *show_info = cb_data;
- struct ref_states *states = show_info->states;
+ struct ref_states *states = &show_info->states;
struct branch_info *branch_info = branch_item->util;
struct string_list_item *item;
int n;
@@ -1029,7 +1039,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
if (branch_info->rebase >= REBASE_TRUE)
show_info->any_rebase = 1;
- item = string_list_insert(show_info->list, branch_item->string);
+ item = string_list_insert(&show_info->list, branch_item->string);
item->util = branch_info;
return 0;
@@ -1084,7 +1094,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
show_info->width = n;
if ((n = strlen(push_info->dest)) > show_info->width2)
show_info->width2 = n;
- item = string_list_append(show_info->list, push_item->string);
+ item = string_list_append(&show_info->list, push_item->string);
item->util = push_item->util;
return 0;
}
@@ -1212,9 +1222,7 @@ static int show(int argc, const char **argv)
OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")),
OPT_END()
};
- struct ref_states states;
- struct string_list info_list = STRING_LIST_INIT_NODUP;
- struct show_info info;
+ struct show_info info = SHOW_INFO_INIT;
argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
0);
@@ -1225,26 +1233,22 @@ static int show(int argc, const char **argv)
if (!no_query)
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
- memset(&states, 0, sizeof(states));
- memset(&info, 0, sizeof(info));
- info.states = &states;
- info.list = &info_list;
for (; argc; argc--, argv++) {
int i;
const char **url;
int url_nr;
- get_remote_ref_states(*argv, &states, query_flag);
+ get_remote_ref_states(*argv, &info.states, query_flag);
printf_ln(_("* remote %s"), *argv);
- printf_ln(_(" Fetch URL: %s"), states.remote->url_nr > 0 ?
- states.remote->url[0] : _("(no URL)"));
- if (states.remote->pushurl_nr) {
- url = states.remote->pushurl;
- url_nr = states.remote->pushurl_nr;
+ printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ?
+ info.states.remote->url[0] : _("(no URL)"));
+ if (info.states.remote->pushurl_nr) {
+ url = info.states.remote->pushurl;
+ url_nr = info.states.remote->pushurl_nr;
} else {
- url = states.remote->url;
- url_nr = states.remote->url_nr;
+ url = info.states.remote->url;
+ url_nr = info.states.remote->url_nr;
}
for (i = 0; i < url_nr; i++)
/*
@@ -1257,57 +1261,57 @@ static int show(int argc, const char **argv)
printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
printf_ln(_(" HEAD branch: %s"), _("(not queried)"));
- else if (!states.heads.nr)
+ else if (!info.states.heads.nr)
printf_ln(_(" HEAD branch: %s"), _("(unknown)"));
- else if (states.heads.nr == 1)
- printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string);
+ else if (info.states.heads.nr == 1)
+ printf_ln(_(" HEAD branch: %s"), info.states.heads.items[0].string);
else {
printf(_(" HEAD branch (remote HEAD is ambiguous,"
" may be one of the following):\n"));
- for (i = 0; i < states.heads.nr; i++)
- printf(" %s\n", states.heads.items[i].string);
+ for (i = 0; i < info.states.heads.nr; i++)
+ printf(" %s\n", info.states.heads.items[i].string);
}
/* remote branch info */
info.width = 0;
- for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
- for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
- for_each_string_list(&states.stale, add_remote_to_show_info, &info);
- if (info.list->nr)
+ for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
+ if (info.list.nr)
printf_ln(Q_(" Remote branch:%s",
" Remote branches:%s",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_remote_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_remote_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git pull info */
info.width = 0;
info.any_rebase = 0;
for_each_string_list(&branch_list, add_local_to_show_info, &info);
- if (info.list->nr)
+ if (info.list.nr)
printf_ln(Q_(" Local branch configured for 'git pull':",
" Local branches configured for 'git pull':",
- info.list->nr));
- for_each_string_list(info.list, show_local_info_item, &info);
- string_list_clear(info.list, 0);
+ info.list.nr));
+ for_each_string_list(&info.list, show_local_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git push info */
- if (states.remote->mirror)
+ if (info.states.remote->mirror)
printf_ln(_(" Local refs will be mirrored by 'git push'"));
info.width = info.width2 = 0;
- for_each_string_list(&states.push, add_push_to_show_info, &info);
- QSORT(info.list->items, info.list->nr, cmp_string_with_push);
- if (info.list->nr)
+ for_each_string_list(&info.states.push, add_push_to_show_info, &info);
+ QSORT(info.list.items, info.list.nr, cmp_string_with_push);
+ if (info.list.nr)
printf_ln(Q_(" Local ref configured for 'git push'%s:",
" Local refs configured for 'git push'%s:",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_push_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_push_info_item, &info);
+ string_list_clear(&info.list, 0);
- free_remote_ref_states(&states);
+ free_remote_ref_states(&info.states);
}
return result;
@@ -1334,8 +1338,7 @@ static int set_head(int argc, const char **argv)
if (!opt_a && !opt_d && argc == 2) {
head_name = xstrdup(argv[1]);
} else if (opt_a && !opt_d && argc == 1) {
- struct ref_states states;
- memset(&states, 0, sizeof(states));
+ struct ref_states states = REF_STATES_INIT;
get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
if (!states.heads.nr)
result |= error(_("Cannot determine remote HEAD"));
@@ -1374,14 +1377,13 @@ static int set_head(int argc, const char **argv)
static int prune_remote(const char *remote, int dry_run)
{
int result = 0;
- struct ref_states states;
+ struct ref_states states = REF_STATES_INIT;
struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
const char *dangling_msg = dry_run
? _(" %s will become dangling!")
: _(" %s has become dangling!");
- memset(&states, 0, sizeof(states));
get_remote_ref_states(remote, &states, GET_REF_STATES);
if (!states.stale.nr) {
diff --git a/builtin/repack.c b/builtin/repack.c
index cb9f4bfed3..da1e364a75 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -15,6 +15,8 @@
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
+#include "pack-bitmap.h"
+#include "refs.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
@@ -94,12 +96,14 @@ static void remove_pack_on_signal(int signo)
}
/*
- * Adds all packs hex strings to the fname list, which do not
- * have a corresponding .keep file. These packs are not to
- * be kept if we are going to pack everything into one file.
+ * Adds all packs hex strings to either fname_nonkept_list or
+ * fname_kept_list based on whether each pack has a corresponding
+ * .keep file or not. Packs without a .keep file are not to be kept
+ * if we are going to pack everything into one file.
*/
-static void get_non_kept_pack_filenames(struct string_list *fname_list,
- const struct string_list *extra_keep)
+static void collect_pack_filenames(struct string_list *fname_nonkept_list,
+ struct string_list *fname_kept_list,
+ const struct string_list *extra_keep)
{
DIR *dir;
struct dirent *e;
@@ -112,21 +116,20 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
size_t len;
int i;
+ if (!strip_suffix(e->d_name, ".pack", &len))
+ continue;
+
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
break;
- if (extra_keep->nr > 0 && i < extra_keep->nr)
- continue;
-
- if (!strip_suffix(e->d_name, ".pack", &len))
- continue;
fname = xmemdupz(e->d_name, len);
- if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
- string_list_append_nodup(fname_list, fname);
+ if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
+ (file_exists(mkpath("%s/%s.keep", packdir, fname))))
+ string_list_append_nodup(fname_kept_list, fname);
else
- free(fname);
+ string_list_append_nodup(fname_nonkept_list, fname);
}
closedir(dir);
}
@@ -255,9 +258,11 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
for_each_packed_object(write_oid, &cmd,
FOR_EACH_OBJECT_PROMISOR_ONLY);
- if (cmd.in == -1)
+ if (cmd.in == -1) {
/* No packed objects; cmd was never started */
+ child_process_clear(&cmd);
return;
+ }
close(cmd.in);
@@ -422,6 +427,25 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
geometry->split = split;
}
+static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+{
+ if (!geometry) {
+ /*
+ * No geometry means either an all-into-one repack (in which
+ * case there is only one pack left and it is the largest) or an
+ * incremental one.
+ *
+ * If repacking incrementally, then we could check the size of
+ * all packs to determine which should be preferred, but leave
+ * this for later.
+ */
+ return NULL;
+ }
+ if (geometry->split == geometry->pack_nr)
+ return NULL;
+ return geometry->pack[geometry->pack_nr - 1];
+}
+
static void clear_pack_geometry(struct pack_geometry *geometry)
{
if (!geometry)
@@ -433,17 +457,162 @@ static void clear_pack_geometry(struct pack_geometry *geometry)
geometry->split = 0;
}
+struct midx_snapshot_ref_data {
+ struct tempfile *f;
+ struct oidset seen;
+ int preferred;
+};
+
+static int midx_snapshot_ref_one(const char *refname,
+ const struct object_id *oid,
+ int flag, void *_data)
+{
+ struct midx_snapshot_ref_data *data = _data;
+ struct object_id peeled;
+
+ if (!peel_iterated_oid(oid, &peeled))
+ oid = &peeled;
+
+ if (oidset_insert(&data->seen, oid))
+ return 0; /* already seen */
+
+ if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
+ return 0;
+
+ fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
+ oid_to_hex(oid));
+
+ return 0;
+}
+
+static void midx_snapshot_refs(struct tempfile *f)
+{
+ struct midx_snapshot_ref_data data;
+ const struct string_list *preferred = bitmap_preferred_tips(the_repository);
+
+ data.f = f;
+ data.preferred = 0;
+ oidset_init(&data.seen, 0);
+
+ if (!fdopen_tempfile(f, "w"))
+ die(_("could not open tempfile %s for writing"),
+ get_tempfile_path(f));
+
+ if (preferred) {
+ struct string_list_item *item;
+
+ data.preferred = 1;
+ for_each_string_list_item(item, preferred)
+ for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+ data.preferred = 0;
+ }
+
+ for_each_ref(midx_snapshot_ref_one, &data);
+
+ if (close_tempfile_gently(f)) {
+ int save_errno = errno;
+ delete_tempfile(&f);
+ errno = save_errno;
+ die_errno(_("could not close refs snapshot tempfile"));
+ }
+
+ oidset_clear(&data.seen);
+}
+
+static void midx_included_packs(struct string_list *include,
+ struct string_list *existing_nonkept_packs,
+ struct string_list *existing_kept_packs,
+ struct string_list *names,
+ struct pack_geometry *geometry)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, existing_kept_packs)
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ for_each_string_list_item(item, names)
+ string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
+ if (geometry) {
+ struct strbuf buf = STRBUF_INIT;
+ uint32_t i;
+ for (i = geometry->split; i < geometry->pack_nr; i++) {
+ struct packed_git *p = geometry->pack[i];
+
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+ strbuf_addstr(&buf, ".idx");
+
+ string_list_insert(include, strbuf_detach(&buf, NULL));
+ }
+ } else {
+ for_each_string_list_item(item, existing_nonkept_packs) {
+ if (item->util)
+ continue;
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ }
+ }
+}
+
+static int write_midx_included_packs(struct string_list *include,
+ struct pack_geometry *geometry,
+ const char *refs_snapshot,
+ int show_progress, int write_bitmaps)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct string_list_item *item;
+ struct packed_git *largest = get_largest_active_pack(geometry);
+ FILE *in;
+ int ret;
+
+ if (!include->nr)
+ return 0;
+
+ cmd.in = -1;
+ cmd.git_cmd = 1;
+
+ strvec_push(&cmd.args, "multi-pack-index");
+ strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
+
+ if (show_progress)
+ strvec_push(&cmd.args, "--progress");
+ else
+ strvec_push(&cmd.args, "--no-progress");
+
+ if (write_bitmaps)
+ strvec_push(&cmd.args, "--bitmap");
+
+ if (largest)
+ strvec_pushf(&cmd.args, "--preferred-pack=%s",
+ pack_basename(largest));
+
+ if (refs_snapshot)
+ strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
+
+ ret = start_command(&cmd);
+ if (ret)
+ return ret;
+
+ in = xfdopen(cmd.in, "w");
+ for_each_string_list_item(item, include)
+ fprintf(in, "%s\n", item->string);
+ fclose(in);
+
+ return finish_command(&cmd);
+}
+
int cmd_repack(int argc, const char **argv, const char *prefix)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
struct string_list rollback = STRING_LIST_INIT_NODUP;
- struct string_list existing_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL;
struct strbuf line = STRBUF_INIT;
+ struct tempfile *refs_snapshot = NULL;
int i, ext, ret;
FILE *out;
+ int show_progress;
/* variables to be filled by option parsing */
int pack_everything = 0;
@@ -454,6 +623,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0;
struct pack_objects_args po_args = {NULL};
int geometric_factor = 0;
+ int write_midx = 0;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -496,6 +666,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("do not repack this pack")),
OPT_INTEGER('g', "geometric", &geometric_factor,
N_("find a geometric progression with factor <N>")),
+ OPT_BOOL('m', "write-midx", &write_midx,
+ N_("write a multi-pack index of the resulting packs")),
OPT_END()
};
@@ -509,11 +681,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (keep_unreachable &&
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
- die(_("--keep-unreachable and -A are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
if (write_bitmaps < 0) {
- if (!(pack_everything & ALL_INTO_ONE) ||
- !is_bare_repository())
+ if (!write_midx &&
+ (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
write_bitmaps = 0;
} else if (write_bitmaps &&
git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0) &&
@@ -521,14 +693,26 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
write_bitmaps = 0;
}
if (pack_kept_objects < 0)
- pack_kept_objects = write_bitmaps > 0;
+ pack_kept_objects = write_bitmaps > 0 && !write_midx;
- if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
+ if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
+ if (write_midx && write_bitmaps) {
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addf(&path, "%s/%s_XXXXXX", get_object_directory(),
+ "bitmap-ref-tips");
+
+ refs_snapshot = xmks_tempfile(path.buf);
+ midx_snapshot_refs(refs_snapshot);
+
+ strbuf_release(&path);
+ }
+
if (geometric_factor) {
if (pack_everything)
- die(_("--geometric is incompatible with -A, -a"));
+ die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
init_pack_geometry(&geometry);
split_pack_geometry(geometry, geometric_factor);
}
@@ -541,6 +725,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
prepare_pack_objects(&cmd, &po_args);
+ show_progress = !po_args.quiet && isatty(2);
+
strvec_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
strvec_push(&cmd.args, "--honor-pack-keep");
@@ -565,19 +751,22 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
if (has_promisor_remote())
strvec_push(&cmd.args, "--exclude-promisor-objects");
- if (write_bitmaps > 0)
- strvec_push(&cmd.args, "--write-bitmap-index");
- else if (write_bitmaps < 0)
- strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ if (!write_midx) {
+ if (write_bitmaps > 0)
+ strvec_push(&cmd.args, "--write-bitmap-index");
+ else if (write_bitmaps < 0)
+ strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ }
if (use_delta_islands)
strvec_push(&cmd.args, "--delta-islands");
- if (pack_everything & ALL_INTO_ONE) {
- get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
+ collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
+ &keep_pack_list);
+ if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(&po_args, &names);
- if (existing_packs.nr && delete_redundant) {
+ if (existing_nonkept_packs.nr && delete_redundant) {
for_each_string_list_item(item, &names) {
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
packtmp_name, item->string);
@@ -657,7 +846,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
fname_old = mkpathdup("%s-%s%s",
packtmp, item->string, exts[ext].name);
- if (((uintptr_t)item->util) & (1 << ext)) {
+ if (((uintptr_t)item->util) & ((uintptr_t)1 << ext)) {
struct stat statbuffer;
if (!stat(fname_old, &statbuffer)) {
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
@@ -677,20 +866,48 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
/* End of pack replacement. */
- reprepare_packed_git(the_repository);
-
- if (delete_redundant) {
+ if (delete_redundant && pack_everything & ALL_INTO_ONE) {
const int hexsz = the_hash_algo->hexsz;
- int opts = 0;
string_list_sort(&names);
- for_each_string_list_item(item, &existing_packs) {
+ for_each_string_list_item(item, &existing_nonkept_packs) {
char *sha1;
size_t len = strlen(item->string);
if (len < hexsz)
continue;
sha1 = item->string + len - hexsz;
- if (!string_list_has_string(&names, sha1))
- remove_redundant_pack(packdir, item->string);
+ /*
+ * Mark this pack for deletion, which ensures that this
+ * pack won't be included in a MIDX (if `--write-midx`
+ * was given) and that we will actually delete this pack
+ * (if `-d` was given).
+ */
+ item->util = (void*)(intptr_t)!string_list_has_string(&names, sha1);
+ }
+ }
+
+ if (write_midx) {
+ struct string_list include = STRING_LIST_INIT_NODUP;
+ midx_included_packs(&include, &existing_nonkept_packs,
+ &existing_kept_packs, &names, geometry);
+
+ ret = write_midx_included_packs(&include, geometry,
+ refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
+ show_progress, write_bitmaps > 0);
+
+ string_list_clear(&include, 0);
+
+ if (ret)
+ return ret;
+ }
+
+ reprepare_packed_git(the_repository);
+
+ if (delete_redundant) {
+ int opts = 0;
+ for_each_string_list_item(item, &existing_nonkept_packs) {
+ if (!item->util)
+ continue;
+ remove_redundant_pack(packdir, item->string);
}
if (geometry) {
@@ -711,7 +928,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
}
- if (!po_args.quiet && isatty(2))
+ if (show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -730,12 +947,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
unsigned flags = 0;
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0))
flags |= MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX;
- write_midx_file(get_object_directory(), NULL, flags);
+ write_midx_file(get_object_directory(), NULL, NULL, flags);
}
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
- string_list_clear(&existing_packs, 0);
+ string_list_clear(&existing_nonkept_packs, 0);
+ string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);
strbuf_release(&line);
diff --git a/builtin/replace.c b/builtin/replace.c
index 946938d011..6ff1734d58 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -258,11 +258,10 @@ static int import_object(struct object_id *oid, enum object_type type,
return error_errno(_("unable to open %s for reading"), filename);
if (!raw && type == OBJ_TREE) {
- const char *argv[] = { "mktree", NULL };
struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf result = STRBUF_INIT;
- cmd.argv = argv;
+ strvec_push(&cmd.args, "mktree");
cmd.git_cmd = 1;
cmd.in = fd;
cmd.out = -1;
diff --git a/builtin/reset.c b/builtin/reset.c
index 7393595349..b97745ee94 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -25,6 +25,7 @@
#include "cache-tree.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "dir.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -136,21 +137,36 @@ static void update_index_from_diff(struct diff_queue_struct *q,
int intent_to_add = *(int *)data;
for (i = 0; i < q->nr; i++) {
+ int pos;
struct diff_filespec *one = q->queue[i]->one;
- int is_missing = !(one->mode && !is_null_oid(&one->oid));
+ int is_in_reset_tree = one->mode && !is_null_oid(&one->oid);
struct cache_entry *ce;
- if (is_missing && !intent_to_add) {
+ if (!is_in_reset_tree && !intent_to_add) {
remove_file_from_cache(one->path);
continue;
}
ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
0, 0);
+
+ /*
+ * If the file 1) corresponds to an existing index entry with
+ * skip-worktree set, or 2) does not exist in the index but is
+ * outside the sparse checkout definition, add a skip-worktree bit
+ * to the new index entry. Note that a sparse index will be expanded
+ * if this entry is outside the sparse cone - this is necessary
+ * to properly construct the reset sparse directory.
+ */
+ pos = cache_name_pos(one->path, strlen(one->path));
+ if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
+ (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+ ce->ce_flags |= CE_SKIP_WORKTREE;
+
if (!ce)
die(_("make_cache_entry failed for path '%s'"),
one->path);
- if (is_missing) {
+ if (!is_in_reset_tree) {
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
}
@@ -158,6 +174,82 @@ static void update_index_from_diff(struct diff_queue_struct *q,
}
}
+static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
+{
+ unsigned int i, pos;
+ int res = 0;
+ char *skip_worktree_seen = NULL;
+
+ /*
+ * When using a magic pathspec, assume for the sake of simplicity that
+ * the index needs to be expanded to match all matchable files.
+ */
+ if (pathspec->magic)
+ return 1;
+
+ for (i = 0; i < pathspec->nr; i++) {
+ struct pathspec_item item = pathspec->items[i];
+
+ /*
+ * If the pathspec item has a wildcard, the index should be expanded
+ * if the pathspec has the possibility of matching a subset of entries inside
+ * of a sparse directory (but not the entire directory).
+ *
+ * If the pathspec item is a literal path, the index only needs to be expanded
+ * if a) the pathspec isn't in the sparse checkout cone (to make sure we don't
+ * expand for in-cone files) and b) it doesn't match any sparse directories
+ * (since we can reset whole sparse directories without expanding them).
+ */
+ if (item.nowildcard_len < item.len) {
+ /*
+ * Special case: if the pattern is a path inside the cone
+ * followed by only wildcards, the pattern cannot match
+ * partial sparse directories, so we don't expand the index.
+ */
+ if (path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
+ strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len)
+ continue;
+
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+
+ if (!S_ISSPARSEDIR(ce->ce_mode))
+ continue;
+
+ /*
+ * If the pre-wildcard length is longer than the sparse
+ * directory name and the sparse directory is the first
+ * component of the pathspec, need to expand the index.
+ */
+ if (item.nowildcard_len > ce_namelen(ce) &&
+ !strncmp(item.original, ce->name, ce_namelen(ce))) {
+ res = 1;
+ break;
+ }
+
+ /*
+ * If the pre-wildcard length is shorter than the sparse
+ * directory and the pathspec does not match the whole
+ * directory, need to expand the index.
+ */
+ if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
+ wildmatch(item.original, ce->name, 0)) {
+ res = 1;
+ break;
+ }
+ }
+ } else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
+ !matches_skip_worktree(pathspec, i, &skip_worktree_seen))
+ res = 1;
+
+ if (res > 0)
+ break;
+ }
+
+ free(skip_worktree_seen);
+ return res;
+}
+
static int read_from_tree(const struct pathspec *pathspec,
struct object_id *tree_oid,
int intent_to_add)
@@ -170,7 +262,13 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
opt.flags.override_submodule_config = 1;
+ opt.flags.recursive = 1;
opt.repo = the_repository;
+ opt.change = diff_change;
+ opt.add_remove = diff_addremove;
+
+ if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec))
+ ensure_full_index(&the_index);
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -249,9 +347,6 @@ static void parse_args(struct pathspec *pathspec,
}
*rev_ret = rev;
- if (read_cache() < 0)
- die(_("index file corrupt"));
-
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
@@ -328,16 +423,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (pathspec_from_file) {
if (patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
@@ -364,7 +459,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (patch_mode) {
if (reset_type != NONE)
- die(_("--patch is incompatible with --{hard,mixed,soft}"));
+ die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive");
return run_add_interactive(rev, "--patch=reset", &pathspec);
}
@@ -395,7 +490,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
_(reset_type_names[reset_type]));
if (intent_to_add && reset_type != MIXED)
- die(_("-N can only be used with --mixed"));
+ die(_("the option '%s' requires '%s'"), "-N", "--mixed");
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
+ if (read_cache() < 0)
+ die(_("index file corrupt"));
/* Soft reset does not touch the index file nor the working tree
* at all, but requires them in a good order. Other resets reset
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 36cb909eba..777558e9b0 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -538,7 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
const char *arg = argv[i];
if (skip_prefix(arg, "--missing=", &arg)) {
if (revs.exclude_promisor_objects)
- die(_("cannot combine --exclude-promisor-objects and --missing"));
+ die(_("options '%s' and '%s' cannot be used together"), "--exclude-promisor-objects", "--missing");
if (parse_missing_action_value(arg))
break;
}
@@ -676,7 +676,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (revs.count &&
(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
(revs.left_right || revs.cherry_mark))
- die(_("marked counting is incompatible with --objects"));
+ die(_("marked counting and '%s' cannot be used together"), "--objects");
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
diff --git a/builtin/rm.c b/builtin/rm.c
index 3d0967cdc1..84a935a16e 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -272,13 +272,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (pathspec_from_file) {
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (!pathspec.nr)
@@ -399,12 +399,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
if (!index_only) {
int removed = 0, gitmodules_modified = 0;
struct strbuf buf = STRBUF_INIT;
+ int flag = force ? REMOVE_DIR_PURGE_ORIGINAL_CWD : 0;
for (i = 0; i < list.nr; i++) {
const char *path = list.entry[i].name;
if (list.entry[i].is_submodule) {
strbuf_reset(&buf);
strbuf_addstr(&buf, path);
- if (remove_dir_recursively(&buf, 0))
+ if (remove_dir_recursively(&buf, flag))
die(_("could not remove '%s'"), path);
removed = 1;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 8932142312..69c432ef1a 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -87,6 +87,10 @@ static void print_helper_status(struct ref *ref)
break;
case REF_STATUS_EXPECTING_REPORT:
+ res = "error";
+ msg = "expecting report";
+ break;
+
default:
continue;
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3e7ab1ca82..e7f7af5de3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -374,6 +374,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
for (;;) {
switch (parse_options_step(&ctx, options, shortlog_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 082449293b..e12c5e80e3 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -707,8 +707,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
*
* Also --all and --remotes do not make sense either.
*/
- die(_("--reflog is incompatible with --all, --remotes, "
- "--independent or --merge-base"));
+ die(_("options '%s' and '%s' cannot be used together"), "--reflog",
+ "--all/--remotes/--independent/--merge-base");
}
/* If nothing is specified, show all branches by default */
@@ -761,6 +761,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
char *logmsg;
char *nth_desc;
const char *msg;
+ char *end;
timestamp_t timestamp;
int tz;
@@ -770,11 +771,12 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
reflog = i;
break;
}
- msg = strchr(logmsg, '\t');
- if (!msg)
- msg = "(none)";
- else
- msg++;
+
+ end = strchr(logmsg, '\n');
+ if (end)
+ *end = '\0';
+
+ msg = (*logmsg == '\0') ? "(none)" : logmsg;
reflog_msg[i] = xstrfmt("(%s) %s",
show_date(timestamp, tz,
DATE_MODE(RELATIVE)),
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index d0f5c4702b..679c107036 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -56,6 +56,9 @@ static int sparse_checkout_list(int argc, const char **argv)
char *sparse_filename;
int res;
+ if (!core_apply_sparse_checkout)
+ die(_("this worktree is not sparse"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_list_options,
builtin_sparse_checkout_list_usage, 0);
@@ -380,6 +383,41 @@ static int set_config(enum sparse_checkout_mode mode)
return 0;
}
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+ int mode, record_mode;
+
+ /* Determine if we need to record the mode; ensure sparse checkout on */
+ record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+ /* If not specified, use previous definition of cone mode */
+ if (*cone_mode == -1 && core_apply_sparse_checkout)
+ *cone_mode = core_sparse_checkout_cone;
+
+ /* Set cone/non-cone mode appropriately */
+ core_apply_sparse_checkout = 1;
+ if (*cone_mode == 1) {
+ mode = MODE_CONE_PATTERNS;
+ core_sparse_checkout_cone = 1;
+ } else {
+ mode = MODE_ALL_PATTERNS;
+ }
+ if (record_mode && set_config(mode))
+ return 1;
+
+ /* Set sparse-index/non-sparse-index mode if specified */
+ if (*sparse_index >= 0) {
+ if (set_sparse_index_config(the_repository, *sparse_index) < 0)
+ die(_("failed to modify sparse-index config"));
+
+ /* force an index rewrite */
+ repo_read_index(the_repository);
+ the_repository->index->updated_workdir = 1;
+ }
+
+ return 0;
+}
+
static char const * const builtin_sparse_checkout_init_usage[] = {
N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"),
NULL
@@ -396,7 +434,6 @@ static int sparse_checkout_init(int argc, const char **argv)
char *sparse_filename;
int res;
struct object_id oid;
- int mode;
struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
@@ -409,19 +446,14 @@ static int sparse_checkout_init(int argc, const char **argv)
repo_read_index(the_repository);
+ init_opts.cone_mode = -1;
init_opts.sparse_index = -1;
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
- if (init_opts.cone_mode) {
- mode = MODE_CONE_PATTERNS;
- core_sparse_checkout_cone = 1;
- } else
- mode = MODE_ALL_PATTERNS;
-
- if (set_config(mode))
+ if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index))
return 1;
memset(&pl, 0, sizeof(pl));
@@ -429,17 +461,6 @@ static int sparse_checkout_init(int argc, const char **argv)
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
- if (init_opts.sparse_index >= 0) {
- if (set_sparse_index_config(the_repository, init_opts.sparse_index) < 0)
- die(_("failed to modify sparse-index config"));
-
- /* force an index rewrite */
- repo_read_index(the_repository);
- the_repository->index->updated_workdir = 1;
- }
-
- core_apply_sparse_checkout = 1;
-
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
@@ -483,7 +504,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
char *oldpattern = e->pattern;
size_t newlen;
- if (slash == e->pattern)
+ if (!slash || slash == e->pattern)
break;
newlen = slash - e->pattern;
@@ -515,17 +536,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
insert_recursive_pattern(pl, line);
}
-static char const * const builtin_sparse_checkout_set_usage[] = {
- N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
- NULL
-};
-
-static struct sparse_checkout_set_opts {
- int use_stdin;
-} set_opts;
-
static void add_patterns_from_input(struct pattern_list *pl,
- int argc, const char **argv)
+ int argc, const char **argv,
+ int use_stdin)
{
int i;
if (core_sparse_checkout_cone) {
@@ -535,7 +548,7 @@ static void add_patterns_from_input(struct pattern_list *pl,
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf unquoted = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
if (line.buf[0] == '"') {
@@ -559,7 +572,7 @@ static void add_patterns_from_input(struct pattern_list *pl,
}
}
} else {
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf line = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
@@ -580,7 +593,8 @@ enum modify_type {
};
static void add_patterns_cone_mode(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
struct strbuf buffer = STRBUF_INIT;
struct pattern_entry *pe;
@@ -588,7 +602,7 @@ static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -598,6 +612,9 @@ static void add_patterns_cone_mode(int argc, const char **argv,
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
+ if (!existing.use_cone_patterns)
+ die(_("existing sparse-checkout patterns do not use cone mode"));
+
hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern, &buffer) ||
@@ -614,17 +631,19 @@ static void add_patterns_cone_mode(int argc, const char **argv,
}
static void add_patterns_literal(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
}
-static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
+static int modify_pattern_list(int argc, const char **argv, int use_stdin,
+ enum modify_type m)
{
int result;
int changed_config = 0;
@@ -633,13 +652,13 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
switch (m) {
case ADD:
if (core_sparse_checkout_cone)
- add_patterns_cone_mode(argc, argv, pl);
+ add_patterns_cone_mode(argc, argv, pl, use_stdin);
else
- add_patterns_literal(argc, argv, pl);
+ add_patterns_literal(argc, argv, pl, use_stdin);
break;
case REPLACE:
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
break;
}
@@ -659,41 +678,124 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
return result;
}
-static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
- enum modify_type m)
+static char const * const builtin_sparse_checkout_add_usage[] = {
+ N_("git sparse-checkout add (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_add_opts {
+ int use_stdin;
+} add_opts;
+
+static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
{
- static struct option builtin_sparse_checkout_set_options[] = {
- OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+ static struct option builtin_sparse_checkout_add_options[] = {
+ OPT_BOOL(0, "stdin", &add_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("no sparse-checkout to add to"));
+
repo_read_index(the_repository);
argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_add_options,
+ builtin_sparse_checkout_add_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+ N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_set_opts {
+ int cone_mode;
+ int sparse_index;
+ int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+ int default_patterns_nr = 2;
+ const char *default_patterns[] = {"/*", "!/*/", NULL};
+
+ static struct option builtin_sparse_checkout_set_options[] = {
+ OPT_BOOL(0, "cone", &set_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &set_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
+ OPT_BOOL_F(0, "stdin", &set_opts.use_stdin,
+ N_("read patterns from standard in"),
+ PARSE_OPT_NONEG),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+
+ set_opts.cone_mode = -1;
+ set_opts.sparse_index = -1;
+
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
- return modify_pattern_list(argc, argv, m);
+ if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
+ return 1;
+
+ /*
+ * Cone mode automatically specifies the toplevel directory. For
+ * 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) {
+ argv = default_patterns;
+ argc = default_patterns_nr;
+ }
+
+ return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
}
static char const * const builtin_sparse_checkout_reapply_usage[] = {
- N_("git sparse-checkout reapply"),
+ N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"),
NULL
};
+static struct sparse_checkout_reapply_opts {
+ int cone_mode;
+ int sparse_index;
+} reapply_opts;
+
static int sparse_checkout_reapply(int argc, const char **argv)
{
static struct option builtin_sparse_checkout_reapply_options[] = {
+ OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("must be in a sparse-checkout to reapply sparsity patterns"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
repo_read_index(the_repository);
+
+ reapply_opts.cone_mode = -1;
+ reapply_opts.sparse_index = -1;
+
+ if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
+ return 1;
+
return update_working_directory(NULL);
}
@@ -710,6 +812,17 @@ static int sparse_checkout_disable(int argc, const char **argv)
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
+ /*
+ * We do not exit early if !core_apply_sparse_checkout; due to the
+ * ability for users to manually muck things up between
+ * direct editing of .git/info/sparse-checkout
+ * running read-tree -m u HEAD or update-index --skip-worktree
+ * direct toggling of config options
+ * users might end up with an index with SKIP_WORKTREE bit set on
+ * some files and not know how to undo it. So, here we just
+ * forcibly return to a dense checkout regardless of initial state.
+ */
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_disable_options,
builtin_sparse_checkout_disable_usage, 0);
@@ -758,9 +871,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
if (!strcmp(argv[0], "set"))
- return sparse_checkout_set(argc, argv, prefix, REPLACE);
+ return sparse_checkout_set(argc, argv, prefix);
if (!strcmp(argv[0], "add"))
- return sparse_checkout_set(argc, argv, prefix, ADD);
+ return sparse_checkout_add(argc, argv, prefix);
if (!strcmp(argv[0], "reapply"))
return sparse_checkout_reapply(argc, argv);
if (!strcmp(argv[0], "disable"))
diff --git a/builtin/stash.c b/builtin/stash.c
index a0ccc8654d..1ef2017c59 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,11 +27,11 @@ static const char * const git_stash_usage[] = {
N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
N_("git stash branch <branchname> [<stash>]"),
"git stash clear",
- N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
" [--] [<pathspec>...]]"),
- N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -561,18 +561,19 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
if (index)
fprintf_ln(stderr, _("Index was not unstashed."));
- return ret;
+ goto restore_untracked;
}
if (has_index) {
if (reset_tree(&index_tree, 0, 0))
- return -1;
+ ret = -1;
} else {
unstage_changes_unless_new(&c_tree);
}
+restore_untracked:
if (info->has_u && restore_untracked(&info->u_tree))
- return error(_("could not restore untracked files from stash"));
+ ret = error(_("could not restore untracked files from stash"));
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -592,7 +593,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
run_command(&cp);
}
- return 0;
+ return ret;
}
static int apply_stash(int argc, const char **argv, const char *prefix)
@@ -1132,6 +1133,38 @@ done:
return ret;
}
+static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
+ int quiet)
+{
+ int ret = 0;
+ struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+ struct index_state istate = { NULL };
+
+ if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
+ 0, NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_diff_tree.git_cmd = 1;
+ strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
+ oid_to_hex(&info->w_tree), "--", NULL);
+ if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (!out_patch->len) {
+ if (!quiet)
+ fprintf_ln(stderr, _("No staged changes"));
+ ret = 1;
+ }
+
+done:
+ discard_index(&istate);
+ return ret;
+}
+
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
struct strbuf *out_patch, int quiet)
{
@@ -1258,7 +1291,7 @@ done:
}
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
- int include_untracked, int patch_mode,
+ int include_untracked, int patch_mode, int only_staged,
struct stash_info *info, struct strbuf *patch,
int quiet)
{
@@ -1337,6 +1370,16 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
} else if (ret > 0) {
goto done;
}
+ } else if (only_staged) {
+ ret = stash_staged(info, patch, quiet);
+ if (ret < 0) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "staged state"));
+ goto done;
+ } else if (ret > 0) {
+ goto done;
+ }
} else {
if (stash_working_tree(info, ps)) {
if (!quiet)
@@ -1395,7 +1438,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
if (!check_changes_tracked_files(&ps))
return 0;
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+ ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
NULL, 0);
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
@@ -1405,7 +1448,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
}
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
- int keep_index, int patch_mode, int include_untracked)
+ int keep_index, int patch_mode, int include_untracked, int only_staged)
{
int ret = 0;
struct stash_info info;
@@ -1423,6 +1466,17 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
+ /* --patch overrides --staged */
+ if (patch_mode)
+ only_staged = 0;
+
+ if (only_staged && include_untracked) {
+ fprintf_ln(stderr, _("Can't use --staged and --include-untracked"
+ " or --all at the same time"));
+ ret = -1;
+ goto done;
+ }
+
read_cache_preload(NULL);
if (!include_untracked && ps->nr) {
int i;
@@ -1463,7 +1517,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
if (stash_msg)
strbuf_addstr(&stash_msg_buf, stash_msg);
- if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+ if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
&info, &patch, quiet)) {
ret = -1;
goto done;
@@ -1480,13 +1534,15 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
printf_ln(_("Saved working directory and index state %s"),
stash_msg_buf.buf);
- if (!patch_mode) {
+ if (!(patch_mode || only_staged)) {
if (include_untracked && !ps->nr) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ if (startup_info->original_cwd)
+ cp.dir = startup_info->original_cwd;
strvec_pushl(&cp.args, "clean", "--force",
- "--quiet", "-d", NULL);
+ "--quiet", "-d", ":/", NULL);
if (include_untracked == INCLUDE_ALL_FILES)
strvec_push(&cp.args, "-x");
if (run_command(&cp)) {
@@ -1598,6 +1654,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
{
int force_assume = 0;
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1608,6 +1665,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1625,6 +1684,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
if (argc) {
force_assume = !strcmp(argv[0], "-p");
argc = parse_options(argc, argv, prefix, options,
+ push_assumed ? git_stash_usage :
git_stash_push_usage,
PARSE_OPT_KEEP_DASHDASH);
}
@@ -1644,25 +1704,29 @@ static int push_stash(int argc, const char **argv, const char *prefix,
if (pathspec_from_file) {
if (patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
+
+ if (only_staged)
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--staged");
if (ps.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&ps, 0,
PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- include_untracked);
+ include_untracked, only_staged);
}
static int save_stash(int argc, const char **argv, const char *prefix)
{
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1673,6 +1737,8 @@ static int save_stash(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1694,7 +1760,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)
memset(&ps, 0, sizeof(ps));
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
- patch_mode, include_untracked);
+ patch_mode, include_untracked, only_staged);
strbuf_release(&stash_msg_buf);
return ret;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6298cbdd4e..c5d3fc3817 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1313,7 +1313,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
if (files) {
if (cached)
- die(_("--cached and --files are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--cached", "--files");
diff_cmd = DIFF_FILES;
}
@@ -1503,16 +1503,17 @@ static void deinit_submodule(const char *path, const char *prefix,
struct strbuf sb_rm = STRBUF_INIT;
const char *format;
- /*
- * protect submodules containing a .git directory
- * NEEDSWORK: instead of dying, automatically call
- * absorbgitdirs and (possibly) warn.
- */
- if (is_directory(sub_git_dir))
- die(_("Submodule work tree '%s' contains a .git "
- "directory (use 'rm -rf' if you really want "
- "to remove it including all of its history)"),
- displaypath);
+ if (is_directory(sub_git_dir)) {
+ if (!(flags & OPT_QUIET))
+ warning(_("Submodule work tree '%s' contains a .git "
+ "directory. This will be replaced with a "
+ ".git file by using absorbgitdirs."),
+ displaypath);
+
+ absorb_git_dir_into_superproject(path,
+ ABSORB_GITDIR_RECURSE_SUBMODULES);
+
+ }
if (!(flags & OPT_FORCE)) {
struct child_process cp_rm = CHILD_PROCESS_INIT;
@@ -2971,7 +2972,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
die(_("--branch or --default required"));
if (opt_branch && opt_default)
- die(_("--branch and --default are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--branch", "--default");
if (argc != 1 || !(path = argv[0]))
usage_with_options(usage, options);
@@ -2999,7 +3000,7 @@ struct add_data {
};
#define ADD_DATA_INIT { .depth = -1 }
-static void show_fetch_remotes(FILE *output, const char *git_dir_path)
+static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
{
struct child_process cp_remote = CHILD_PROCESS_INIT;
struct strbuf sb_remote_out = STRBUF_INIT;
@@ -3015,7 +3016,7 @@ static void show_fetch_remotes(FILE *output, const char *git_dir_path)
while ((next_line = strchr(line, '\n')) != NULL) {
size_t len = next_line - line;
if (strip_suffix_mem(line, &len, " (fetch)"))
- fprintf(output, " %.*s\n", (int)len, line);
+ strbuf_addf(msg, " %.*s\n", (int)len, line);
line = next_line + 1;
}
}
@@ -3047,19 +3048,27 @@ static int add_submodule(const struct add_data *add_data)
if (is_directory(submod_gitdir_path)) {
if (!add_data->force) {
- fprintf(stderr, _("A git directory for '%s' is found "
- "locally with remote(s):"),
- add_data->sm_name);
- show_fetch_remotes(stderr, submod_gitdir_path);
+ struct strbuf msg = STRBUF_INIT;
+ char *die_msg;
+
+ strbuf_addf(&msg, _("A git directory for '%s' is found "
+ "locally with remote(s):\n"),
+ add_data->sm_name);
+
+ append_fetch_remotes(&msg, submod_gitdir_path);
free(submod_gitdir_path);
- die(_("If you want to reuse this local git "
- "directory instead of cloning again from\n"
- " %s\n"
- "use the '--force' option. If the local git "
- "directory is not the correct repo\n"
- "or if you are unsure what this means, choose "
- "another name with the '--name' option.\n"),
- add_data->realrepo);
+
+ strbuf_addf(&msg, _("If you want to reuse this local git "
+ "directory instead of cloning again from\n"
+ " %s\n"
+ "use the '--force' option. If the local git "
+ "directory is not the correct repo\n"
+ "or you are unsure what this means choose "
+ "another name with the '--name' option."),
+ add_data->realrepo);
+
+ die_msg = strbuf_detach(&msg, NULL);
+ die("%s", die_msg);
} else {
printf(_("Reactivating local git directory for "
"submodule '%s'\n"), add_data->sm_name);
@@ -3220,6 +3229,7 @@ static void die_on_index_match(const char *path, int force)
}
free(ps_matched);
}
+ clear_pathspec(&ps);
}
static void die_on_repo_without_commits(const char *path)
@@ -3231,6 +3241,7 @@ static void die_on_repo_without_commits(const char *path)
if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
die(_("'%s' does not have a commit checked out"), path);
}
+ strbuf_release(&sb);
}
static int module_add(int argc, const char **argv, const char *prefix)
diff --git a/builtin/tag.c b/builtin/tag.c
index 6535ed27ee..134b3f1edf 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -178,7 +178,6 @@ static const char tag_template_nocleanup[] =
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "tag.gpgsign")) {
config_sign_tag = git_config_bool(var, value);
@@ -188,7 +187,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -432,11 +431,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
int annotate = 0, force = 0;
int cmdmode = 0, create_tag_object = 0;
const char *msgfile = NULL, *keyid = NULL;
- struct msg_arg msg = { 0, STRBUF_INIT };
+ struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
int icase = 0;
int edit_flag = 0;
@@ -470,7 +470,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_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
{
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
@@ -482,10 +482,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END()
};
+ int ret = 0;
+ const char *only_in_list = NULL;
setup_ref_filter_porcelain_msg();
- git_config(git_tag_config, sorting_tail);
+ git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
@@ -521,15 +523,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
finalize_colopts(&colopts, -1);
if (cmdmode == 'l' && filter.lines != -1) {
if (explicitly_enable_column(colopts))
- die(_("--column and -n are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--column", "-n");
colopts = 0;
}
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
- int ret;
if (column_active(colopts)) {
struct column_options copts;
memset(&copts, 0, sizeof(copts));
@@ -540,29 +540,36 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
ret = list_tags(&filter, sorting, &format);
if (column_active(colopts))
stop_column_filter();
- return ret;
+ goto cleanup;
}
if (filter.lines != -1)
- die(_("-n option is only allowed in list mode"));
- if (filter.with_commit)
- die(_("--contains option is only allowed in list mode"));
- if (filter.no_commit)
- die(_("--no-contains option is only allowed in list mode"));
- if (filter.points_at.nr)
- die(_("--points-at option is only allowed in list mode"));
- if (filter.reachable_from || filter.unreachable_from)
- die(_("--merged and --no-merged options are only allowed in list mode"));
- if (cmdmode == 'd')
- return delete_tags(argv);
+ only_in_list = "-n";
+ else if (filter.with_commit)
+ only_in_list = "--contains";
+ else if (filter.no_commit)
+ only_in_list = "--no-contains";
+ else if (filter.points_at.nr)
+ only_in_list = "--points-at";
+ else if (filter.reachable_from)
+ only_in_list = "--merged";
+ else if (filter.unreachable_from)
+ only_in_list = "--no-merged";
+ if (only_in_list)
+ die(_("the '%s' option is only allowed in list mode"), only_in_list);
+ if (cmdmode == 'd') {
+ ret = delete_tags(argv);
+ goto cleanup;
+ }
if (cmdmode == 'v') {
if (format.format && verify_ref_format(&format))
usage_with_options(git_tag_usage, options);
- return for_each_tag_name(argv, verify_tag, &format);
+ ret = for_each_tag_name(argv, verify_tag, &format);
+ goto cleanup;
}
if (msg.given || msgfile) {
if (msg.given && msgfile)
- die(_("only one -F or -m option is allowed."));
+ die(_("options '%s' and '%s' cannot be used together"), "-F", "-m");
if (msg.given)
strbuf_addbuf(&buf, &(msg.buf));
else {
@@ -626,10 +633,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
printf(_("Updated tag '%s' (was %s)\n"), tag,
find_unique_abbrev(&prev, DEFAULT_ABBREV));
- UNLEAK(buf);
- UNLEAK(ref);
- UNLEAK(reflog_msg);
- UNLEAK(msg);
- UNLEAK(err);
- return 0;
+cleanup:
+ ref_sorting_release(sorting);
+ strbuf_release(&buf);
+ strbuf_release(&ref);
+ strbuf_release(&reflog_msg);
+ strbuf_release(&msg.buf);
+ strbuf_release(&err);
+ return ret;
}
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 24654b4c9b..98d028dae6 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -77,7 +77,7 @@ static ssize_t process_input(int child_fd, int band)
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
- struct child_process writer = { argv };
+ struct child_process writer = CHILD_PROCESS_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
@@ -89,9 +89,10 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
* multiplexed out to our fd#1. If the child dies, we tell the other
* end over channel #3.
*/
- argv[0] = "upload-archive--writer";
writer.out = writer.err = -1;
writer.git_cmd = 1;
+ strvec_push(&writer.args, "upload-archive--writer");
+ strvec_pushv(&writer.args, argv + 1);
if (start_command(&writer)) {
int err = errno;
packet_write_fmt(1, "NACK unable to spawn subprocess\n");
diff --git a/builtin/var.c b/builtin/var.c
index 6c6f46b4ae..491db27429 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -5,6 +5,7 @@
*/
#include "builtin.h"
#include "config.h"
+#include "refs.h"
static const char var_usage[] = "git var (-l | <variable>)";
@@ -27,6 +28,11 @@ static const char *pager(int flag)
return pgm;
}
+static const char *default_branch(int flag)
+{
+ return git_default_branch_name(1);
+}
+
struct git_var {
const char *name;
const char *(*read)(int);
@@ -36,6 +42,7 @@ static struct git_var git_vars[] = {
{ "GIT_AUTHOR_IDENT", git_author_info },
{ "GIT_EDITOR", editor },
{ "GIT_PAGER", pager },
+ { "GIT_DEFAULT_BRANCH", default_branch },
{ "", NULL },
};
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d22ece93e1..2838254f7f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -72,7 +72,7 @@ static void delete_worktrees_dir_if_empty(void)
static void prune_worktree(const char *id, const char *reason)
{
if (show_only || verbose)
- printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
+ fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
if (!show_only)
delete_git_dir(id);
}
@@ -349,18 +349,18 @@ static int add_worktree(const char *path, const char *refname,
strvec_push(&cp.args, "--quiet");
}
- cp.env = child_env.v;
+ strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
if (opts->checkout) {
- cp.argv = NULL;
- strvec_clear(&cp.args);
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
- cp.env = child_env.v;
+ strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
@@ -385,12 +385,11 @@ done:
const char *hook = find_hook("post-checkout");
if (hook) {
const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
- cp.git_cmd = 0;
+ struct child_process cp = CHILD_PROCESS_INIT;
cp.no_stdin = 1;
cp.stdout_to_stderr = 1;
cp.dir = path;
- cp.env = env;
- cp.argv = NULL;
+ strvec_pushv(&cp.env_array, env);
cp.trace2_hook_name = "post-checkout";
strvec_pushl(&cp.args, absolute_path(hook),
oid_to_hex(null_oid()),
@@ -418,24 +417,24 @@ static void print_preparing_worktree_line(int detach,
if (force_new_branch) {
struct commit *commit = lookup_commit_reference_by_name(new_branch);
if (!commit)
- printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
else
- printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
+ fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
new_branch,
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
} else if (new_branch) {
- printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
} else {
struct strbuf s = STRBUF_INIT;
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
ref_exists(s.buf))
- printf_ln(_("Preparing worktree (checking out '%s')"),
+ fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
else {
struct commit *commit = lookup_commit_reference_by_name(branch);
if (!commit)
die(_("invalid reference: %s"), branch);
- printf_ln(_("Preparing worktree (detached HEAD %s)"),
+ fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
}
strbuf_release(&s);
@@ -504,9 +503,9 @@ static int add(int ac, const char **av, const char *prefix)
opts.checkout = 1;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
- die(_("-b, -B, and --detach are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
if (lock_reason && !keep_locked)
- die(_("--reason requires --lock"));
+ die(_("the option '%s' requires '%s'"), "--reason", "--lock");
if (lock_reason)
opts.keep_locked = lock_reason;
else if (keep_locked)
@@ -700,7 +699,7 @@ static int list(int ac, const char **av, const char *prefix)
if (ac)
usage_with_options(worktree_usage, options);
else if (verbose && porcelain)
- die(_("--verbose and --porcelain are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
else {
struct worktree **worktrees = get_worktrees();
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
@@ -1006,7 +1005,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
{
if (!iserr) {
- printf_ln(_("repair: %s: %s"), msg, path);
+ fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
} else {
int *exit_status = (int *)cb_data;
fprintf_ln(stderr, _("error: %s: %s"), msg, path);