summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c75
-rw-r--r--builtin/bisect--helper.c11
-rw-r--r--builtin/branch.c2
-rw-r--r--builtin/checkout.c33
-rw-r--r--builtin/clone.c27
-rw-r--r--builtin/commit.c6
-rw-r--r--builtin/fetch.c4
-rw-r--r--builtin/fmt-merge-msg.c11
-rw-r--r--builtin/log.c40
-rw-r--r--builtin/merge.c9
-rw-r--r--builtin/name-rev.c147
-rw-r--r--builtin/patch-id.c9
-rw-r--r--builtin/pull.c13
-rw-r--r--builtin/range-diff.c1
-rw-r--r--builtin/read-tree.c2
-rw-r--r--builtin/rebase.c10
-rw-r--r--builtin/remote.c3
-rw-r--r--builtin/sparse-checkout.c516
-rw-r--r--builtin/submodule--helper.c10
19 files changed, 802 insertions, 127 deletions
diff --git a/builtin/add.c b/builtin/add.c
index d4686d5218..4c38aff419 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -29,6 +29,8 @@ static const char * const builtin_add_usage[] = {
static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
+static int pathspec_file_nul;
+static const char *pathspec_from_file;
struct update_callback_data {
int flags;
@@ -189,12 +191,17 @@ int run_add_interactive(const char *revision, const char *patch_mode,
int use_builtin_add_i =
git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
- if (!patch_mode) {
- if (use_builtin_add_i < 0)
- git_config_get_bool("add.interactive.usebuiltin",
- &use_builtin_add_i);
- if (use_builtin_add_i == 1)
+ if (use_builtin_add_i < 0)
+ git_config_get_bool("add.interactive.usebuiltin",
+ &use_builtin_add_i);
+
+ if (use_builtin_add_i == 1) {
+ if (!patch_mode)
return !!run_add_i(the_repository, pathspec);
+ if (strcmp(patch_mode, "--patch"))
+ die("'%s' not yet supported in the built-in add -p",
+ patch_mode);
+ return !!run_add_p(the_repository, pathspec);
}
argv_array_push(&argv, "add--interactive");
@@ -320,6 +327,8 @@ static struct option builtin_add_options[] = {
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
N_("warn when adding an embedded repository")),
+ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
};
@@ -414,11 +423,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
if (patch_interactive)
add_interactive = 1;
- if (add_interactive)
+ if (add_interactive) {
+ if (pathspec_from_file)
+ die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
+ }
- if (edit_interactive)
+ if (edit_interactive) {
+ if (pathspec_from_file)
+ die(_("--pathspec-from-file is incompatible with --edit"));
return(edit_patch(argc, argv, prefix));
+ }
argc--;
argv++;
@@ -430,10 +445,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (addremove && take_worktree_changes)
die(_("-A and -u are mutually incompatible"));
- if (!take_worktree_changes && addremove_explicit < 0 && argc)
- /* Turn "git add pathspec..." to "git add -A pathspec..." */
- addremove = 1;
-
if (!show_only && ignore_missing)
die(_("Option --ignore-missing can only be used together with --dry-run"));
@@ -446,19 +457,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
- (show_only ? ADD_CACHE_PRETEND : 0) |
- (intent_to_add ? ADD_CACHE_INTENT : 0) |
- (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
- (!(addremove || take_worktree_changes)
- ? ADD_CACHE_IGNORE_REMOVAL : 0));
-
- if (require_pathspec && argc == 0) {
- fprintf(stderr, _("Nothing specified, nothing added.\n"));
- fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
- return 0;
- }
-
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
@@ -468,6 +466,35 @@ int cmd_add(int argc, const char **argv, const char *prefix)
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
+ if (pathspec_from_file) {
+ if (pathspec.nr)
+ die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+ 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"));
+ }
+
+ if (require_pathspec && pathspec.nr == 0) {
+ fprintf(stderr, _("Nothing specified, nothing added.\n"));
+ fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
+ return 0;
+ }
+
+ if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
+ /* Turn "git add pathspec..." to "git add -A pathspec..." */
+ addremove = 1;
+
+ flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
+ (show_only ? ADD_CACHE_PRETEND : 0) |
+ (intent_to_add ? ADD_CACHE_INTENT : 0) |
+ (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+ (!(addremove || take_worktree_changes)
+ ? ADD_CACHE_IGNORE_REMOVAL : 0));
+
if (read_cache_preload(&pathspec) < 0)
die(_("index file corrupt"));
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 1fbe156e67..1718df7f09 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -169,11 +169,12 @@ static int bisect_reset(const char *commit)
argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ error(_("could not check out original"
+ " HEAD '%s'. Try 'git bisect"
+ " reset <commit>'."), branch.buf);
strbuf_release(&branch);
argv_array_clear(&argv);
- return error(_("could not check out original"
- " HEAD '%s'. Try 'git bisect"
- " reset <commit>'."), branch.buf);
+ return -1;
}
argv_array_clear(&argv);
}
@@ -281,11 +282,11 @@ static int mark_good(const char *refname, const struct object_id *oid,
return 1;
}
-static const char *need_bad_and_good_revision_warning =
+static const char need_bad_and_good_revision_warning[] =
N_("You need to give me at least one %s and %s revision.\n"
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
-static const char *need_bisect_start_warning =
+static const char need_bisect_start_warning[] =
N_("You need to start by \"git bisect start\".\n"
"You then need to give me at least one %s and %s revision.\n"
"You can use \"git bisect %s\" and \"git bisect %s\" for that.");
diff --git a/builtin/branch.c b/builtin/branch.c
index 2ef214632f..d8297f80ff 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -624,7 +624,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
- OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("Unset the upstream info")),
+ OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
OPT__COLOR(&branch_use_color, N_("use colored output")),
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
FILTER_REFS_REMOTES),
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3634a3dac1..18ef5fb975 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -70,6 +70,8 @@ struct checkout_opts {
int checkout_worktree;
const char *ignore_unmerged_opt;
int ignore_unmerged;
+ int pathspec_file_nul;
+ const char *pathspec_from_file;
const char *new_branch;
const char *new_branch_force;
@@ -522,6 +524,8 @@ static int checkout_paths(const struct checkout_opts *opts,
/* Now we are committed to check them out */
if (opts->checkout_worktree)
errs |= checkout_worktree(opts);
+ else
+ remove_marked_cache_entries(&the_index, 1);
/*
* Allow updating the index when checking out from the index.
@@ -1480,6 +1484,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")),
+ OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
+ OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1618,10 +1624,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
die(_("reference is not a tree: %s"), opts->from_treeish);
}
- if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
- !opts->patch_mode) /* patch mode is special */
- die(_("you must specify path(s) to restore"));
-
if (argc) {
parse_pathspec(&opts->pathspec, 0,
opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1641,10 +1643,33 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (opts->force_detach)
die(_("git checkout: --detach does not take a path argument '%s'"),
argv[0]);
+ }
+
+ if (opts->pathspec_from_file) {
+ if (opts->pathspec.nr)
+ die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+ if (opts->force_detach)
+ die(_("--pathspec-from-file is incompatible with --detach"));
+
+ if (opts->patch_mode)
+ die(_("--pathspec-from-file is incompatible with --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"));
+ }
+
+ if (opts->pathspec.nr) {
if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."));
+ } else {
+ if (opts->accept_pathspec && !opts->empty_pathspec_ok &&
+ !opts->patch_mode) /* patch mode is special */
+ die(_("you must specify path(s) to restore"));
}
if (opts->new_branch) {
diff --git a/builtin/clone.c b/builtin/clone.c
index 6dee265cc9..0fc89ae2b9 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -59,6 +59,7 @@ static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
+static int option_sparse_checkout;
static enum transport_family family;
static struct string_list option_config = STRING_LIST_INIT_NODUP;
static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
@@ -146,6 +147,8 @@ static struct option builtin_clone_options[] = {
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
N_("any cloned submodules will use their remote-tracking branch")),
+ OPT_BOOL(0, "sparse", &option_sparse_checkout,
+ N_("initialize sparse-checkout file to include only files at root")),
OPT_END()
};
@@ -733,6 +736,27 @@ static void update_head(const struct ref *our, const struct ref *remote,
}
}
+static int git_sparse_checkout_init(const char *repo)
+{
+ struct argv_array argv = ARGV_ARRAY_INIT;
+ int result = 0;
+ argv_array_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+
+ /*
+ * We must apply the setting in the current process
+ * for the later checkout to use the sparse-checkout file.
+ */
+ core_apply_sparse_checkout = 1;
+
+ if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ error(_("failed to initialize sparse-checkout"));
+ result = 1;
+ }
+
+ argv_array_clear(&argv);
+ return result;
+}
+
static int checkout(int submodule_progress)
{
struct object_id oid;
@@ -1104,6 +1128,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
+ if (option_sparse_checkout && git_sparse_checkout_init(repo))
+ return 1;
+
remote = remote_get(option_origin);
strbuf_addf(&default_refspec, "+%s*:%s*", src_ref_prefix,
diff --git a/builtin/commit.c b/builtin/commit.c
index 2db2ad0de4..646e84547d 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -347,6 +347,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
if (interactive)
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ if (all)
+ die(_("--pathspec-from-file with -a does not make sense"));
+
if (pathspec.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
@@ -554,7 +557,7 @@ static void export_one(const char *var, const char *s, const char *e, int hack)
struct strbuf buf = STRBUF_INIT;
if (hack)
strbuf_addch(&buf, hack);
- strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+ strbuf_add(&buf, s, e - s);
setenv(var, buf.buf, 1);
strbuf_release(&buf);
}
@@ -961,6 +964,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
*/
if (!committable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(current_head))) {
+ s->hints = advice_status_hints;
s->display_comment_prefix = old_display_comment_prefix;
run_status(stdout, index_file, prefix, 0, s);
if (amend)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f8765b385b..b4c6d921d0 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1866,15 +1866,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
(fetch_write_commit_graph < 0 &&
the_repository->settings.fetch_write_commit_graph)) {
int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
- struct split_commit_graph_opts split_opts;
- memset(&split_opts, 0, sizeof(struct split_commit_graph_opts));
if (progress)
commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
write_commit_graph_reachable(get_object_directory(),
commit_graph_flags,
- &split_opts);
+ NULL);
}
close_object_store(the_repository->objects);
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 736f666f64..05a92c59d8 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -494,6 +494,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
enum object_type type;
unsigned long size, len;
char *buf = read_object_file(oid, &type, &size);
+ struct signature_check sigc = { 0 };
struct strbuf sig = STRBUF_INIT;
if (!buf || type != OBJ_TAG)
@@ -502,10 +503,12 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
if (size == len)
; /* merely annotated */
- else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig, NULL)) {
- if (!sig.len)
- strbuf_addstr(&sig, "gpg verification failed.\n");
- }
+ else if (!check_signature(buf, len, buf + len, size - len,
+ &sigc)) {
+ strbuf_addstr(&sig, sigc.gpg_output);
+ signature_check_clear(&sigc);
+ } else
+ strbuf_addstr(&sig, "gpg verification failed.\n");
if (!tag_number++) {
fmt_tag_signature(&tagbuf, &sig, buf, len);
diff --git a/builtin/log.c b/builtin/log.c
index e192f219d9..83a4a6188e 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -208,7 +208,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
rev->show_notes = 1;
if (rev->show_notes)
- init_display_notes(&rev->notes_opt);
+ load_display_notes(&rev->notes_opt);
if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
rev->diffopt.filter || rev->diffopt.flags.follow_renames)
@@ -795,6 +795,8 @@ static const char *signature_file;
static enum cover_setting config_cover_letter;
static const char *config_output_directory;
static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
+static int show_notes;
+static struct display_notes_opt notes_opt;
static enum cover_from_description parse_cover_from_description(const char *arg)
{
@@ -814,8 +816,6 @@ static enum cover_from_description parse_cover_from_description(const char *arg)
static int git_format_config(const char *var, const char *value, void *cb)
{
- struct rev_info *rev = cb;
-
if (!strcmp(var, "format.headers")) {
if (!value)
die(_("format.headers without value"));
@@ -902,19 +902,13 @@ static int git_format_config(const char *var, const char *value, void *cb)
return 0;
}
if (!strcmp(var, "format.notes")) {
- struct strbuf buf = STRBUF_INIT;
int b = git_parse_maybe_bool(value);
- if (!b)
- return 0;
- rev->show_notes = 1;
- if (b < 0) {
- strbuf_addstr(&buf, value);
- expand_notes_ref(&buf);
- string_list_append(&rev->notes_opt.extra_notes_refs,
- strbuf_detach(&buf, NULL));
- } else {
- rev->notes_opt.use_default_notes = 1;
- }
+ if (b < 0)
+ enable_ref_display_notes(&notes_opt, &show_notes, value);
+ else if (b)
+ enable_default_display_notes(&notes_opt, &show_notes);
+ else
+ disable_display_notes(&notes_opt, &show_notes);
return 0;
}
if (!strcmp(var, "format.coverfromdescription")) {
@@ -1372,7 +1366,7 @@ static int header_callback(const struct option *opt, const char *arg, int unset)
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
} else {
- add_header(arg);
+ add_header(arg);
}
return 0;
}
@@ -1428,7 +1422,7 @@ static struct commit *get_base_commit(const char *base_commit,
base = lookup_commit_reference_by_name(base_commit);
if (!base)
die(_("unknown commit %s"), base_commit);
- } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+ } else if ((base_commit && !strcmp(base_commit, "auto"))) {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) {
@@ -1719,8 +1713,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
init_log_defaults();
+ init_display_notes(&notes_opt);
+ git_config(git_format_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
- git_config(git_format_config, &rev);
+ rev.show_notes = show_notes;
+ memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
@@ -1732,6 +1729,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
+ if (base_auto)
+ base_commit = "auto";
+
if (default_attach) {
rev.mime_boundary = default_attach;
rev.no_inline = 1;
@@ -1836,7 +1836,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.flags.binary = 1;
if (rev.show_notes)
- init_display_notes(&rev.notes_opt);
+ load_display_notes(&rev.notes_opt);
if (!output_directory && !use_stdout)
output_directory = config_output_directory;
@@ -1995,7 +1995,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
memset(&bases, 0, sizeof(bases));
- if (base_commit || base_auto) {
+ if (base_commit) {
struct commit *base = get_base_commit(base_commit, list, nr);
reset_revision_walk();
clear_object_flags(UNINTERESTING);
diff --git a/builtin/merge.c b/builtin/merge.c
index 062e911441..d127d2225f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -62,6 +62,7 @@ static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = -1;
static int option_edit = -1;
static int allow_trivial = 1, have_message, verify_signatures;
+static int check_trust_level = 1;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
static struct strategy **use_strategies;
@@ -631,6 +632,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
} else if (!strcmp(k, "commit.gpgsign")) {
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
+ } else if (!strcmp(k, "gpg.mintrustlevel")) {
+ check_trust_level = 0;
}
status = fmt_merge_msg_config(k, v, cb);
@@ -1397,7 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die(_("Can merge only exactly one commit into empty head"));
if (verify_signatures)
- verify_merge_signature(remoteheads->item, verbosity);
+ verify_merge_signature(remoteheads->item, verbosity,
+ check_trust_level);
remote_head_oid = &remoteheads->item->object.oid;
read_empty(remote_head_oid, 0);
@@ -1420,7 +1424,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (verify_signatures) {
for (p = remoteheads; p; p = p->next) {
- verify_merge_signature(p->item, verbosity);
+ verify_merge_signature(p->item, verbosity,
+ check_trust_level);
}
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index e55a4f04ee..6b9e8c850b 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -6,6 +6,7 @@
#include "tag.h"
#include "refs.h"
#include "parse-options.h"
+#include "prio-queue.h"
#include "sha1-lookup.h"
#include "commit-slab.h"
@@ -79,30 +80,16 @@ static int is_better_name(struct rev_name *name,
return 0;
}
-static void name_rev(struct commit *commit,
- const char *tip_name, timestamp_t taggerdate,
- int generation, int distance, int from_tag,
- int deref)
+static struct rev_name *create_or_update_name(struct commit *commit,
+ const char *tip_name,
+ timestamp_t taggerdate,
+ int generation, int distance,
+ int from_tag)
{
struct rev_name *name = get_commit_rev_name(commit);
- struct commit_list *parents;
- int parent_number = 1;
- char *to_free = NULL;
-
- parse_commit(commit);
-
- if (commit->date < cutoff)
- return;
-
- if (deref) {
- tip_name = to_free = xstrfmt("%s^0", tip_name);
-
- if (generation)
- die("generation: %d, but deref?", generation);
- }
if (name == NULL) {
- name = xmalloc(sizeof(rev_name));
+ name = xmalloc(sizeof(*name));
set_commit_rev_name(commit, name);
goto copy_data;
} else if (is_better_name(name, taggerdate, distance, from_tag)) {
@@ -112,35 +99,97 @@ copy_data:
name->generation = generation;
name->distance = distance;
name->from_tag = from_tag;
- } else {
+
+ return name;
+ } else
+ return NULL;
+}
+
+static void name_rev(struct commit *start_commit,
+ const char *tip_name, timestamp_t taggerdate,
+ int from_tag, int deref)
+{
+ struct prio_queue queue;
+ struct commit *commit;
+ struct commit **parents_to_queue = NULL;
+ size_t parents_to_queue_nr, parents_to_queue_alloc = 0;
+ char *to_free = NULL;
+
+ parse_commit(start_commit);
+ if (start_commit->date < cutoff)
+ return;
+
+ if (deref)
+ tip_name = to_free = xstrfmt("%s^0", tip_name);
+
+ if (!create_or_update_name(start_commit, tip_name, taggerdate, 0, 0,
+ from_tag)) {
free(to_free);
return;
}
- for (parents = commit->parents;
- parents;
- parents = parents->next, parent_number++) {
- if (parent_number > 1) {
- size_t len;
- char *new_name;
-
- strip_suffix(tip_name, "^0", &len);
- if (generation > 0)
- new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
- generation, parent_number);
- else
- new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
- parent_number);
-
- name_rev(parents->item, new_name, taggerdate, 0,
- distance + MERGE_TRAVERSAL_WEIGHT,
- from_tag, 0);
- } else {
- name_rev(parents->item, tip_name, taggerdate,
- generation + 1, distance + 1,
- from_tag, 0);
+ memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
+ prio_queue_put(&queue, start_commit);
+
+ while ((commit = prio_queue_get(&queue))) {
+ struct rev_name *name = get_commit_rev_name(commit);
+ struct commit_list *parents;
+ int parent_number = 1;
+
+ parents_to_queue_nr = 0;
+
+ for (parents = commit->parents;
+ parents;
+ parents = parents->next, parent_number++) {
+ struct commit *parent = parents->item;
+ const char *new_name;
+ int generation, distance;
+
+ parse_commit(parent);
+ if (parent->date < cutoff)
+ continue;
+
+ if (parent_number > 1) {
+ size_t len;
+
+ strip_suffix(name->tip_name, "^0", &len);
+ if (name->generation > 0)
+ new_name = xstrfmt("%.*s~%d^%d",
+ (int)len,
+ name->tip_name,
+ name->generation,
+ parent_number);
+ else
+ new_name = xstrfmt("%.*s^%d", (int)len,
+ name->tip_name,
+ parent_number);
+ generation = 0;
+ distance = name->distance + MERGE_TRAVERSAL_WEIGHT;
+ } else {
+ new_name = name->tip_name;
+ generation = name->generation + 1;
+ distance = name->distance + 1;
+ }
+
+ if (create_or_update_name(parent, new_name, taggerdate,
+ generation, distance,
+ from_tag)) {
+ ALLOC_GROW(parents_to_queue,
+ parents_to_queue_nr + 1,
+ parents_to_queue_alloc);
+ parents_to_queue[parents_to_queue_nr] = parent;
+ parents_to_queue_nr++;
+ }
}
+
+ /* The first parent must come out first from the prio_queue */
+ while (parents_to_queue_nr)
+ prio_queue_put(&queue,
+ parents_to_queue[--parents_to_queue_nr]);
}
+
+ clear_prio_queue(&queue);
+ free(parents_to_queue);
}
static int subpath_matches(const char *path, const char *filter)
@@ -272,10 +321,9 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
int from_tag = starts_with(path, "refs/tags/");
if (taggerdate == TIME_MAX)
- taggerdate = ((struct commit *)o)->date;
+ taggerdate = commit->date;
path = name_ref_abbrev(path, can_abbreviate_output);
- name_rev(commit, xstrdup(path), taggerdate, 0, 0,
- from_tag, deref);
+ name_rev(commit, xstrdup(path), taggerdate, from_tag, deref);
}
return 0;
}
@@ -321,11 +369,10 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
if (!n->generation)
return n->tip_name;
else {
- int len = strlen(n->tip_name);
- if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
- len -= 2;
strbuf_reset(buf);
- strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
+ strbuf_addstr(buf, n->tip_name);
+ strbuf_strip_suffix(buf, "^0");
+ strbuf_addf(buf, "~%d", n->generation);
return buf->buf;
}
}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 3059e525b8..822ffff51f 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -5,13 +5,8 @@
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
- char name[GIT_MAX_HEXSZ + 1];
-
- if (!patchlen)
- return;
-
- memcpy(name, oid_to_hex(id), the_hash_algo->hexsz + 1);
- printf("%s %s\n", oid_to_hex(result), name);
+ if (patchlen)
+ printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
}
static int remove_space(char *line)
diff --git a/builtin/pull.c b/builtin/pull.c
index d25ff13a60..d4e3e77c8e 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -107,6 +107,7 @@ static char *opt_ff;
static char *opt_verify_signatures;
static int opt_autostash = -1;
static int config_autostash;
+static int check_trust_level = 1;
static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
static char *opt_gpg_sign;
@@ -355,6 +356,8 @@ static enum rebase_type config_get_rebase(void)
*/
static int git_pull_config(const char *var, const char *value, void *cb)
{
+ int status;
+
if (!strcmp(var, "rebase.autostash")) {
config_autostash = git_config_bool(var, value);
return 0;
@@ -362,7 +365,14 @@ static int git_pull_config(const char *var, const char *value, void *cb)
recurse_submodules = git_config_bool(var, value) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
return 0;
+ } else if (!strcmp(var, "gpg.mintrustlevel")) {
+ check_trust_level = 0;
}
+
+ status = git_gpg_config(var, value, cb);
+ if (status)
+ return status;
+
return git_default_config(var, value, cb);
}
@@ -587,7 +597,8 @@ static int pull_into_void(const struct object_id *merge_head,
die(_("unable to access commit %s"),
oid_to_hex(merge_head));
- verify_merge_signature(commit, opt_verbosity);
+ verify_merge_signature(commit, opt_verbosity,
+ check_trust_level);
}
/*
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 98acf3533e..d8a4670629 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -84,6 +84,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
res = show_range_diff(range1.buf, range2.buf, creation_factor,
simple_color < 1, &diffopt, &other_arg);
+ argv_array_clear(&other_arg);
strbuf_release(&range1);
strbuf_release(&range2);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index ca5e655d2f..af7424b94c 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -185,7 +185,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (opts.reset || opts.merge || opts.prefix) {
if (read_cache_unmerged() && (opts.prefix || opts.merge))
- die("You need to resolve your current index first");
+ die(_("You need to resolve your current index first"));
stage = opts.merge = 1;
}
resolve_undo_clear();
diff --git a/builtin/rebase.c b/builtin/rebase.c
index e755087b0f..8081741f8a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -117,6 +117,11 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
if (opts->strategy_opts)
parse_strategy_opts(&replay, opts->strategy_opts);
+ if (opts->squash_onto) {
+ oidcpy(&replay.squash_onto, opts->squash_onto);
+ replay.have_squash_onto = 1;
+ }
+
return replay;
}
@@ -685,7 +690,7 @@ static int rebase_write_basic_state(struct rebase_options *opts)
write_file(state_dir_path("gpg_sign_opt", opts), "%s",
opts->gpg_sign_opt);
if (opts->signoff)
- write_file(state_dir_path("strategy", opts), "--signoff");
+ write_file(state_dir_path("signoff", opts), "--signoff");
return 0;
}
@@ -1012,7 +1017,8 @@ static int run_am(struct rebase_options *opts)
argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
"--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
- "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL);
+ "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
+ "--no-base", NULL);
if (opts->git_format_patch_opt.len)
argv_array_split(&format_patch.args,
opts->git_format_patch_opt.buf);
diff --git a/builtin/remote.c b/builtin/remote.c
index 5591cef775..96bbe828fe 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -693,9 +693,8 @@ static int mv(int argc, const char **argv)
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;
int flag = 0;
- struct object_id oid;
- read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag);
+ read_ref_full(item->string, RESOLVE_REF_READING, NULL, &flag);
if (!(flag & REF_ISSYMREF))
continue;
if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
new file mode 100644
index 0000000000..b3bed891cb
--- /dev/null
+++ b/builtin/sparse-checkout.c
@@ -0,0 +1,516 @@
+#include "builtin.h"
+#include "config.h"
+#include "dir.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "repository.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "string-list.h"
+#include "cache.h"
+#include "cache-tree.h"
+#include "lockfile.h"
+#include "resolve-undo.h"
+#include "unpack-trees.h"
+#include "wt-status.h"
+
+static const char *empty_base = "";
+
+static char const * const builtin_sparse_checkout_usage[] = {
+ N_("git sparse-checkout (init|list|set|disable) <options>"),
+ NULL
+};
+
+static char *get_sparse_checkout_filename(void)
+{
+ return git_pathdup("info/sparse-checkout");
+}
+
+static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
+{
+ int i;
+
+ for (i = 0; i < pl->nr; i++) {
+ struct path_pattern *p = pl->patterns[i];
+
+ if (p->flags & PATTERN_FLAG_NEGATIVE)
+ fprintf(fp, "!");
+
+ fprintf(fp, "%s", p->pattern);
+
+ if (p->flags & PATTERN_FLAG_MUSTBEDIR)
+ fprintf(fp, "/");
+
+ fprintf(fp, "\n");
+ }
+}
+
+static int sparse_checkout_list(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ char *sparse_filename;
+ int res;
+
+ memset(&pl, 0, sizeof(pl));
+
+ pl.use_cone_patterns = core_sparse_checkout_cone;
+
+ sparse_filename = get_sparse_checkout_filename();
+ res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
+ free(sparse_filename);
+
+ if (res < 0) {
+ warning(_("this worktree is not sparse (sparse-checkout file may not exist)"));
+ return 0;
+ }
+
+ if (pl.use_cone_patterns) {
+ int i;
+ struct pattern_entry *pe;
+ struct hashmap_iter iter;
+ struct string_list sl = STRING_LIST_INIT_DUP;
+
+ hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) {
+ /* pe->pattern starts with "/", skip it */
+ string_list_insert(&sl, pe->pattern + 1);
+ }
+
+ string_list_sort(&sl);
+
+ for (i = 0; i < sl.nr; i++)
+ printf("%s\n", sl.items[i].string);
+
+ return 0;
+ }
+
+ write_patterns_to_file(stdout, &pl);
+ clear_pattern_list(&pl);
+
+ return 0;
+}
+
+static int update_working_directory(struct pattern_list *pl)
+{
+ int result = 0;
+ struct unpack_trees_options o;
+ struct lock_file lock_file = LOCK_INIT;
+ struct object_id oid;
+ struct tree *tree;
+ struct tree_desc t;
+ struct repository *r = the_repository;
+
+ if (repo_read_index_unmerged(r))
+ die(_("you need to resolve your current index first"));
+
+ if (get_oid("HEAD", &oid))
+ return 0;
+
+ tree = parse_tree_indirect(&oid);
+ parse_tree(tree);
+ init_tree_desc(&t, tree->buffer, tree->size);
+
+ memset(&o, 0, sizeof(o));
+ o.verbose_update = isatty(2);
+ o.merge = 1;
+ o.update = 1;
+ o.fn = oneway_merge;
+ o.head_idx = -1;
+ o.src_index = r->index;
+ o.dst_index = r->index;
+ o.skip_sparse_checkout = 0;
+ o.pl = pl;
+ o.keep_pattern_list = !!pl;
+
+ resolve_undo_clear_index(r->index);
+ setup_work_tree();
+
+ cache_tree_free(&r->index->cache_tree);
+
+ repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
+
+ core_apply_sparse_checkout = 1;
+ result = unpack_trees(1, &t, &o);
+
+ if (!result) {
+ prime_cache_tree(r, r->index, tree);
+ write_locked_index(r->index, &lock_file, COMMIT_LOCK);
+ } else
+ rollback_lock_file(&lock_file);
+
+ return result;
+}
+
+static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
+{
+ int i;
+ struct pattern_entry *pe;
+ struct hashmap_iter iter;
+ struct string_list sl = STRING_LIST_INIT_DUP;
+ struct strbuf parent_pattern = STRBUF_INIT;
+
+ hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) {
+ if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL))
+ continue;
+
+ if (!hashmap_contains_parent(&pl->recursive_hashmap,
+ pe->pattern,
+ &parent_pattern))
+ string_list_insert(&sl, pe->pattern);
+ }
+
+ string_list_sort(&sl);
+ string_list_remove_duplicates(&sl, 0);
+
+ fprintf(fp, "/*\n!/*/\n");
+
+ for (i = 0; i < sl.nr; i++) {
+ char *pattern = sl.items[i].string;
+
+ if (strlen(pattern))
+ fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
+ }
+
+ string_list_clear(&sl, 0);
+
+ hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) {
+ if (!hashmap_contains_parent(&pl->recursive_hashmap,
+ pe->pattern,
+ &parent_pattern))
+ string_list_insert(&sl, pe->pattern);
+ }
+
+ strbuf_release(&parent_pattern);
+
+ string_list_sort(&sl);
+ string_list_remove_duplicates(&sl, 0);
+
+ for (i = 0; i < sl.nr; i++) {
+ char *pattern = sl.items[i].string;
+ fprintf(fp, "%s/\n", pattern);
+ }
+}
+
+static int write_patterns_and_update(struct pattern_list *pl)
+{
+ char *sparse_filename;
+ FILE *fp;
+ int fd;
+ struct lock_file lk = LOCK_INIT;
+ int result;
+
+ sparse_filename = get_sparse_checkout_filename();
+ fd = hold_lock_file_for_update(&lk, sparse_filename,
+ LOCK_DIE_ON_ERROR);
+
+ result = update_working_directory(pl);
+ if (result) {
+ rollback_lock_file(&lk);
+ free(sparse_filename);
+ clear_pattern_list(pl);
+ update_working_directory(NULL);
+ return result;
+ }
+
+ fp = xfdopen(fd, "w");
+
+ if (core_sparse_checkout_cone)
+ write_cone_to_file(fp, pl);
+ else
+ write_patterns_to_file(fp, pl);
+
+ fflush(fp);
+ commit_lock_file(&lk);
+
+ free(sparse_filename);
+ clear_pattern_list(pl);
+
+ return 0;
+}
+
+enum sparse_checkout_mode {
+ MODE_NO_PATTERNS = 0,
+ MODE_ALL_PATTERNS = 1,
+ MODE_CONE_PATTERNS = 2,
+};
+
+static int set_config(enum sparse_checkout_mode mode)
+{
+ const char *config_path;
+
+ if (git_config_set_gently("extensions.worktreeConfig", "true")) {
+ error(_("failed to set extensions.worktreeConfig setting"));
+ return 1;
+ }
+
+ config_path = git_path("config.worktree");
+ git_config_set_in_file_gently(config_path,
+ "core.sparseCheckout",
+ mode ? "true" : NULL);
+
+ git_config_set_in_file_gently(config_path,
+ "core.sparseCheckoutCone",
+ mode == MODE_CONE_PATTERNS ? "true" : NULL);
+
+ return 0;
+}
+
+static char const * const builtin_sparse_checkout_init_usage[] = {
+ N_("git sparse-checkout init [--cone]"),
+ NULL
+};
+
+static struct sparse_checkout_init_opts {
+ int cone_mode;
+} init_opts;
+
+static int sparse_checkout_init(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ char *sparse_filename;
+ int res;
+ struct object_id oid;
+ int mode;
+ struct strbuf pattern = STRBUF_INIT;
+
+ static struct option builtin_sparse_checkout_init_options[] = {
+ OPT_BOOL(0, "cone", &init_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("initialize sparse-checkout"), NULL, 1, 0);
+
+ 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))
+ return 1;
+
+ memset(&pl, 0, sizeof(pl));
+
+ sparse_filename = get_sparse_checkout_filename();
+ res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
+
+ /* If we already have a sparse-checkout file, use it. */
+ if (res >= 0) {
+ free(sparse_filename);
+ core_apply_sparse_checkout = 1;
+ return update_working_directory(NULL);
+ }
+
+ if (get_oid("HEAD", &oid)) {
+ FILE *fp;
+
+ /* assume we are in a fresh repo, but update the sparse-checkout file */
+ fp = xfopen(sparse_filename, "w");
+ if (!fp)
+ die(_("failed to open '%s'"), sparse_filename);
+
+ free(sparse_filename);
+ fprintf(fp, "/*\n!/*/\n");
+ fclose(fp);
+ return 0;
+ }
+
+ strbuf_addstr(&pattern, "/*");
+ add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+ strbuf_addstr(&pattern, "!/*/");
+ add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+
+ return write_patterns_and_update(&pl);
+}
+
+static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
+{
+ struct pattern_entry *e = xmalloc(sizeof(*e));
+ e->patternlen = path->len;
+ e->pattern = strbuf_detach(path, NULL);
+ hashmap_entry_init(&e->ent,
+ ignore_case ?
+ strihash(e->pattern) :
+ strhash(e->pattern));
+
+ hashmap_add(&pl->recursive_hashmap, &e->ent);
+
+ while (e->patternlen) {
+ char *slash = strrchr(e->pattern, '/');
+ char *oldpattern = e->pattern;
+ size_t newlen;
+
+ if (slash == e->pattern)
+ break;
+
+ newlen = slash - e->pattern;
+ e = xmalloc(sizeof(struct pattern_entry));
+ e->patternlen = newlen;
+ e->pattern = xstrndup(oldpattern, newlen);
+ hashmap_entry_init(&e->ent,
+ ignore_case ?
+ strihash(e->pattern) :
+ strhash(e->pattern));
+
+ if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+ hashmap_add(&pl->parent_hashmap, &e->ent);
+ }
+}
+
+static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
+{
+ strbuf_trim(line);
+
+ strbuf_trim_trailing_dir_sep(line);
+
+ if (!line->len)
+ return;
+
+ if (line->buf[0] != '/')
+ strbuf_insert(line, 0, "/", 1);
+
+ insert_recursive_pattern(pl, line);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+ N_("git sparse-checkout set (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_set_opts {
+ int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct pattern_list pl;
+ int result;
+ int changed_config = 0;
+
+ static struct option builtin_sparse_checkout_set_options[] = {
+ OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+ N_("read patterns from standard in")),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("set sparse-checkout patterns"), NULL, 1, 0);
+
+ memset(&pl, 0, sizeof(pl));
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_set_options,
+ builtin_sparse_checkout_set_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ if (core_sparse_checkout_cone) {
+ struct strbuf line = STRBUF_INIT;
+
+ hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
+ hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
+ pl.use_cone_patterns = 1;
+
+ if (set_opts.use_stdin) {
+ while (!strbuf_getline(&line, stdin))
+ strbuf_to_cone_pattern(&line, &pl);
+ } else {
+ for (i = 0; i < argc; i++) {
+ strbuf_setlen(&line, 0);
+ strbuf_addstr(&line, argv[i]);
+ strbuf_to_cone_pattern(&line, &pl);
+ }
+ }
+ } else {
+ if (set_opts.use_stdin) {
+ struct strbuf line = STRBUF_INIT;
+
+ while (!strbuf_getline(&line, stdin)) {
+ size_t len;
+ char *buf = strbuf_detach(&line, &len);
+ add_pattern(buf, empty_base, 0, &pl, 0);
+ }
+ } else {
+ for (i = 0; i < argc; i++)
+ add_pattern(argv[i], empty_base, 0, &pl, 0);
+ }
+ }
+
+ if (!core_apply_sparse_checkout) {
+ set_config(MODE_ALL_PATTERNS);
+ core_apply_sparse_checkout = 1;
+ changed_config = 1;
+ }
+
+ result = write_patterns_and_update(&pl);
+
+ if (result && changed_config)
+ set_config(MODE_NO_PATTERNS);
+
+ clear_pattern_list(&pl);
+ return result;
+}
+
+static int sparse_checkout_disable(int argc, const char **argv)
+{
+ struct pattern_list pl;
+ struct strbuf match_all = STRBUF_INIT;
+
+ repo_read_index(the_repository);
+ require_clean_work_tree(the_repository,
+ N_("disable sparse-checkout"), NULL, 1, 0);
+
+ memset(&pl, 0, sizeof(pl));
+ hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
+ hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
+ pl.use_cone_patterns = 0;
+ core_apply_sparse_checkout = 1;
+
+ strbuf_addstr(&match_all, "/*");
+ add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
+
+ if (update_working_directory(&pl))
+ die(_("error while refreshing working directory"));
+
+ clear_pattern_list(&pl);
+ return set_config(MODE_NO_PATTERNS);
+}
+
+int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
+{
+ static struct option builtin_sparse_checkout_options[] = {
+ OPT_END(),
+ };
+
+ if (argc == 2 && !strcmp(argv[1], "-h"))
+ usage_with_options(builtin_sparse_checkout_usage,
+ builtin_sparse_checkout_options);
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_options,
+ builtin_sparse_checkout_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ git_config(git_default_config, NULL);
+
+ if (argc > 0) {
+ if (!strcmp(argv[0], "list"))
+ return sparse_checkout_list(argc, argv);
+ if (!strcmp(argv[0], "init"))
+ return sparse_checkout_init(argc, argv);
+ if (!strcmp(argv[0], "set"))
+ return sparse_checkout_set(argc, argv, prefix);
+ if (!strcmp(argv[0], "disable"))
+ return sparse_checkout_disable(argc, argv);
+ }
+
+ usage_with_options(builtin_sparse_checkout_usage,
+ builtin_sparse_checkout_options);
+}
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a4f9f8dc72..c72931ecd7 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -20,6 +20,7 @@
#include "diff.h"
#include "object-store.h"
#include "dir.h"
+#include "advice.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@@ -1270,6 +1271,13 @@ struct submodule_alternate_setup {
#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+static const char alternate_error_advice[] = N_(
+"An alternate computed from a superproject's alternate is invalid.\n"
+"To allow Git to clone without an alternate in such a case, set\n"
+"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n"
+"'--reference-if-able' instead of '--reference'."
+);
+
static int add_possible_reference_from_superproject(
struct object_directory *odb, void *sas_cb)
{
@@ -1301,6 +1309,8 @@ static int add_possible_reference_from_superproject(
} else {
switch (sas->error_mode) {
case SUBMODULE_ALTERNATE_ERROR_DIE:
+ if (advice_submodule_alternate_error_strategy_die)
+ advise(_(alternate_error_advice));
die(_("submodule '%s' cannot add alternate: %s"),
sas->submodule_name, err.buf);
case SUBMODULE_ALTERNATE_ERROR_INFO: