summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c57
-rw-r--r--builtin/am.c11
-rw-r--r--builtin/archive.c4
-rw-r--r--builtin/bisect--helper.c162
-rw-r--r--builtin/blame.c30
-rw-r--r--builtin/branch.c4
-rw-r--r--builtin/bugreport.c51
-rw-r--r--builtin/bundle.c14
-rw-r--r--builtin/cat-file.c54
-rw-r--r--builtin/checkout.c26
-rw-r--r--builtin/clone.c167
-rw-r--r--builtin/column.c2
-rw-r--r--builtin/commit-graph.c108
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c47
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/credential-cache.c32
-rw-r--r--builtin/credential-store.c2
-rw-r--r--builtin/diff-index.c6
-rw-r--r--builtin/diff.c4
-rw-r--r--builtin/difftool.c159
-rw-r--r--builtin/fast-export.c3
-rw-r--r--builtin/fetch.c99
-rw-r--r--builtin/for-each-ref.c2
-rw-r--r--builtin/for-each-repo.c14
-rw-r--r--builtin/fsck.c44
-rw-r--r--builtin/gc.c686
-rw-r--r--builtin/grep.c67
-rw-r--r--builtin/hash-object.c6
-rw-r--r--builtin/help.c140
-rw-r--r--builtin/index-pack.c72
-rw-r--r--builtin/log.c5
-rw-r--r--builtin/ls-files.c8
-rw-r--r--builtin/ls-remote.c6
-rw-r--r--builtin/mailsplit.c4
-rw-r--r--builtin/merge.c45
-rw-r--r--builtin/mktag.c3
-rw-r--r--builtin/multi-pack-index.c101
-rw-r--r--builtin/mv.c57
-rw-r--r--builtin/notes.c4
-rw-r--r--builtin/pack-objects.c131
-rw-r--r--builtin/prune.c1
-rw-r--r--builtin/pull.c67
-rw-r--r--builtin/push.c12
-rw-r--r--builtin/read-tree.c26
-rw-r--r--builtin/rebase.c394
-rw-r--r--builtin/receive-pack.c31
-rw-r--r--builtin/reflog.c13
-rw-r--r--builtin/remote.c114
-rw-r--r--builtin/repack.c297
-rw-r--r--builtin/replace.c2
-rw-r--r--builtin/reset.c12
-rw-r--r--builtin/rev-list.c33
-rw-r--r--builtin/rev-parse.c4
-rw-r--r--builtin/revert.c6
-rw-r--r--builtin/rm.c12
-rw-r--r--builtin/send-pack.c9
-rw-r--r--builtin/show-branch.c21
-rw-r--r--builtin/sparse-checkout.c104
-rw-r--r--builtin/stash.c27
-rw-r--r--builtin/submodule--helper.c984
-rw-r--r--builtin/tag.c10
-rw-r--r--builtin/update-index.c4
-rw-r--r--builtin/update-ref.c14
-rw-r--r--builtin/upload-pack.c28
-rw-r--r--builtin/worktree.c22
66 files changed, 3063 insertions, 1627 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 09e684585d..ef6b619c45 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -30,6 +30,7 @@ static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
+static int include_sparse;
static const char *pathspec_from_file;
static int legacy_stash_p; /* support for the scripted `git stash` */
@@ -46,7 +47,9 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
struct cache_entry *ce = active_cache[i];
int err;
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
@@ -94,6 +97,10 @@ static void update_callback(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *path = p->one->path;
+
+ if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
+ continue;
+
switch (fix_unmerged_status(p, data)) {
default:
die(_("unexpected diff status %c"), p->status);
@@ -144,12 +151,12 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
{
int i, retval = 0;
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (ce_stage(ce))
continue; /* do not touch unmerged paths */
@@ -198,7 +205,10 @@ static int refresh(int verbose, const struct pathspec *pathspec)
_("Unstaged changes after refreshing the index:"));
for (i = 0; i < pathspec->nr; i++) {
if (!seen[i]) {
- if (matches_skip_worktree(pathspec, i, &skip_worktree_seen)) {
+ const char *path = pathspec->items[i].original;
+
+ if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
+ !path_in_sparse_checkout(path, &the_index)) {
string_list_append(&only_match_skip_worktree,
pathspec->items[i].original);
} else {
@@ -313,9 +323,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
rev.diffopt.use_color = 0;
rev.diffopt.flags.ignore_dirty_submodules = 1;
- out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if (out < 0)
- die(_("Could not open '%s' for writing."), file);
+ out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
rev.diffopt.file = xfdopen(out, "w");
rev.diffopt.close_file = 1;
if (run_diff_files(&rev, 0))
@@ -378,6 +386,7 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x",
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
@@ -419,6 +428,7 @@ static const char embedded_advice[] = N_(
static void check_embedded_repo(const char *path)
{
struct strbuf name = STRBUF_INIT;
+ static int adviced_on_embedded_repo = 0;
if (!warn_on_embedded_repo)
return;
@@ -430,10 +440,10 @@ static void check_embedded_repo(const char *path)
strbuf_strip_suffix(&name, "/");
warning(_("adding embedded git repository: %s"), name.buf);
- if (advice_add_embedded_repo) {
+ if (!adviced_on_embedded_repo &&
+ advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) {
advise(embedded_advice, name.buf, name.buf);
- /* there may be multiple entries; advise only once */
- advice_add_embedded_repo = 0;
+ adviced_on_embedded_repo = 1;
}
strbuf_release(&name);
@@ -442,12 +452,13 @@ static void check_embedded_repo(const char *path)
static int add_files(struct dir_struct *dir, int flags)
{
int i, exit_status = 0;
+ struct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP;
if (dir->ignored_nr) {
fprintf(stderr, _(ignore_error));
for (i = 0; i < dir->ignored_nr; i++)
fprintf(stderr, "%s\n", dir->ignored[i]->name);
- if (advice_add_ignored_file)
+ if (advice_enabled(ADVICE_ADD_IGNORED_FILE))
advise(_("Use -f if you really want to add them.\n"
"Turn this message off by running\n"
"\"git config advice.addIgnoredFile false\""));
@@ -455,6 +466,12 @@ static int add_files(struct dir_struct *dir, int flags)
}
for (i = 0; i < dir->nr; i++) {
+ if (!include_sparse &&
+ !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+ string_list_append(&matched_sparse_paths,
+ dir->entries[i]->name);
+ continue;
+ }
if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
@@ -463,6 +480,14 @@ static int add_files(struct dir_struct *dir, int flags)
check_embedded_repo(dir->entries[i]->name);
}
}
+
+ if (matched_sparse_paths.nr) {
+ advise_on_updating_sparse_paths(&matched_sparse_paths);
+ exit_status = 1;
+ }
+
+ string_list_clear(&matched_sparse_paths, 0);
+
return exit_status;
}
@@ -528,6 +553,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
/*
@@ -553,7 +581,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (require_pathspec && pathspec.nr == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
- if (advice_add_empty_pathspec)
+ if (advice_enabled(ADVICE_ADD_EMPTY_PATHSPEC))
advise( _("Maybe you wanted to say 'git add .'?\n"
"Turn this message off by running\n"
"\"git config advice.addEmptyPathspec false\""));
@@ -624,7 +652,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (seen[i])
continue;
- if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
+ if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
string_list_append(&only_match_skip_worktree,
pathspec.items[i].original);
continue;
diff --git a/builtin/am.c b/builtin/am.c
index 0b2d886c81..8677ea2348 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -11,6 +11,7 @@
#include "parse-options.h"
#include "dir.h"
#include "run-command.h"
+#include "hook.h"
#include "quote.h"
#include "tempfile.h"
#include "lockfile.h"
@@ -210,6 +211,7 @@ static void write_state_bool(const struct am_state *state,
* If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline
* at the end.
*/
+__attribute__((format (printf, 3, 4)))
static void say(const struct am_state *state, FILE *fp, const char *fmt, ...)
{
va_list ap;
@@ -1819,7 +1821,7 @@ static void am_run(struct am_state *state, int resume)
printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
linelen(state->msg), state->msg);
- if (advice_amworkdir)
+ if (advice_enabled(ADVICE_AM_WORK_DIR))
advise(_("Use 'git am --show-current-patch=diff' to see the failed patch"));
die_user_resolve(state);
@@ -1847,7 +1849,6 @@ next:
*/
if (!state->rebasing) {
am_destroy(state);
- close_object_store(the_repository->objects);
run_auto_maintenance(state->quiet);
}
}
@@ -1917,7 +1918,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
opts.dst_index = &the_index;
opts.update = 1;
opts.merge = 1;
- opts.reset = reset;
+ opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = twoway_merge;
init_tree_desc(&t[0], head->buffer, head->size);
init_tree_desc(&t[1], remote->buffer, remote->size);
@@ -2105,7 +2107,8 @@ static void am_abort(struct am_state *state)
if (!has_orig_head)
oidcpy(&orig_head, the_hash_algo->empty_tree);
- clean_index(&curr_head, &orig_head);
+ if (clean_index(&curr_head, &orig_head))
+ die(_("failed to clean index"));
if (has_orig_head)
update_ref("am --abort", "HEAD", &orig_head,
diff --git a/builtin/archive.c b/builtin/archive.c
index 45d11669aa..7176b041b6 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -12,9 +12,7 @@
static void create_output_file(const char *output_file)
{
- int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if (output_fd < 0)
- die_errno(_("could not create archive file '%s'"), output_file);
+ int output_fd = xopen(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (output_fd != 1) {
if (dup2(output_fd, 1) < 0)
die_errno(_("could not redirect output"));
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 9d9540a0ab..28a2e6a575 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -18,10 +18,10 @@ static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
+static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-reset [<commit>]"),
- N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
@@ -30,6 +30,8 @@ static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
N_("git bisect--helper --bisect-replay <filename>"),
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
+ N_("git bisect--helper --bisect-visualize"),
+ N_("git bisect--helper --bisect-run <cmd>..."),
NULL
};
@@ -117,6 +119,7 @@ static int write_in_file(const char *path, const char *mode, const char *format,
return fclose(fp);
}
+__attribute__((format (printf, 2, 3)))
static int write_to_file(const char *path, const char *format, ...)
{
int res;
@@ -129,6 +132,7 @@ static int write_to_file(const char *path, const char *format, ...)
return res;
}
+__attribute__((format (printf, 2, 3)))
static int append_to_file(const char *path, const char *format, ...)
{
int res;
@@ -141,6 +145,19 @@ static int append_to_file(const char *path, const char *format, ...)
return res;
}
+static int print_file_to_stdout(const char *path)
+{
+ int fd = open(path, O_RDONLY);
+ int ret = 0;
+
+ if (fd < 0)
+ return error_errno(_("cannot open file '%s' for reading"), path);
+ if (copy_fd(fd, 1) < 0)
+ ret = error_errno(_("failed to read '%s'"), path);
+ close(fd);
+ return ret;
+}
+
static int check_term_format(const char *term, const char *orig_term)
{
int res;
@@ -1034,6 +1051,125 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
return res;
}
+static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+{
+ struct strvec args = STRVEC_INIT;
+ int flags = RUN_COMMAND_NO_STDIN, res = 0;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (bisect_next_check(terms, NULL) != 0)
+ return BISECT_FAILED;
+
+ if (!argc) {
+ if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
+ getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
+ strvec_push(&args, "gitk");
+ } else {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ }
+ } else {
+ if (argv[0][0] == '-') {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
+ flags |= RUN_GIT_CMD;
+
+ strvec_pushv(&args, argv);
+ }
+
+ strvec_pushl(&args, "--bisect", "--", NULL);
+
+ strbuf_read_file(&sb, git_path_bisect_names(), 0);
+ sq_dequote_to_strvec(sb.buf, &args);
+ strbuf_release(&sb);
+
+ res = run_command_v_opt(args.v, flags);
+ strvec_clear(&args);
+ return res;
+}
+
+static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+{
+ int res = BISECT_OK;
+ struct strbuf command = STRBUF_INIT;
+ struct strvec args = STRVEC_INIT;
+ struct strvec run_args = STRVEC_INIT;
+ const char *new_state;
+ int temporary_stdout_fd, saved_stdout;
+
+ if (bisect_next_check(terms, NULL))
+ return BISECT_FAILED;
+
+ if (argc)
+ sq_quote_argv(&command, argv);
+ else {
+ error(_("bisect run failed: no command provided."));
+ return BISECT_FAILED;
+ }
+
+ strvec_push(&run_args, command.buf);
+
+ while (1) {
+ strvec_clear(&args);
+
+ printf(_("running %s\n"), command.buf);
+ res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+
+ if (res < 0 || 128 <= res) {
+ error(_("bisect run failed: exit code %d from"
+ " '%s' is < 0 or >= 128"), res, command.buf);
+ strbuf_release(&command);
+ return res;
+ }
+
+ if (res == 125)
+ new_state = "skip";
+ else if (!res)
+ new_state = terms->term_good;
+ else
+ new_state = terms->term_bad;
+
+ temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+
+ if (temporary_stdout_fd < 0)
+ return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
+
+ fflush(stdout);
+ saved_stdout = dup(1);
+ dup2(temporary_stdout_fd, 1);
+
+ res = bisect_state(terms, &new_state, 1);
+
+ fflush(stdout);
+ dup2(saved_stdout, 1);
+ close(saved_stdout);
+ close(temporary_stdout_fd);
+
+ print_file_to_stdout(git_path_bisect_run());
+
+ if (res == BISECT_ONLY_SKIPPED_LEFT)
+ error(_("bisect run cannot continue any more"));
+ else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
+ printf(_("bisect run success"));
+ res = BISECT_OK;
+ } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
+ printf(_("bisect found first bad commit"));
+ res = BISECT_OK;
+ } else if (res) {
+ error(_("bisect run failed: 'git bisect--helper --bisect-state"
+ " %s' exited with error code %d"), args.v[0], res);
+ } else {
+ continue;
+ }
+
+ strbuf_release(&command);
+ strvec_clear(&args);
+ strvec_clear(&run_args);
+ return res;
+ }
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
@@ -1046,7 +1182,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
BISECT_STATE,
BISECT_LOG,
BISECT_REPLAY,
- BISECT_SKIP
+ BISECT_SKIP,
+ BISECT_VISUALIZE,
+ BISECT_RUN,
} cmdmode = 0;
int res = 0, nolog = 0;
struct option options[] = {
@@ -1068,6 +1206,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
N_("replay the bisection process from the given file"), BISECT_REPLAY),
OPT_CMDMODE(0, "bisect-skip", &cmdmode,
N_("skip some commits for checkout"), BISECT_SKIP),
+ OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
+ N_("visualize the bisection"), BISECT_VISUALIZE),
+ OPT_CMDMODE(0, "bisect-run", &cmdmode,
+ N_("use <cmd>... to automatically bisect."), BISECT_RUN),
OPT_BOOL(0, "no-log", &nolog,
N_("no log for BISECT_WRITE")),
OPT_END()
@@ -1087,12 +1229,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
return error(_("--bisect-reset requires either no argument or a commit"));
res = bisect_reset(argc ? argv[0] : NULL);
break;
- case BISECT_NEXT_CHECK:
- if (argc != 2 && argc != 3)
- return error(_("--bisect-next-check requires 2 or 3 arguments"));
- set_terms(&terms, argv[1], argv[0]);
- res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
- break;
case BISECT_TERMS:
if (argc > 1)
return error(_("--bisect-terms requires 0 or 1 argument"));
@@ -1129,6 +1265,16 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
get_terms(&terms);
res = bisect_skip(&terms, argv, argc);
break;
+ case BISECT_VISUALIZE:
+ get_terms(&terms);
+ res = bisect_visualize(&terms, argv, argc);
+ break;
+ case BISECT_RUN:
+ if (!argc)
+ return error(_("bisect run failed: no command provided."));
+ get_terms(&terms);
+ res = bisect_run(&terms, argv, argc);
+ break;
default:
BUG("unknown subcommand %d", cmdmode);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 641523ff9a..1c31a99640 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)
diff --git a/builtin/branch.c b/builtin/branch.c
index b23b1d1752..0b7ed82654 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -168,7 +168,7 @@ static int check_branch_commit(const char *branchname, const char *refname,
int kinds, int force)
{
struct commit *rev = lookup_commit_reference(the_repository, oid);
- if (!rev) {
+ if (!force && !rev) {
error(_("Couldn't look up commit object for '%s'"), refname);
return -1;
}
@@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
memset(&array, 0, sizeof(array));
- filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, filter, filter->kind);
if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 9915a5841d..9de32bc96e 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -3,7 +3,8 @@
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
-#include "run-command.h"
+#include "hook.h"
+#include "hook-list.h"
static void get_system_info(struct strbuf *sys_info)
@@ -41,39 +42,7 @@ static void get_system_info(struct strbuf *sys_info)
static void get_populated_hooks(struct strbuf *hook_info, int nongit)
{
- /*
- * NEEDSWORK: Doesn't look like there is a list of all possible hooks;
- * so below is a transcription of `git help hooks`. Later, this should
- * be replaced with some programmatically generated list (generated from
- * doc or else taken from some library which tells us about all the
- * hooks)
- */
- static const char *hook[] = {
- "applypatch-msg",
- "pre-applypatch",
- "post-applypatch",
- "pre-commit",
- "pre-merge-commit",
- "prepare-commit-msg",
- "commit-msg",
- "post-commit",
- "pre-rebase",
- "post-checkout",
- "post-merge",
- "pre-push",
- "pre-receive",
- "update",
- "post-receive",
- "post-update",
- "push-to-checkout",
- "pre-auto-gc",
- "post-rewrite",
- "sendemail-validate",
- "fsmonitor-watchman",
- "p4-pre-submit",
- "post-index-change",
- };
- int i;
+ const char **p;
if (nongit) {
strbuf_addstr(hook_info,
@@ -81,9 +50,12 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
return;
}
- for (i = 0; i < ARRAY_SIZE(hook); i++)
- if (find_hook(hook[i]))
- strbuf_addf(hook_info, "%s\n", hook[i]);
+ for (p = hook_name_list; *p; p++) {
+ const char *hook = *p;
+
+ if (hook_exists(hook))
+ strbuf_addf(hook_info, "%s\n", hook);
+ }
}
static const char * const bugreport_usage[] = {
@@ -171,10 +143,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
get_populated_hooks(&buffer, !startup_info->have_repository);
/* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
- report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
-
- if (report < 0)
- die(_("couldn't create a new file at '%s'"), report_path.buf);
+ report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (write_in_full(report, buffer.buf, buffer.len) < 0)
die_errno(_("unable to write to %s"), report_path.buf);
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 053a51bea1..5a85d7cd0f 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -39,8 +39,6 @@ static const char * const builtin_bundle_unbundle_usage[] = {
NULL
};
-static int verbose;
-
static int parse_options_cmd_bundle(int argc,
const char **argv,
const char* prefix,
@@ -162,10 +160,15 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
struct bundle_header header = BUNDLE_HEADER_INIT;
int bundle_fd = -1;
int ret;
+ int progress = isatty(2);
+
struct option options[] = {
+ OPT_BOOL(0, "progress", &progress,
+ N_("show progress meter")),
OPT_END()
};
char *bundle_file;
+ struct strvec extra_index_pack_args = STRVEC_INIT;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_unbundle_usage, options, &bundle_file);
@@ -177,7 +180,11 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
}
if (!startup_info->have_repository)
die(_("Need a repository to unbundle."));
- ret = !!unbundle(the_repository, &header, bundle_fd, 0) ||
+ if (progress)
+ strvec_pushl(&extra_index_pack_args, "-v", "--progress-title",
+ _("Unbundling objects"), NULL);
+ ret = !!unbundle(the_repository, &header, bundle_fd,
+ &extra_index_pack_args) ||
list_bundle_refs(&header, argc, argv);
bundle_header_release(&header);
cleanup:
@@ -188,7 +195,6 @@ cleanup:
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct option options[] = {
- OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
OPT_END()
};
int result;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 243fe6844b..86fc03242b 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;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f4cd7747d3..cbf73b8c9f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -378,9 +378,6 @@ static int checkout_worktree(const struct checkout_opts *opts,
if (pc_workers > 1)
init_parallel_checkout();
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
-
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
@@ -407,7 +404,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
remove_marked_cache_entries(&the_index, 1);
remove_scheduled_dirs();
- errs |= finish_delayed_checkout(&state, &nr_checkouts);
+ errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress);
if (opts->count_checkout_paths) {
if (nr_unmerged)
@@ -530,8 +527,6 @@ static int checkout_paths(const struct checkout_opts *opts,
* Make sure all pathspecs participated in locating the paths
* to be checked out.
*/
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(&the_index);
for (pos = 0; pos < active_nr; pos++)
if (opts->overlay_mode)
mark_ce_for_checkout_overlay(active_cache[pos],
@@ -651,7 +646,9 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.head_idx = -1;
opts.update = worktree;
opts.skip_unmerged = !worktree;
- opts.reset = 1;
+ opts.reset = o->force ? UNPACK_RESET_OVERWRITE_UNTRACKED :
+ UNPACK_RESET_PROTECT_UNTRACKED;
+ opts.preserve_ignored = (!o->force && !o->overwrite_ignore);
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = o->show_progress;
@@ -751,11 +748,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
new_branch_info->commit ?
&new_branch_info->commit->object.oid :
&new_branch_info->oid, NULL);
- if (opts->overwrite_ignore) {
- topts.dir = xcalloc(1, sizeof(*topts.dir));
- topts.dir->flags |= DIR_SHOW_IGNORED;
- setup_standard_excludes(topts.dir);
- }
+ topts.preserve_ignored = !opts->overwrite_ignore;
tree = parse_tree_indirect(old_branch_info->commit ?
&old_branch_info->commit->object.oid :
the_hash_algo->empty_tree);
@@ -923,7 +916,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
if (!opts->quiet) {
if (old_branch_info->path &&
- advice_detached_head && !opts->force_detach)
+ advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach)
detach_advice(new_branch_info->name);
describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
}
@@ -1016,7 +1009,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
sb.buf);
strbuf_release(&sb);
- if (advice_detached_head)
+ if (advice_enabled(ADVICE_DETACHED_HEAD))
fprintf(stderr,
Q_(
/* The singular version */
@@ -1187,7 +1180,7 @@ static const char *parse_remote_branch(const char *arg,
}
if (!remote && num_matches > 1) {
- if (advice_checkout_ambiguous_remote_branch_name) {
+ if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
@@ -1593,6 +1586,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
git_config(git_checkout_config, opts);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
opts->track = BRANCH_TRACK_UNSPECIFIED;
if (!opts->accept_pathspec && !opts->accept_ref)
diff --git a/builtin/clone.c b/builtin/clone.c
index 66fe66679c..559acf9e03 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
return canon;
}
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
- const char *end = repo + strlen(repo), *start, *ptr;
- size_t len;
- char *dir;
-
- /*
- * Skip scheme.
- */
- start = strstr(repo, "://");
- if (start == NULL)
- start = repo;
- else
- start += 3;
-
- /*
- * Skip authentication data. The stripping does happen
- * greedily, such that we strip up to the last '@' inside
- * the host part.
- */
- for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
- if (*ptr == '@')
- start = ptr + 1;
- }
-
- /*
- * Strip trailing spaces, slashes and /.git
- */
- while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
- end--;
- if (end - start > 5 && is_dir_sep(end[-5]) &&
- !strncmp(end - 4, ".git", 4)) {
- end -= 5;
- while (start < end && is_dir_sep(end[-1]))
- end--;
- }
-
- /*
- * Strip trailing port number if we've got only a
- * hostname (that is, there is no dir separator but a
- * colon). This check is required such that we do not
- * strip URI's like '/foo/bar:2222.git', which should
- * result in a dir '2222' being guessed due to backwards
- * compatibility.
- */
- if (memchr(start, '/', end - start) == NULL
- && memchr(start, ':', end - start) != NULL) {
- ptr = end;
- while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
- ptr--;
- if (start < ptr && ptr[-1] == ':')
- end = ptr - 1;
- }
-
- /*
- * Find last component. To remain backwards compatible we
- * also regard colons as path separators, such that
- * cloning a repository 'foo:bar.git' would result in a
- * directory 'bar' being guessed.
- */
- ptr = end;
- while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
- ptr--;
- start = ptr;
-
- /*
- * Strip .{bundle,git}.
- */
- len = end - start;
- strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
- if (!len || (len == 1 && *start == '/'))
- die(_("No directory name could be guessed.\n"
- "Please specify a directory on the command line"));
-
- if (is_bare)
- dir = xstrfmt("%.*s.git", (int)len, start);
- else
- dir = xstrndup(start, len);
- /*
- * Replace sequences of 'control' characters and whitespace
- * with one ascii space, remove leading and trailing spaces.
- */
- if (*dir) {
- char *out = dir;
- int prev_space = 1 /* strip leading whitespace */;
- for (end = dir; *end; ++end) {
- char ch = *end;
- if ((unsigned char)ch < '\x20')
- ch = '\x20';
- if (isspace(ch)) {
- if (prev_space)
- continue;
- prev_space = 1;
- } else
- prev_space = 0;
- *out++ = ch;
- }
- *out = '\0';
- if (out > dir && prev_space)
- out[-1] = '\0';
- }
- return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
- char *end = dir + strlen(dir);
-
- while (dir < end - 1 && is_dir_sep(end[-1]))
- end--;
- *end = '\0';
-}
-
static int add_one_reference(struct string_list_item *item, void *cb_data)
{
struct strbuf err = STRBUF_INIT;
@@ -657,7 +543,7 @@ static void write_followtags(const struct ref *refs, const char *msg)
}
}
-static int iterate_ref_map(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_ref_map(void *cb_data)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -668,13 +554,11 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
*/
while (ref && !ref->peer_ref)
ref = ref->next;
- /* Returning -1 notes "end of list" to the caller. */
if (!ref)
- return -1;
+ return NULL;
- oidcpy(oid, &ref->old_oid);
*rm = ref->next;
- return 0;
+ return &ref->old_oid;
}
static void update_remote_refs(const struct ref *refs,
@@ -786,7 +670,7 @@ static int checkout(int submodule_progress)
return 0;
}
if (!strcmp(head, "HEAD")) {
- if (advice_detached_head)
+ if (advice_enabled(ADVICE_DETACHED_HEAD))
detach_advice(oid_to_hex(&oid));
FREE_AND_NULL(head);
} else {
@@ -803,6 +687,7 @@ static int checkout(int submodule_progress)
opts.update = 1;
opts.merge = 1;
opts.clone = 1;
+ opts.preserve_ignored = 0;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
@@ -1041,8 +926,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (argc == 2)
dir = xstrdup(argv[1]);
else
- dir = guess_dir_name(repo_name, is_bundle, option_bare);
- strip_trailing_slashes(dir);
+ dir = git_url_basename(repo_name, is_bundle, option_bare);
+ strip_dir_trailing_slashes(dir);
dest_exists = path_exists(dir);
if (dest_exists && !is_empty_dir(dir))
@@ -1114,6 +999,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_recurse_submodules.nr > 0) {
struct string_list_item *item;
struct strbuf sb = STRBUF_INIT;
+ int val;
/* remove duplicates */
string_list_sort(&option_recurse_submodules);
@@ -1130,6 +1016,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_detach(&sb, NULL));
}
+ if (!git_config_get_bool("submodule.stickyRecursiveClone", &val) &&
+ val)
+ string_list_append(&option_config, "submodule.recurse=true");
+
if (option_required_reference.nr &&
option_optional_reference.nr)
die(_("clone --recursive is not compatible with "
@@ -1340,6 +1230,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
our_head_points_at = remote_head_points_at;
}
else {
+ const char *branch;
+ char *ref;
+
if (option_branch)
die(_("Remote branch %s not found in upstream %s"),
option_branch, remote_name);
@@ -1350,24 +1243,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
- if (!option_bare) {
- const char *branch;
- char *ref;
-
- if (transport_ls_refs_options.unborn_head_target &&
- skip_prefix(transport_ls_refs_options.unborn_head_target,
- "refs/heads/", &branch)) {
- ref = transport_ls_refs_options.unborn_head_target;
- transport_ls_refs_options.unborn_head_target = NULL;
- create_symref("HEAD", ref, reflog_msg.buf);
- } else {
- branch = git_default_branch_name(0);
- ref = xstrfmt("refs/heads/%s", branch);
- }
- install_branch_config(0, branch, remote_name, ref);
- free(ref);
+ if (transport_ls_refs_options.unborn_head_target &&
+ skip_prefix(transport_ls_refs_options.unborn_head_target,
+ "refs/heads/", &branch)) {
+ ref = transport_ls_refs_options.unborn_head_target;
+ transport_ls_refs_options.unborn_head_target = NULL;
+ create_symref("HEAD", ref, reflog_msg.buf);
+ } else {
+ branch = git_default_branch_name(0);
+ ref = xstrfmt("refs/heads/%s", branch);
}
+
+ if (!option_bare)
+ install_branch_config(0, branch, remote_name, ref);
+
+ free(ref);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
diff --git a/builtin/column.c b/builtin/column.c
index 40d4b3bee2..158fdf53d9 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -29,7 +29,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")),
OPT_INTEGER(0, "width", &copts.width, N_("maximum width")),
OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("padding space on left border")),
- OPT_INTEGER(0, "nl", &copts.nl, N_("padding space on right border")),
+ OPT_STRING(0, "nl", &copts.nl, N_("string"), N_("padding space on right border")),
OPT_INTEGER(0, "padding", &copts.padding, N_("padding space between columns")),
OPT_END()
};
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index cd86315221..3c3de3a156 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -9,26 +9,29 @@
#include "progress.h"
#include "tag.h"
-static char const * const builtin_commit_graph_usage[] = {
- N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
- N_("git commit-graph write [--object-dir <objdir>] [--append] "
- "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
- "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] "
- "<split options>"),
+#define BUILTIN_COMMIT_GRAPH_VERIFY_USAGE \
+ N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]")
+
+#define BUILTIN_COMMIT_GRAPH_WRITE_USAGE \
+ N_("git commit-graph write [--object-dir <objdir>] [--append] " \
+ "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " \
+ "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " \
+ "<split options>")
+
+static const char * builtin_commit_graph_verify_usage[] = {
+ BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
NULL
};
-static const char * const builtin_commit_graph_verify_usage[] = {
- N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
+static const char * builtin_commit_graph_write_usage[] = {
+ BUILTIN_COMMIT_GRAPH_WRITE_USAGE,
NULL
};
-static const char * const builtin_commit_graph_write_usage[] = {
- N_("git commit-graph write [--object-dir <objdir>] [--append] "
- "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
- "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] "
- "<split options>"),
- NULL
+static char const * const builtin_commit_graph_usage[] = {
+ BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
+ BUILTIN_COMMIT_GRAPH_WRITE_USAGE,
+ NULL,
};
static struct opts_commit_graph {
@@ -43,26 +46,16 @@ static struct opts_commit_graph {
int enable_changed_paths;
} opts;
-static struct object_directory *find_odb(struct repository *r,
- const char *obj_dir)
-{
- struct object_directory *odb;
- char *obj_dir_real = real_pathdup(obj_dir, 1);
- struct strbuf odb_path_real = STRBUF_INIT;
-
- prepare_alt_odb(r);
- for (odb = r->objects->odb; odb; odb = odb->next) {
- strbuf_realpath(&odb_path_real, odb->path, 1);
- if (!strcmp(obj_dir_real, odb_path_real.buf))
- break;
- }
-
- free(obj_dir_real);
- strbuf_release(&odb_path_real);
+static struct option common_opts[] = {
+ OPT_STRING(0, "object-dir", &opts.obj_dir,
+ N_("dir"),
+ N_("the object directory to store the graph")),
+ OPT_END()
+};
- if (!odb)
- die(_("could not find object directory matching %s"), obj_dir);
- return odb;
+static struct option *add_common_options(struct option *to)
+{
+ return parse_options_concat(common_opts, to);
}
static int graph_verify(int argc, const char **argv)
@@ -76,21 +69,22 @@ static int graph_verify(int argc, const char **argv)
int flags = 0;
static struct option builtin_commit_graph_verify_options[] = {
- OPT_STRING(0, "object-dir", &opts.obj_dir,
- N_("dir"),
- N_("the object directory to store the graph")),
OPT_BOOL(0, "shallow", &opts.shallow,
N_("if the commit-graph is split, only verify the tip file")),
- OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
+ struct option *options = add_common_options(builtin_commit_graph_verify_options);
trace2_cmd_mode("verify");
opts.progress = isatty(2);
argc = parse_options(argc, argv, NULL,
- builtin_commit_graph_verify_options,
+ options,
builtin_commit_graph_verify_usage, 0);
+ if (argc)
+ usage_with_options(builtin_commit_graph_verify_usage, options);
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
@@ -106,6 +100,7 @@ static int graph_verify(int argc, const char **argv)
die_errno(_("Could not open commit-graph '%s'"), graph_name);
FREE_AND_NULL(graph_name);
+ FREE_AND_NULL(options);
if (open_ok)
graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb);
@@ -206,9 +201,6 @@ static int graph_write(int argc, const char **argv)
struct progress *progress = NULL;
static struct option builtin_commit_graph_write_options[] = {
- OPT_STRING(0, "object-dir", &opts.obj_dir,
- N_("dir"),
- N_("the object directory to store the graph")),
OPT_BOOL(0, "reachable", &opts.reachable,
N_("start walk at all refs")),
OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
@@ -219,7 +211,6 @@ static int graph_write(int argc, const char **argv)
N_("include all commits already in the commit-graph file")),
OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths,
N_("enable computation for changed paths")),
- OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
OPT_CALLBACK_F(0, "split", &write_opts.split_flags, NULL,
N_("allow writing an incremental commit-graph file"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
@@ -233,8 +224,11 @@ static int graph_write(int argc, const char **argv)
OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters,
NULL, N_("maximum number of changed-path Bloom filters to compute"),
0, write_option_max_new_filters),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
+ struct option *options = add_common_options(builtin_commit_graph_write_options);
opts.progress = isatty(2);
opts.enable_changed_paths = -1;
@@ -248,8 +242,10 @@ static int graph_write(int argc, const char **argv)
git_config(git_commit_graph_write_config, &opts);
argc = parse_options(argc, argv, NULL,
- builtin_commit_graph_write_options,
+ options,
builtin_commit_graph_write_usage, 0);
+ if (argc)
+ usage_with_options(builtin_commit_graph_write_usage, options);
if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1)
die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
@@ -304,6 +300,7 @@ static int graph_write(int argc, const char **argv)
result = 1;
cleanup:
+ FREE_AND_NULL(options);
string_list_clear(&pack_indexes, 0);
strbuf_release(&buf);
return result;
@@ -311,32 +308,25 @@ cleanup:
int cmd_commit_graph(int argc, const char **argv, const char *prefix)
{
- static struct option builtin_commit_graph_options[] = {
- OPT_STRING(0, "object-dir", &opts.obj_dir,
- N_("dir"),
- N_("the object directory to store the graph")),
- OPT_END(),
- };
-
- if (argc == 2 && !strcmp(argv[1], "-h"))
- usage_with_options(builtin_commit_graph_usage,
- builtin_commit_graph_options);
+ struct option *builtin_commit_graph_options = common_opts;
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix,
builtin_commit_graph_options,
builtin_commit_graph_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ goto usage;
save_commit_buffer = 0;
- if (argc > 0) {
- if (!strcmp(argv[0], "verify"))
- return graph_verify(argc, argv);
- if (!strcmp(argv[0], "write"))
- return graph_write(argc, argv);
- }
+ if (!strcmp(argv[0], "verify"))
+ return graph_verify(argc, argv);
+ else if (argc && !strcmp(argv[0], "write"))
+ return graph_write(argc, argv);
+ error(_("unrecognized subcommand: %s"), argv[0]);
+usage:
usage_with_options(builtin_commit_graph_usage,
builtin_commit_graph_options);
}
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 1031b9a491..63ea322933 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -88,9 +88,7 @@ static int parse_file_arg_callback(const struct option *opt,
if (!strcmp(arg, "-"))
fd = 0;
else {
- fd = open(arg, O_RDONLY);
- if (fd < 0)
- die_errno(_("git commit-tree: failed to open '%s'"), arg);
+ fd = xopen(arg, O_RDONLY);
}
if (strbuf_read(buf, fd, 0) < 0)
die_errno(_("git commit-tree: failed to read '%s'"), arg);
diff --git a/builtin/commit.c b/builtin/commit.c
index 190d215d43..883c16256c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -19,6 +19,7 @@
#include "revision.h"
#include "wt-status.h"
#include "run-command.h"
+#include "hook.h"
#include "refs.h"
#include "log-tree.h"
#include "strbuf.h"
@@ -203,7 +204,7 @@ static void status_init_config(struct wt_status *s, config_fn_t fn)
init_diff_ui_defaults();
git_config(fn, s);
determine_whence(s);
- s->hints = advice_status_hints; /* must come after git_config() */
+ s->hints = advice_enabled(ADVICE_STATUS_HINTS); /* must come after git_config() */
}
static void rollback_index_files(void)
@@ -889,7 +890,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
int ident_shown = 0;
int saved_color_setting;
struct ident_split ci, ai;
-
+ const char *hint_cleanup_all = allow_empty_message ?
+ _("Please enter the commit message for your changes."
+ " Lines starting\nwith '%c' will be ignored.\n") :
+ _("Please enter the commit message for your changes."
+ " Lines starting\nwith '%c' will be ignored, and an empty"
+ " message aborts the commit.\n");
+ const char *hint_cleanup_space = allow_empty_message ?
+ _("Please enter the commit message for your changes."
+ " Lines starting\n"
+ "with '%c' will be kept; you may remove them"
+ " yourself if you want to.\n") :
+ _("Please enter the commit message for your changes."
+ " Lines starting\n"
+ "with '%c' will be kept; you may remove them"
+ " yourself if you want to.\n"
+ "An empty message aborts the commit.\n");
if (whence != FROM_COMMIT) {
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
!merge_contains_scissors)
@@ -911,20 +927,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fprintf(s->fp, "\n");
if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
- status_printf(s, GIT_COLOR_NORMAL,
- _("Please enter the commit message for your changes."
- " Lines starting\nwith '%c' will be ignored, and an empty"
- " message aborts the commit.\n"), comment_line_char);
+ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
if (whence == FROM_COMMIT && !merge_contains_scissors)
wt_status_add_cut_line(s->fp);
} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
- status_printf(s, GIT_COLOR_NORMAL,
- _("Please enter the commit message for your changes."
- " Lines starting\n"
- "with '%c' will be kept; you may remove them"
- " yourself if you want to.\n"
- "An empty message aborts the commit.\n"), comment_line_char);
+ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
/*
* These should never fail because they come from our own
@@ -1026,7 +1034,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->hints = advice_enabled(ADVICE_STATUS_HINTS);
s->display_comment_prefix = old_display_comment_prefix;
run_status(stdout, index_file, prefix, 0, s);
if (amend)
@@ -1044,7 +1052,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- if (!no_verify && find_hook("pre-commit")) {
+ if (!no_verify && hook_exists("pre-commit")) {
/*
* Re-read the index as pre-commit hook could have updated it,
* and write it out as a tree. We must do this before we invoke
@@ -1246,8 +1254,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (logfile || have_option_m || use_message)
use_editor = 0;
- if (0 <= edit_flag)
- use_editor = edit_flag;
/* Sanity check options */
if (amend && !current_head)
@@ -1337,6 +1343,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
}
}
+ if (0 <= edit_flag)
+ use_editor = edit_flag;
+
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
handle_untracked_files_arg(s);
@@ -1510,6 +1519,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_status_usage, builtin_status_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
status_init_config(&s, git_status_config);
argc = parse_options(argc, argv, prefix,
builtin_status_options,
@@ -1679,6 +1691,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
status_init_config(&s, git_commit_config);
s.commit_template = 1;
status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
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-cache.c b/builtin/credential-cache.c
index 76a6ba3722..78c02ad531 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -11,6 +11,32 @@
#define FLAG_SPAWN 0x1
#define FLAG_RELAY 0x2
+#ifdef GIT_WINDOWS_NATIVE
+
+static int connection_closed(int error)
+{
+ return (error == EINVAL);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ENETDOWN);
+}
+
+#else
+
+static int connection_closed(int error)
+{
+ return (error == ECONNRESET);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ECONNREFUSED);
+}
+
+#endif
+
static int send_request(const char *socket, const struct strbuf *out)
{
int got_data = 0;
@@ -28,7 +54,7 @@ static int send_request(const char *socket, const struct strbuf *out)
int r;
r = read_in_full(fd, in, sizeof(in));
- if (r == 0 || (r < 0 && errno == ECONNRESET))
+ if (r == 0 || (r < 0 && connection_closed(errno)))
break;
if (r < 0)
die_errno("read error from cache daemon");
@@ -75,7 +101,7 @@ static void do_cache(const char *socket, const char *action, int timeout,
}
if (send_request(socket, &buf) < 0) {
- if (errno != ENOENT && errno != ECONNREFUSED)
+ if (connection_fatally_broken(errno))
die_errno("unable to connect to cache daemon");
if (flags & FLAG_SPAWN) {
spawn_daemon(socket);
@@ -90,7 +116,7 @@ static char *get_socket_path(void)
{
struct stat sb;
char *old_dir, *socket;
- old_dir = expand_user_path("~/.git-credential-cache", 0);
+ old_dir = interpolate_path("~/.git-credential-cache", 0);
if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode))
socket = xstrfmt("%s/socket", old_dir);
else
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index ae3c1ba75f..62a4f3c265 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -173,7 +173,7 @@ int cmd_credential_store(int argc, const char **argv, const char *prefix)
if (file) {
string_list_append(&fns, file);
} else {
- if ((file = expand_user_path("~/.git-credentials", 0)))
+ if ((file = interpolate_path("~/.git-credentials", 0)))
string_list_append_nodup(&fns, file);
file = xdg_config_home("credentials");
if (file)
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index cf09559e42..5fd23ab5b6 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -29,10 +29,10 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
prefix = precompose_argv_prefix(argc, argv, prefix);
/*
- * We need no diff for merges options, and we need to avoid conflict
- * with our own meaning of "-m".
+ * We need (some of) diff for merges options (e.g., --cc), and we need
+ * to avoid conflict with our own meaning of "-m".
*/
- diff_merges_suppress_options_parsing();
+ diff_merges_suppress_m_parsing();
argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
diff --git a/builtin/diff.c b/builtin/diff.c
index 2d87c37a17..dd8ce688ba 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -26,8 +26,8 @@
static const char builtin_diff_usage[] =
"git diff [<options>] [<commit>] [--] [<path>...]\n"
-" or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
-" or: git diff [<options>] <commit> [--merge-base] [<commit>...] <commit> [--] [<path>...]\n"
+" or: git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]\n"
+" or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
" or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
" or: git diff [<options>] <blob> <blob>]\n"
" or: git diff [<options>] --no-index [--] <path> <path>]\n"
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a803..4931c10845 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -252,16 +252,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
strbuf_release(&buf);
}
-static NORETURN void exit_cleanup(const char *tmpdir, int exit_code)
-{
- struct strbuf buf = STRBUF_INIT;
- strbuf_addstr(&buf, tmpdir);
- remove_dir_recursively(&buf, 0);
- if (exit_code)
- warning(_("failed: %d"), exit_code);
- exit(exit_code);
-}
-
static int ensure_leading_directories(char *path)
{
switch (safe_create_leading_directories(path)) {
@@ -330,19 +320,44 @@ static int checkout_path(unsigned mode, struct object_id *oid,
return ret;
}
+static void write_file_in_directory(struct strbuf *dir, size_t dir_len,
+ const char *path, const char *content)
+{
+ add_path(dir, dir_len, path);
+ ensure_leading_directories(dir->buf);
+ unlink(dir->buf);
+ write_file(dir->buf, "%s", content);
+}
+
+/* Write the file contents for the left and right sides of the difftool
+ * dir-diff representation for submodules and symlinks. Symlinks and submodules
+ * are written as regular text files so that external diff tools can diff them
+ * as text files, resulting in behavior that is analogous to to what "git diff"
+ * displays for symlink and submodule diffs.
+ */
+static void write_standin_files(struct pair_entry *entry,
+ struct strbuf *ldir, size_t ldir_len,
+ struct strbuf *rdir, size_t rdir_len)
+{
+ if (*entry->left)
+ write_file_in_directory(ldir, ldir_len, entry->path, entry->left);
+ if (*entry->right)
+ write_file_in_directory(rdir, rdir_len, entry->path, entry->right);
+}
+
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- char tmpdir[PATH_MAX];
struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
struct strbuf wtdir = STRBUF_INIT;
- char *lbase_dir, *rbase_dir;
+ struct strbuf tmpdir = STRBUF_INIT;
+ char *lbase_dir = NULL, *rbase_dir = NULL;
size_t ldir_len, rdir_len, wtdir_len;
const char *workdir, *tmp;
int ret = 0, i;
- FILE *fp;
+ FILE *fp = NULL;
struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp,
NULL);
struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL);
@@ -351,8 +366,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct pair_entry *entry;
struct index_state wtindex;
struct checkout lstate, rstate;
- int rc, flags = RUN_GIT_CMD, err = 0;
- struct child_process child = CHILD_PROCESS_INIT;
+ int flags = RUN_GIT_CMD, err = 0;
const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
struct hashmap wt_modified, tmp_modified;
int indices_loaded = 0;
@@ -361,11 +375,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Setup temp directories */
tmp = getenv("TMPDIR");
- xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
- if (!mkdtemp(tmpdir))
- return error("could not create '%s'", tmpdir);
- strbuf_addf(&ldir, "%s/left/", tmpdir);
- strbuf_addf(&rdir, "%s/right/", tmpdir);
+ strbuf_add_absolute_path(&tmpdir, tmp ? tmp : "/tmp");
+ strbuf_trim_trailing_dir_sep(&tmpdir);
+ strbuf_addstr(&tmpdir, "/git-difftool.XXXXXX");
+ if (!mkdtemp(tmpdir.buf)) {
+ ret = error("could not create '%s'", tmpdir.buf);
+ goto finish;
+ }
+ strbuf_addf(&ldir, "%s/left/", tmpdir.buf);
+ strbuf_addf(&rdir, "%s/right/", tmpdir.buf);
strbuf_addstr(&wtdir, workdir);
if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
strbuf_addch(&wtdir, '/');
@@ -387,19 +405,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
rdir_len = rdir.len;
wtdir_len = wtdir.len;
- child.no_stdin = 1;
- child.git_cmd = 1;
- child.use_shell = 0;
- child.clean_on_exit = 1;
- child.dir = prefix;
- child.out = -1;
- strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
- NULL);
- for (i = 0; i < argc; i++)
- strvec_push(&child.args, argv[i]);
- if (start_command(&child))
+ child->no_stdin = 1;
+ child->git_cmd = 1;
+ child->use_shell = 0;
+ child->clean_on_exit = 1;
+ child->dir = prefix;
+ child->out = -1;
+ if (start_command(child))
die("could not obtain raw diff");
- fp = xfdopen(child.out, "r");
+ fp = xfdopen(child->out, "r");
/* Build index info for left and right sides of the diff */
i = 0;
@@ -410,9 +424,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
const char *src_path, *dst_path;
if (starts_with(info.buf, "::"))
- die(N_("combined diff formats('-c' and '--cc') are "
+ die(N_("combined diff formats ('-c' and '--cc') are "
"not supported in\n"
- "directory diff mode('-d' and '--dir-diff')."));
+ "directory diff mode ('-d' and '--dir-diff')."));
if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
&status))
@@ -525,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
fclose(fp);
fp = NULL;
- if (finish_command(&child)) {
+ if (finish_command(child)) {
ret = error("error occurred running diff --raw");
goto finish;
}
@@ -540,38 +554,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
*/
hashmap_for_each_entry(&submodules, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
}
/*
- * Symbolic links require special treatment.The standard "git diff"
+ * Symbolic links require special treatment. The standard "git diff"
* shows only the link itself, not the contents of the link target.
* This loop replicates that behavior.
*/
hashmap_for_each_entry(&symlinks2, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
- }
- strbuf_release(&buf);
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
+ }
strbuf_setlen(&ldir, ldir_len);
helper_argv[1] = ldir.buf;
@@ -583,7 +578,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
flags = 0;
} else
setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
- rc = run_command_v_opt(helper_argv, flags);
+ ret = run_command_v_opt(helper_argv, flags);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&wtindex);
@@ -617,7 +612,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
if (!indices_loaded) {
struct lock_file lock = LOCK_INIT;
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s/wtindex", tmpdir);
+ strbuf_addf(&buf, "%s/wtindex", tmpdir.buf);
if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
ret = error("could not write %s", buf.buf);
@@ -647,11 +642,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
if (err) {
- warning(_("temporary files exist in '%s'."), tmpdir);
+ warning(_("temporary files exist in '%s'."), tmpdir.buf);
warning(_("you may want to cleanup or recover these."));
- exit(1);
- } else
- exit_cleanup(tmpdir, rc);
+ ret = 1;
+ } else {
+ remove_dir_recursively(&tmpdir, 0);
+ if (ret)
+ warning(_("failed: %d"), ret);
+ }
finish:
if (fp)
@@ -663,30 +661,29 @@ finish:
strbuf_release(&rdir);
strbuf_release(&wtdir);
strbuf_release(&buf);
+ strbuf_release(&tmpdir);
- return ret;
+ return (ret < 0) ? 1 : ret;
}
static int run_file_diff(int prompt, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- struct strvec args = STRVEC_INIT;
const char *env[] = {
"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
NULL
};
- int i;
if (prompt > 0)
env[2] = "GIT_DIFFTOOL_PROMPT=true";
else if (!prompt)
env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+ child->git_cmd = 1;
+ child->dir = prefix;
+ strvec_pushv(&child->env_array, env);
- strvec_push(&args, "diff");
- for (i = 0; i < argc; i++)
- strvec_push(&args, argv[i]);
- return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
+ return run_command(child);
}
int cmd_difftool(int argc, const char **argv, const char *prefix)
@@ -713,12 +710,13 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
"`--tool`")),
OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
N_("make 'git-difftool' exit when an invoked diff "
- "tool returns a non - zero exit code")),
+ "tool returns a non-zero exit code")),
OPT_STRING('x', "extcmd", &extcmd, N_("command"),
N_("specify a custom command for viewing diffs")),
- OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+ OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
OPT_END()
};
+ struct child_process child = CHILD_PROCESS_INIT;
git_config(difftool_config, NULL);
symlinks = has_symlinks;
@@ -768,7 +766,14 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
* will invoke a separate instance of 'git-difftool--helper' for
* each file that changed.
*/
+ strvec_push(&child.args, "diff");
+ if (no_index)
+ strvec_push(&child.args, "--no-index");
+ if (dir_diff)
+ strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
+ strvec_pushv(&child.args, argv);
+
if (dir_diff)
- return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
- return run_file_diff(prompt, prefix, argc, argv);
+ return run_dir_diff(extcmd, symlinks, prefix, &child);
+ return run_file_diff(prompt, prefix, &child);
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 3c20f164f0..8e2caf7281 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -312,7 +312,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);
@@ -821,6 +821,7 @@ static void handle_tag(const char *name, struct tag *tag)
static struct hashmap tags;
message = anonymize_str(&tags, anonymize_tag,
message, message_size, NULL);
+ message_size = strlen(message);
}
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 25740c13df..f7abbc31ff 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -712,7 +712,7 @@ static void adjust_refcol_width(const struct ref *ref)
int max, rlen, llen, len;
/* uptodate lines are only shown on high verbosity level */
- if (!verbosity && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+ if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
return;
max = term_columns();
@@ -748,6 +748,9 @@ static void prepare_format_display(struct ref *ref_map)
struct ref *rm;
const char *format = "full";
+ if (verbosity < 0)
+ return;
+
git_config_get_string_tmp("fetch.output", &format);
if (!strcasecmp(format, "full"))
compact_format = 0;
@@ -827,7 +830,12 @@ static void format_display(struct strbuf *display, char code,
const char *remote, const char *local,
int summary_width)
{
- int width = (summary_width + strlen(summary) - gettext_width(summary));
+ int width;
+
+ if (verbosity < 0)
+ return;
+
+ width = (summary_width + strlen(summary) - gettext_width(summary));
strbuf_addf(display, "%c %-*s ", code, width, summary);
if (!compact_format)
@@ -846,13 +854,11 @@ static int update_local_ref(struct ref *ref,
int summary_width)
{
struct commit *current = NULL, *updated;
- enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
- type = oid_object_info(the_repository, &ref->new_oid, NULL);
- if (type < 0)
+ if (!repo_has_object_file(the_repository, &ref->new_oid))
die(_("object %s not found"), oid_to_hex(&ref->new_oid));
if (oideq(&ref->old_oid, &ref->new_oid)) {
@@ -964,7 +970,7 @@ static int update_local_ref(struct ref *ref,
}
}
-static int iterate_ref_map(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_ref_map(void *cb_data)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -972,10 +978,9 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
ref = ref->next;
if (!ref)
- return -1; /* end of the list */
+ return NULL;
*rm = ref->next;
- oidcpy(oid, &ref->old_oid);
- return 0;
+ return &ref->old_oid;
}
struct fetch_head {
@@ -1074,7 +1079,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
int connectivity_checked, struct ref *ref_map)
{
struct fetch_head fetch_head;
- struct commit *commit;
int url_len, i, rc = 0;
struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
struct ref_transaction *transaction = NULL;
@@ -1122,6 +1126,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
want_status <= FETCH_HEAD_IGNORE;
want_status++) {
for (rm = ref_map; rm; rm = rm->next) {
+ struct commit *commit = NULL;
struct ref *ref = NULL;
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
@@ -1131,11 +1136,23 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(the_repository,
- &rm->old_oid,
- 1);
- if (!commit)
- rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+ /*
+ * References in "refs/tags/" are often going to point
+ * to annotated tags, which are not part of the
+ * commit-graph. We thus only try to look up refs in
+ * the graph which are not in that namespace to not
+ * regress performance in repositories with many
+ * annotated tags.
+ */
+ if (!starts_with(rm->name, "refs/tags/"))
+ commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
+ if (!commit) {
+ commit = lookup_commit_reference_gently(the_repository,
+ &rm->old_oid,
+ 1);
+ if (!commit)
+ rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+ }
if (rm->fetch_head_status != want_status)
continue;
@@ -1202,13 +1219,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
"FETCH_HEAD", summary_width);
}
if (note.len) {
- if (verbosity >= 0 && !shown_url) {
+ if (!shown_url) {
fprintf(stderr, _("From %.*s\n"),
url_len, url);
shown_url = 1;
}
- if (verbosity >= 0)
- fprintf(stderr, " %s\n", note.buf);
+ fprintf(stderr, " %s\n", note.buf);
}
}
}
@@ -1229,7 +1245,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
" 'git remote prune %s' to remove any old, conflicting "
"branches"), remote_name);
- if (advice_fetch_show_forced_updates) {
+ if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) {
if (!fetch_show_forced_updates) {
warning(_(warn_show_forced_updates));
} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
@@ -1282,37 +1298,35 @@ static int check_exist_and_connected(struct ref *ref_map)
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
{
- int ret = check_exist_and_connected(ref_map);
+ int connectivity_checked = 1;
+ int ret;
+
+ /*
+ * We don't need to perform a fetch in case we can already satisfy all
+ * refs.
+ */
+ ret = check_exist_and_connected(ref_map);
if (ret) {
trace2_region_enter("fetch", "fetch_refs", the_repository);
ret = transport_fetch_refs(transport, ref_map);
trace2_region_leave("fetch", "fetch_refs", the_repository);
+ if (ret)
+ goto out;
+ connectivity_checked = transport->smart_options ?
+ transport->smart_options->connectivity_checked : 0;
}
- if (!ret)
- /*
- * Keep the new pack's ".keep" file around to allow the caller
- * time to update refs to reference the new objects.
- */
- return 0;
- transport_unlock_pack(transport);
- return ret;
-}
-/* Update local refs based on the ref values fetched from a remote */
-static int consume_refs(struct transport *transport, struct ref *ref_map)
-{
- int connectivity_checked = transport->smart_options
- ? transport->smart_options->connectivity_checked : 0;
- int ret;
trace2_region_enter("fetch", "consume_refs", the_repository);
ret = store_updated_refs(transport->url,
transport->remote->name,
connectivity_checked,
ref_map);
- transport_unlock_pack(transport);
trace2_region_leave("fetch", "consume_refs", the_repository);
+
+out:
+ transport_unlock_pack(transport);
return ret;
}
@@ -1428,7 +1442,9 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
if (!has_glob_specials(s)) {
struct object_id oid;
if (get_oid(s, &oid))
- die("%s is not a valid object", s);
+ die(_("%s is not a valid object"), s);
+ if (!has_object(the_repository, &oid, 0))
+ die(_("the object %s does not exist"), s);
oid_array_append(oids, &oid);
continue;
}
@@ -1499,8 +1515,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);
- if (!fetch_refs(transport, ref_map))
- consume_refs(transport, ref_map);
+ fetch_and_consume_refs(transport, ref_map);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1591,7 +1606,7 @@ static int do_fetch(struct transport *transport,
transport->url);
}
}
- if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) {
+ if (fetch_and_consume_refs(transport, ref_map)) {
free_refs(ref_map);
retcode = 1;
goto cleanup;
@@ -2133,8 +2148,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
NULL);
}
- close_object_store(the_repository->objects);
-
if (enable_auto_gc)
run_auto_maintenance(verbosity < 0);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 89cb6307d4..642b4b888f 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -77,7 +77,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
filter.name_patterns = argv;
filter.match_as_path = 1;
- filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, &filter, FILTER_REFS_ALL);
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 52be64a437..fd86e5a861 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -10,18 +10,16 @@ static const char * const for_each_repo_usage[] = {
NULL
};
-static int run_command_on_repo(const char *path,
- void *cbdata)
+static int run_command_on_repo(const char *path, int argc, const char ** argv)
{
int i;
struct child_process child = CHILD_PROCESS_INIT;
- struct strvec *args = (struct strvec *)cbdata;
child.git_cmd = 1;
strvec_pushl(&child.args, "-C", path, NULL);
- for (i = 0; i < args->nr; i++)
- strvec_push(&child.args, args->v[i]);
+ for (i = 0; i < argc; i++)
+ strvec_push(&child.args, argv[i]);
return run_command(&child);
}
@@ -31,7 +29,6 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
static const char *config_key = NULL;
int i, result = 0;
const struct string_list *values;
- struct strvec args = STRVEC_INIT;
const struct option options[] = {
OPT_STRING(0, "config", &config_key, N_("config"),
@@ -45,9 +42,6 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
if (!config_key)
die(_("missing --config=<config>"));
- for (i = 0; i < argc; i++)
- strvec_push(&args, argv[i]);
-
values = repo_config_get_value_multi(the_repository,
config_key);
@@ -59,7 +53,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
return 0;
for (i = 0; !result && i < values->nr; i++)
- result = run_command_on_repo(values->items[i].string, &args);
+ result = run_command_on_repo(values->items[i].string, argc, argv);
return result;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b42b6fe21f..30a516da29 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -593,18 +593,43 @@ 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;
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);
return 0; /* keep checking other objects */
}
@@ -640,8 +665,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 +676,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 +688,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,
diff --git a/builtin/gc.c b/builtin/gc.c
index f05d2f0a1a..6b3de3dd51 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -502,7 +502,7 @@ static int report_last_gc_error(void)
*/
warning(_("The last gc run reported the following. "
"Please correct the root cause\n"
- "and remove %s.\n"
+ "and remove %s\n"
"Automatic cleanup will not be performed "
"until the file is removed.\n\n"
"%s"),
@@ -663,8 +663,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
gc_before_repack();
if (!repository_format_precious_objects) {
- close_object_store(the_repository->objects);
- if (run_command_v_opt(repack.v, RUN_GIT_CMD))
+ if (run_command_v_opt(repack.v,
+ RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))
die(FAILED_RUN, repack.v[0]);
if (prune_expire) {
@@ -848,7 +848,7 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "commit-graph", "write",
"--split", "--reachable", NULL);
@@ -864,7 +864,6 @@ static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
if (!the_repository->settings.core_commit_graph)
return 0;
- close_object_store(the_repository->objects);
if (run_write_commit_graph(opts)) {
error(_("failed to write commit-graph"));
return 1;
@@ -913,7 +912,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_push(&child.args, "gc");
if (opts->auto_flag)
@@ -923,7 +922,6 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
else
strvec_push(&child.args, "--no-quiet");
- close_object_store(the_repository->objects);
return run_command(&child);
}
@@ -1097,14 +1095,12 @@ static int multi_pack_index_expire(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "multi-pack-index", "expire", NULL);
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
- close_object_store(the_repository->objects);
-
if (run_command(&child))
return error(_("'git multi-pack-index expire' failed"));
@@ -1155,7 +1151,7 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "multi-pack-index", "repack", NULL);
if (opts->quiet)
@@ -1164,8 +1160,6 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
strvec_pushf(&child.args, "--batch-size=%"PRIuMAX,
(uintmax_t)get_auto_pack_size());
- close_object_store(the_repository->objects);
-
if (run_command(&child))
return error(_("'git multi-pack-index repack' failed"));
@@ -1529,6 +1523,93 @@ static const char *get_frequency(enum schedule_priority schedule)
}
}
+/*
+ * get_schedule_cmd` reads the GIT_TEST_MAINT_SCHEDULER environment variable
+ * to mock the schedulers that `git maintenance start` rely on.
+ *
+ * For test purpose, GIT_TEST_MAINT_SCHEDULER can be set to a comma-separated
+ * list of colon-separated key/value pairs where each pair contains a scheduler
+ * and its corresponding mock.
+ *
+ * * If $GIT_TEST_MAINT_SCHEDULER is not set, return false and leave the
+ * arguments unmodified.
+ *
+ * * If $GIT_TEST_MAINT_SCHEDULER is set, return true.
+ * In this case, the *cmd value is read as input.
+ *
+ * * if the input value *cmd is the key of one of the comma-separated list
+ * item, then *is_available is set to true and *cmd is modified and becomes
+ * the mock command.
+ *
+ * * if the input value *cmd isn’t the key of any of the comma-separated list
+ * item, then *is_available is set to false.
+ *
+ * Ex.:
+ * GIT_TEST_MAINT_SCHEDULER not set
+ * +-------+-------------------------------------------------+
+ * | Input | Output |
+ * | *cmd | return code | *cmd | *is_available |
+ * +-------+-------------+-------------------+---------------+
+ * | "foo" | false | "foo" (unchanged) | (unchanged) |
+ * +-------+-------------+-------------------+---------------+
+ *
+ * GIT_TEST_MAINT_SCHEDULER set to “foo:./mock_foo.sh,bar:./mock_bar.sh”
+ * +-------+-------------------------------------------------+
+ * | Input | Output |
+ * | *cmd | return code | *cmd | *is_available |
+ * +-------+-------------+-------------------+---------------+
+ * | "foo" | true | "./mock.foo.sh" | true |
+ * | "qux" | true | "qux" (unchanged) | false |
+ * +-------+-------------+-------------------+---------------+
+ */
+static int get_schedule_cmd(const char **cmd, int *is_available)
+{
+ char *testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
+ struct string_list_item *item;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+
+ if (!testing)
+ return 0;
+
+ if (is_available)
+ *is_available = 0;
+
+ string_list_split_in_place(&list, testing, ',', -1);
+ for_each_string_list_item(item, &list) {
+ struct string_list pair = STRING_LIST_INIT_NODUP;
+
+ if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
+ continue;
+
+ if (!strcmp(*cmd, pair.items[0].string)) {
+ *cmd = pair.items[1].string;
+ if (is_available)
+ *is_available = 1;
+ string_list_clear(&list, 0);
+ UNLEAK(testing);
+ return 1;
+ }
+ }
+
+ string_list_clear(&list, 0);
+ free(testing);
+ return 1;
+}
+
+static int is_launchctl_available(void)
+{
+ const char *cmd = "launchctl";
+ int is_available;
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef __APPLE__
+ return 1;
+#else
+ return 0;
+#endif
+}
+
static char *launchctl_service_name(const char *frequency)
{
struct strbuf label = STRBUF_INIT;
@@ -1542,7 +1623,7 @@ static char *launchctl_service_filename(const char *name)
struct strbuf filename = STRBUF_INIT;
strbuf_addf(&filename, "~/Library/LaunchAgents/%s.plist", name);
- expanded = expand_user_path(filename.buf, 1);
+ expanded = interpolate_path(filename.buf, 1);
if (!expanded)
die(_("failed to expand path '%s'"), filename.buf);
@@ -1555,19 +1636,17 @@ static char *launchctl_get_uid(void)
return xstrfmt("gui/%d", getuid());
}
-static int launchctl_boot_plist(int enable, const char *filename, const char *cmd)
+static int launchctl_boot_plist(int enable, const char *filename)
{
+ const char *cmd = "launchctl";
int result;
struct child_process child = CHILD_PROCESS_INIT;
char *uid = launchctl_get_uid();
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&child.args, cmd);
- if (enable)
- strvec_push(&child.args, "bootstrap");
- else
- strvec_push(&child.args, "bootout");
- strvec_push(&child.args, uid);
- strvec_push(&child.args, filename);
+ strvec_pushl(&child.args, enable ? "bootstrap" : "bootout", uid,
+ filename, NULL);
child.no_stderr = 1;
child.no_stdout = 1;
@@ -1581,38 +1660,56 @@ static int launchctl_boot_plist(int enable, const char *filename, const char *cm
return result;
}
-static int launchctl_remove_plist(enum schedule_priority schedule, const char *cmd)
+static int launchctl_remove_plist(enum schedule_priority schedule)
{
const char *frequency = get_frequency(schedule);
char *name = launchctl_service_name(frequency);
char *filename = launchctl_service_filename(name);
- int result = launchctl_boot_plist(0, filename, cmd);
+ int result = launchctl_boot_plist(0, filename);
unlink(filename);
free(filename);
free(name);
return result;
}
-static int launchctl_remove_plists(const char *cmd)
+static int launchctl_remove_plists(void)
{
- return launchctl_remove_plist(SCHEDULE_HOURLY, cmd) ||
- launchctl_remove_plist(SCHEDULE_DAILY, cmd) ||
- launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
+ return launchctl_remove_plist(SCHEDULE_HOURLY) ||
+ launchctl_remove_plist(SCHEDULE_DAILY) ||
+ launchctl_remove_plist(SCHEDULE_WEEKLY);
}
-static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+static int launchctl_list_contains_plist(const char *name, const char *cmd)
{
- FILE *plist;
- int i;
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ strvec_split(&child.args, cmd);
+ strvec_pushl(&child.args, "list", name, NULL);
+
+ child.no_stderr = 1;
+ child.no_stdout = 1;
+
+ if (start_command(&child))
+ die(_("failed to start launchctl"));
+
+ /* Returns failure if 'name' doesn't exist. */
+ return !finish_command(&child);
+}
+
+static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule)
+{
+ int i, fd;
const char *preamble, *repeat;
const char *frequency = get_frequency(schedule);
char *name = launchctl_service_name(frequency);
char *filename = launchctl_service_filename(name);
+ struct lock_file lk = LOCK_INIT;
+ static unsigned long lock_file_timeout_ms = ULONG_MAX;
+ struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
+ struct stat st;
+ const char *cmd = "launchctl";
- if (safe_create_leading_directories(filename))
- die(_("failed to create directories for '%s'"), filename);
- plist = xfopen(filename, "w");
-
+ get_schedule_cmd(&cmd, NULL);
preamble = "<?xml version=\"1.0\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">"
@@ -1630,7 +1727,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"</array>\n"
"<key>StartCalendarInterval</key>\n"
"<array>\n";
- fprintf(plist, preamble, name, exec_path, exec_path, frequency);
+ strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency);
switch (schedule) {
case SCHEDULE_HOURLY:
@@ -1639,7 +1736,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"<key>Minute</key><integer>0</integer>\n"
"</dict>\n";
for (i = 1; i <= 23; i++)
- fprintf(plist, repeat, i);
+ strbuf_addf(&plist, repeat, i);
break;
case SCHEDULE_DAILY:
@@ -1649,50 +1746,91 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"<key>Minute</key><integer>0</integer>\n"
"</dict>\n";
for (i = 1; i <= 6; i++)
- fprintf(plist, repeat, i);
+ strbuf_addf(&plist, repeat, i);
break;
case SCHEDULE_WEEKLY:
- fprintf(plist,
- "<dict>\n"
- "<key>Day</key><integer>0</integer>\n"
- "<key>Hour</key><integer>0</integer>\n"
- "<key>Minute</key><integer>0</integer>\n"
- "</dict>\n");
+ strbuf_addstr(&plist,
+ "<dict>\n"
+ "<key>Day</key><integer>0</integer>\n"
+ "<key>Hour</key><integer>0</integer>\n"
+ "<key>Minute</key><integer>0</integer>\n"
+ "</dict>\n");
break;
default:
/* unreachable */
break;
}
- fprintf(plist, "</array>\n</dict>\n</plist>\n");
- fclose(plist);
+ strbuf_addstr(&plist, "</array>\n</dict>\n</plist>\n");
+
+ if (safe_create_leading_directories(filename))
+ die(_("failed to create directories for '%s'"), filename);
- /* bootout might fail if not already running, so ignore */
- launchctl_boot_plist(0, filename, cmd);
- if (launchctl_boot_plist(1, filename, cmd))
- die(_("failed to bootstrap service %s"), filename);
+ if ((long)lock_file_timeout_ms < 0 &&
+ git_config_get_ulong("gc.launchctlplistlocktimeoutms",
+ &lock_file_timeout_ms))
+ lock_file_timeout_ms = 150;
+
+ fd = hold_lock_file_for_update_timeout(&lk, filename, LOCK_DIE_ON_ERROR,
+ lock_file_timeout_ms);
+
+ /*
+ * Does this file already exist? With the intended contents? Is it
+ * registered already? Then it does not need to be re-registered.
+ */
+ if (!stat(filename, &st) && st.st_size == plist.len &&
+ strbuf_read_file(&plist2, filename, plist.len) == plist.len &&
+ !strbuf_cmp(&plist, &plist2) &&
+ launchctl_list_contains_plist(name, cmd))
+ rollback_lock_file(&lk);
+ else {
+ if (write_in_full(fd, plist.buf, plist.len) < 0 ||
+ commit_lock_file(&lk))
+ die_errno(_("could not write '%s'"), filename);
+
+ /* bootout might fail if not already running, so ignore */
+ launchctl_boot_plist(0, filename);
+ if (launchctl_boot_plist(1, filename))
+ die(_("failed to bootstrap service %s"), filename);
+ }
free(filename);
free(name);
+ strbuf_release(&plist);
+ strbuf_release(&plist2);
return 0;
}
-static int launchctl_add_plists(const char *cmd)
+static int launchctl_add_plists(void)
{
const char *exec_path = git_exec_path();
- return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY, cmd) ||
- launchctl_schedule_plist(exec_path, SCHEDULE_DAILY, cmd) ||
- launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY, cmd);
+ return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY) ||
+ launchctl_schedule_plist(exec_path, SCHEDULE_DAILY) ||
+ launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY);
}
-static int launchctl_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int launchctl_update_schedule(int run_maintenance, int fd)
{
if (run_maintenance)
- return launchctl_add_plists(cmd);
+ return launchctl_add_plists();
else
- return launchctl_remove_plists(cmd);
+ return launchctl_remove_plists();
+}
+
+static int is_schtasks_available(void)
+{
+ const char *cmd = "schtasks";
+ int is_available;
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef GIT_WINDOWS_NATIVE
+ return 1;
+#else
+ return 0;
+#endif
}
static char *schtasks_task_name(const char *frequency)
@@ -1702,13 +1840,15 @@ static char *schtasks_task_name(const char *frequency)
return strbuf_detach(&label, NULL);
}
-static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd)
+static int schtasks_remove_task(enum schedule_priority schedule)
{
+ const char *cmd = "schtasks";
int result;
struct strvec args = STRVEC_INIT;
const char *frequency = get_frequency(schedule);
char *name = schtasks_task_name(frequency);
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&args, cmd);
strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
@@ -1719,15 +1859,16 @@ static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd
return result;
}
-static int schtasks_remove_tasks(const char *cmd)
+static int schtasks_remove_tasks(void)
{
- return schtasks_remove_task(SCHEDULE_HOURLY, cmd) ||
- schtasks_remove_task(SCHEDULE_DAILY, cmd) ||
- schtasks_remove_task(SCHEDULE_WEEKLY, cmd);
+ return schtasks_remove_task(SCHEDULE_HOURLY) ||
+ schtasks_remove_task(SCHEDULE_DAILY) ||
+ schtasks_remove_task(SCHEDULE_WEEKLY);
}
-static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule)
{
+ const char *cmd = "schtasks";
int result;
struct child_process child = CHILD_PROCESS_INIT;
const char *xml;
@@ -1736,6 +1877,8 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
char *name = schtasks_task_name(frequency);
struct strbuf tfilename = STRBUF_INIT;
+ get_schedule_cmd(&cmd, NULL);
+
strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX",
get_git_common_dir(), frequency);
tfile = xmks_tempfile(tfilename.buf);
@@ -1840,28 +1983,52 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
return result;
}
-static int schtasks_schedule_tasks(const char *cmd)
+static int schtasks_schedule_tasks(void)
{
const char *exec_path = git_exec_path();
- return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY, cmd) ||
- schtasks_schedule_task(exec_path, SCHEDULE_DAILY, cmd) ||
- schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY, cmd);
+ return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY) ||
+ schtasks_schedule_task(exec_path, SCHEDULE_DAILY) ||
+ schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY);
}
-static int schtasks_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int schtasks_update_schedule(int run_maintenance, int fd)
{
if (run_maintenance)
- return schtasks_schedule_tasks(cmd);
+ return schtasks_schedule_tasks();
else
- return schtasks_remove_tasks(cmd);
+ return schtasks_remove_tasks();
+}
+
+static int is_crontab_available(void)
+{
+ 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;
+ child.no_stdout = 1;
+ child.no_stderr = 1;
+ child.silent_exec_failure = 1;
+
+ if (start_command(&child))
+ return 0;
+ /* Ignore exit code, as an empty crontab will return error. */
+ finish_command(&child);
+ return 1;
}
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
-static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int crontab_update_schedule(int run_maintenance, int fd)
{
+ const char *cmd = "crontab";
int result = 0;
int in_old_region = 0;
struct child_process crontab_list = CHILD_PROCESS_INIT;
@@ -1869,6 +2036,7 @@ static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
FILE *cron_list, *cron_in;
struct strbuf line = STRBUF_INIT;
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&crontab_list.args, cmd);
strvec_push(&crontab_list.args, "-l");
crontab_list.in = -1;
@@ -1945,66 +2113,376 @@ done_editing:
return result;
}
+static int real_is_systemd_timer_available(void)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ strvec_pushl(&child.args, "systemctl", "--user", "list-timers", NULL);
+ child.no_stdin = 1;
+ child.no_stdout = 1;
+ child.no_stderr = 1;
+ child.silent_exec_failure = 1;
+
+ if (start_command(&child))
+ return 0;
+ if (finish_command(&child))
+ return 0;
+ return 1;
+}
+
+static int is_systemd_timer_available(void)
+{
+ const char *cmd = "systemctl";
+ int is_available;
+
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+ return real_is_systemd_timer_available();
+}
+
+static char *xdg_config_home_systemd(const char *filename)
+{
+ return xdg_config_home_for("systemd/user", filename);
+}
+
+static int systemd_timer_enable_unit(int enable,
+ enum schedule_priority schedule)
+{
+ const char *cmd = "systemctl";
+ struct child_process child = CHILD_PROCESS_INIT;
+ const char *frequency = get_frequency(schedule);
+
+ /*
+ * Disabling the systemd unit while it is already disabled makes
+ * systemctl print an error.
+ * Let's ignore it since it means we already are in the expected state:
+ * the unit is disabled.
+ *
+ * On the other hand, enabling a systemd unit which is already enabled
+ * produces no error.
+ */
+ if (!enable)
+ child.no_stderr = 1;
+
+ get_schedule_cmd(&cmd, NULL);
+ strvec_split(&child.args, cmd);
+ strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
+ "--now", NULL);
+ strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency);
+
+ if (start_command(&child))
+ return error(_("failed to start systemctl"));
+ if (finish_command(&child))
+ /*
+ * Disabling an already disabled systemd unit makes
+ * systemctl fail.
+ * Let's ignore this failure.
+ *
+ * Enabling an enabled systemd unit doesn't fail.
+ */
+ if (enable)
+ return error(_("failed to run systemctl"));
+ return 0;
+}
+
+static int systemd_timer_delete_unit_templates(void)
+{
+ int ret = 0;
+ char *filename = xdg_config_home_systemd("git-maintenance@.timer");
+ if (unlink(filename) && !is_missing_file_error(errno))
+ ret = error_errno(_("failed to delete '%s'"), filename);
+ FREE_AND_NULL(filename);
+
+ filename = xdg_config_home_systemd("git-maintenance@.service");
+ if (unlink(filename) && !is_missing_file_error(errno))
+ ret = error_errno(_("failed to delete '%s'"), filename);
+
+ free(filename);
+ return ret;
+}
+
+static int systemd_timer_delete_units(void)
+{
+ return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) ||
+ systemd_timer_enable_unit(0, SCHEDULE_DAILY) ||
+ systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) ||
+ systemd_timer_delete_unit_templates();
+}
+
+static int systemd_timer_write_unit_templates(const char *exec_path)
+{
+ char *filename;
+ FILE *file;
+ const char *unit;
+
+ filename = xdg_config_home_systemd("git-maintenance@.timer");
+ if (safe_create_leading_directories(filename)) {
+ error(_("failed to create directories for '%s'"), filename);
+ goto error;
+ }
+ file = fopen_or_warn(filename, "w");
+ if (file == NULL)
+ goto error;
+
+ unit = "# This file was created and is maintained by Git.\n"
+ "# Any edits made in this file might be replaced in the future\n"
+ "# by a Git command.\n"
+ "\n"
+ "[Unit]\n"
+ "Description=Optimize Git repositories data\n"
+ "\n"
+ "[Timer]\n"
+ "OnCalendar=%i\n"
+ "Persistent=true\n"
+ "\n"
+ "[Install]\n"
+ "WantedBy=timers.target\n";
+ if (fputs(unit, file) == EOF) {
+ error(_("failed to write to '%s'"), filename);
+ fclose(file);
+ goto error;
+ }
+ if (fclose(file) == EOF) {
+ error_errno(_("failed to flush '%s'"), filename);
+ goto error;
+ }
+ free(filename);
+
+ filename = xdg_config_home_systemd("git-maintenance@.service");
+ file = fopen_or_warn(filename, "w");
+ if (file == NULL)
+ goto error;
+
+ unit = "# This file was created and is maintained by Git.\n"
+ "# Any edits made in this file might be replaced in the future\n"
+ "# by a Git command.\n"
+ "\n"
+ "[Unit]\n"
+ "Description=Optimize Git repositories data\n"
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n"
+ "LockPersonality=yes\n"
+ "MemoryDenyWriteExecute=yes\n"
+ "NoNewPrivileges=yes\n"
+ "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n"
+ "RestrictNamespaces=yes\n"
+ "RestrictRealtime=yes\n"
+ "RestrictSUIDSGID=yes\n"
+ "SystemCallArchitectures=native\n"
+ "SystemCallFilter=@system-service\n";
+ if (fprintf(file, unit, exec_path, exec_path) < 0) {
+ error(_("failed to write to '%s'"), filename);
+ fclose(file);
+ goto error;
+ }
+ if (fclose(file) == EOF) {
+ error_errno(_("failed to flush '%s'"), filename);
+ goto error;
+ }
+ free(filename);
+ return 0;
+
+error:
+ free(filename);
+ systemd_timer_delete_unit_templates();
+ return -1;
+}
+
+static int systemd_timer_setup_units(void)
+{
+ const char *exec_path = git_exec_path();
+
+ int ret = systemd_timer_write_unit_templates(exec_path) ||
+ systemd_timer_enable_unit(1, SCHEDULE_HOURLY) ||
+ systemd_timer_enable_unit(1, SCHEDULE_DAILY) ||
+ systemd_timer_enable_unit(1, SCHEDULE_WEEKLY);
+ if (ret)
+ systemd_timer_delete_units();
+ return ret;
+}
+
+static int systemd_timer_update_schedule(int run_maintenance, int fd)
+{
+ if (run_maintenance)
+ return systemd_timer_setup_units();
+ else
+ return systemd_timer_delete_units();
+}
+
+enum scheduler {
+ SCHEDULER_INVALID = -1,
+ SCHEDULER_AUTO,
+ SCHEDULER_CRON,
+ SCHEDULER_SYSTEMD,
+ SCHEDULER_LAUNCHCTL,
+ SCHEDULER_SCHTASKS,
+};
+
+static const struct {
+ const char *name;
+ int (*is_available)(void);
+ int (*update_schedule)(int run_maintenance, int fd);
+} scheduler_fn[] = {
+ [SCHEDULER_CRON] = {
+ .name = "crontab",
+ .is_available = is_crontab_available,
+ .update_schedule = crontab_update_schedule,
+ },
+ [SCHEDULER_SYSTEMD] = {
+ .name = "systemctl",
+ .is_available = is_systemd_timer_available,
+ .update_schedule = systemd_timer_update_schedule,
+ },
+ [SCHEDULER_LAUNCHCTL] = {
+ .name = "launchctl",
+ .is_available = is_launchctl_available,
+ .update_schedule = launchctl_update_schedule,
+ },
+ [SCHEDULER_SCHTASKS] = {
+ .name = "schtasks",
+ .is_available = is_schtasks_available,
+ .update_schedule = schtasks_update_schedule,
+ },
+};
+
+static enum scheduler parse_scheduler(const char *value)
+{
+ if (!value)
+ return SCHEDULER_INVALID;
+ else if (!strcasecmp(value, "auto"))
+ return SCHEDULER_AUTO;
+ else if (!strcasecmp(value, "cron") || !strcasecmp(value, "crontab"))
+ return SCHEDULER_CRON;
+ else if (!strcasecmp(value, "systemd") ||
+ !strcasecmp(value, "systemd-timer"))
+ return SCHEDULER_SYSTEMD;
+ else if (!strcasecmp(value, "launchctl"))
+ return SCHEDULER_LAUNCHCTL;
+ else if (!strcasecmp(value, "schtasks"))
+ return SCHEDULER_SCHTASKS;
+ else
+ return SCHEDULER_INVALID;
+}
+
+static int maintenance_opt_scheduler(const struct option *opt, const char *arg,
+ int unset)
+{
+ enum scheduler *scheduler = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ *scheduler = parse_scheduler(arg);
+ if (*scheduler == SCHEDULER_INVALID)
+ return error(_("unrecognized --scheduler argument '%s'"), arg);
+ return 0;
+}
+
+struct maintenance_start_opts {
+ enum scheduler scheduler;
+};
+
+static enum scheduler resolve_scheduler(enum scheduler scheduler)
+{
+ if (scheduler != SCHEDULER_AUTO)
+ return scheduler;
+
#if defined(__APPLE__)
-static const char platform_scheduler[] = "launchctl";
+ return SCHEDULER_LAUNCHCTL;
+
#elif defined(GIT_WINDOWS_NATIVE)
-static const char platform_scheduler[] = "schtasks";
+ return SCHEDULER_SCHTASKS;
+
+#elif defined(__linux__)
+ if (is_systemd_timer_available())
+ return SCHEDULER_SYSTEMD;
+ else if (is_crontab_available())
+ return SCHEDULER_CRON;
+ else
+ die(_("neither systemd timers nor crontab are available"));
+
#else
-static const char platform_scheduler[] = "crontab";
+ return SCHEDULER_CRON;
#endif
+}
-static int update_background_schedule(int enable)
+static void validate_scheduler(enum scheduler scheduler)
{
- int result;
- const char *scheduler = platform_scheduler;
- const char *cmd = scheduler;
- char *testing;
+ if (scheduler == SCHEDULER_INVALID)
+ BUG("invalid scheduler");
+ if (scheduler == SCHEDULER_AUTO)
+ BUG("resolve_scheduler should have been called before");
+
+ if (!scheduler_fn[scheduler].is_available())
+ die(_("%s scheduler is not available"),
+ scheduler_fn[scheduler].name);
+}
+
+static int update_background_schedule(const struct maintenance_start_opts *opts,
+ int enable)
+{
+ unsigned int i;
+ int result = 0;
struct lock_file lk;
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
- testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
- if (testing) {
- char *sep = strchr(testing, ':');
- if (!sep)
- die("GIT_TEST_MAINT_SCHEDULER unparseable: %s", testing);
- *sep = '\0';
- scheduler = testing;
- cmd = sep + 1;
+ if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
+ free(lock_path);
+ return error(_("another process is scheduling background maintenance"));
}
- if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
- result = error(_("another process is scheduling background maintenance"));
- goto cleanup;
+ for (i = 1; i < ARRAY_SIZE(scheduler_fn); i++) {
+ if (enable && opts->scheduler == i)
+ continue;
+ if (!scheduler_fn[i].is_available())
+ continue;
+ scheduler_fn[i].update_schedule(0, get_lock_file_fd(&lk));
}
- if (!strcmp(scheduler, "launchctl"))
- result = launchctl_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else if (!strcmp(scheduler, "schtasks"))
- result = schtasks_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else if (!strcmp(scheduler, "crontab"))
- result = crontab_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else
- die("unknown background scheduler: %s", scheduler);
+ if (enable)
+ result = scheduler_fn[opts->scheduler].update_schedule(
+ 1, get_lock_file_fd(&lk));
rollback_lock_file(&lk);
-cleanup:
free(lock_path);
- free(testing);
return result;
}
-static int maintenance_start(void)
+static const char *const builtin_maintenance_start_usage[] = {
+ N_("git maintenance start [--scheduler=<scheduler>]"),
+ NULL
+};
+
+static int maintenance_start(int argc, const char **argv, const char *prefix)
{
+ struct maintenance_start_opts opts = { 0 };
+ struct option options[] = {
+ OPT_CALLBACK_F(
+ 0, "scheduler", &opts.scheduler, N_("scheduler"),
+ N_("scheduler to trigger git maintenance run"),
+ PARSE_OPT_NONEG, maintenance_opt_scheduler),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_start_usage, 0);
+ if (argc)
+ usage_with_options(builtin_maintenance_start_usage, options);
+
+ opts.scheduler = resolve_scheduler(opts.scheduler);
+ validate_scheduler(opts.scheduler);
+
if (maintenance_register())
warning(_("failed to add repo to global config"));
-
- return update_background_schedule(1);
+ return update_background_schedule(&opts, 1);
}
static int maintenance_stop(void)
{
- return update_background_schedule(0);
+ return update_background_schedule(NULL, 0);
}
static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]");
@@ -2018,7 +2496,7 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "run"))
return maintenance_run(argc - 1, argv + 1, prefix);
if (!strcmp(argv[1], "start"))
- return maintenance_start();
+ return maintenance_start(argc - 1, argv + 1, prefix);
if (!strcmp(argv[1], "stop"))
return maintenance_stop();
if (!strcmp(argv[1], "register"))
diff --git a/builtin/grep.c b/builtin/grep.c
index 7d2f8e5adb..8af5249a7b 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -65,6 +65,9 @@ static int todo_done;
/* Has all work items been added? */
static int all_work_added;
+static struct repository **repos_to_free;
+static size_t repos_to_free_nr, repos_to_free_alloc;
+
/* This lock protects all the variables above. */
static pthread_mutex_t grep_mutex;
@@ -168,6 +171,19 @@ static void work_done(struct work_item *w)
grep_unlock();
}
+static void free_repos(void)
+{
+ int i;
+
+ for (i = 0; i < repos_to_free_nr; i++) {
+ repo_clear(repos_to_free[i]);
+ free(repos_to_free[i]);
+ }
+ FREE_AND_NULL(repos_to_free);
+ repos_to_free_nr = 0;
+ repos_to_free_alloc = 0;
+}
+
static void *run(void *arg)
{
int hit = 0;
@@ -333,7 +349,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
struct grep_source gs;
grep_source_name(opt, filename, tree_name_len, &pathbuf);
- grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
+ grep_source_init_oid(&gs, pathbuf.buf, path, oid, opt->repo);
strbuf_release(&pathbuf);
if (num_threads > 1) {
@@ -359,7 +375,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
struct grep_source gs;
grep_source_name(opt, filename, 0, &buf);
- grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
+ grep_source_init_file(&gs, buf.buf, filename);
strbuf_release(&buf);
if (num_threads > 1) {
@@ -415,19 +431,21 @@ static int grep_submodule(struct grep_opt *opt,
const struct object_id *oid,
const char *filename, const char *path, int cached)
{
- struct repository subrepo;
+ struct repository *subrepo;
struct repository *superproject = opt->repo;
- const struct submodule *sub;
struct grep_opt subopt;
- int hit;
-
- sub = submodule_from_path(superproject, null_oid(), path);
+ int hit = 0;
if (!is_submodule_active(superproject, path))
return 0;
- if (repo_submodule_init(&subrepo, superproject, sub))
+ subrepo = xmalloc(sizeof(*subrepo));
+ if (repo_submodule_init(subrepo, superproject, path, null_oid())) {
+ free(subrepo);
return 0;
+ }
+ ALLOC_GROW(repos_to_free, repos_to_free_nr + 1, repos_to_free_alloc);
+ repos_to_free[repos_to_free_nr++] = subrepo;
/*
* NEEDSWORK: repo_read_gitmodules() might call
@@ -438,53 +456,49 @@ static int grep_submodule(struct grep_opt *opt,
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
- repo_read_gitmodules(&subrepo, 0);
+ repo_read_gitmodules(subrepo, 0);
/*
- * NEEDSWORK: This adds the submodule's object directory to the list of
- * alternates for the single in-memory object store. This has some bad
- * consequences for memory (processed objects will never be freed) and
- * performance (this increases the number of pack files git has to pay
- * attention to, to the sum of the number of pack files in all the
- * repositories processed so far). This can be removed once the object
- * store is no longer global and instead is a member of the repository
- * object.
+ * All code paths tested by test code no longer need submodule ODBs to
+ * be added as alternates, but add it to the list just in case.
+ * Submodule ODBs added through add_submodule_odb_by_path() will be
+ * lazily registered as alternates when needed (and except in an
+ * unexpected code interaction, it won't be needed).
*/
- add_to_alternates_memory(subrepo.objects->odb->path);
+ add_submodule_odb_by_path(subrepo->objects->odb->path);
obj_read_unlock();
memcpy(&subopt, opt, sizeof(subopt));
- subopt.repo = &subrepo;
+ subopt.repo = subrepo;
if (oid) {
- struct object *object;
+ enum object_type object_type;
struct tree_desc tree;
void *data;
unsigned long size;
struct strbuf base = STRBUF_INIT;
obj_read_lock();
- object = parse_object_or_die(oid, NULL);
+ object_type = oid_object_info(subrepo, oid, NULL);
obj_read_unlock();
- data = read_object_with_reference(&subrepo,
- &object->oid, tree_type,
+ data = read_object_with_reference(subrepo,
+ oid, tree_type,
&size, NULL);
if (!data)
- die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
+ die(_("unable to read tree (%s)"), oid_to_hex(oid));
strbuf_addstr(&base, filename);
strbuf_addch(&base, '/');
init_tree_desc(&tree, data, size);
hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
- object->type == OBJ_COMMIT);
+ object_type == OBJ_COMMIT);
strbuf_release(&base);
free(data);
} else {
hit = grep_cache(&subopt, pathspec, cached);
}
- repo_clear(&subrepo);
return hit;
}
@@ -1182,5 +1196,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
run_pager(&opt, prefix);
clear_pathspec(&pathspec);
free_grep_patterns(&opt);
+ free_repos();
return !hit;
}
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 640ef4ded5..c7b3ad74c6 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -53,9 +53,7 @@ static void hash_object(const char *path, const char *type, const char *vpath,
unsigned flags, int literally)
{
int fd;
- fd = open(path, O_RDONLY);
- if (fd < 0)
- die_errno("Cannot open '%s'", path);
+ fd = xopen(path, O_RDONLY);
hash_fd(fd, type, vpath, flags, literally);
}
@@ -117,7 +115,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
if (vpath && prefix)
- vpath = xstrdup(prefix_filename(prefix, vpath));
+ vpath = prefix_filename(prefix, vpath);
git_config(git_default_config, NULL);
diff --git a/builtin/help.c b/builtin/help.c
index b7eec06c3d..75cd2fb407 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -7,7 +7,6 @@
#include "exec-cmd.h"
#include "parse-options.h"
#include "run-command.h"
-#include "column.h"
#include "config-list.h"
#include "help.h"
#include "alias.h"
@@ -34,32 +33,52 @@ enum help_format {
HELP_FORMAT_WEB
};
-static const char *html_path;
+enum show_config_type {
+ SHOW_CONFIG_HUMAN,
+ SHOW_CONFIG_VARS,
+ SHOW_CONFIG_SECTIONS,
+};
+
+static enum help_action {
+ HELP_ACTION_ALL = 1,
+ HELP_ACTION_GUIDES,
+ HELP_ACTION_CONFIG,
+ HELP_ACTION_CONFIG_FOR_COMPLETION,
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
+} cmd_mode;
-static int show_all = 0;
-static int show_guides = 0;
-static int show_config;
+static const char *html_path;
static int verbose = 1;
-static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
static struct option builtin_help_options[] = {
- OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+ OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
+ HELP_ACTION_ALL),
OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
- OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
- OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
- OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
OPT_SET_INT('i', "info", &help_format, N_("show info page"),
HELP_FORMAT_INFO),
OPT__VERBOSE(&verbose, N_("print command description")),
+
+ OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
+ HELP_ACTION_GUIDES),
+ OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
+ HELP_ACTION_CONFIG),
+ OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+
OPT_END(),
};
static const char * const builtin_help_usage[] = {
- N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"),
+ N_("git help [-a|--all] [--[no-]verbose]]\n"
+ " [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
+ N_("git help [-g|--guides]"),
+ N_("git help [-c|--config]"),
NULL
};
@@ -70,7 +89,7 @@ struct slot_expansion {
int found;
};
-static void list_config_help(int for_human)
+static void list_config_help(enum show_config_type type)
{
struct slot_expansion slot_expansions[] = {
{ "advice", "*", list_config_advices },
@@ -88,6 +107,8 @@ static void list_config_help(int for_human)
const char **p;
struct slot_expansion *e;
struct string_list keys = STRING_LIST_INIT_DUP;
+ struct string_list keys_uniq = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
int i;
for (p = config_name_list; *p; p++) {
@@ -118,34 +139,46 @@ static void list_config_help(int for_human)
for (i = 0; i < keys.nr; i++) {
const char *var = keys.items[i].string;
const char *wildcard, *tag, *cut;
+ const char *dot = NULL;
+ struct strbuf sb = STRBUF_INIT;
- if (for_human) {
+ switch (type) {
+ case SHOW_CONFIG_HUMAN:
puts(var);
continue;
+ case SHOW_CONFIG_SECTIONS:
+ dot = strchr(var, '.');
+ break;
+ case SHOW_CONFIG_VARS:
+ break;
}
-
wildcard = strchr(var, '*');
tag = strchr(var, '<');
- if (!wildcard && !tag) {
- puts(var);
+ if (!dot && !wildcard && !tag) {
+ string_list_append(&keys_uniq, var);
continue;
}
- if (wildcard && !tag)
+ if (dot)
+ cut = dot;
+ else if (wildcard && !tag)
cut = wildcard;
else if (!wildcard && tag)
cut = tag;
else
cut = wildcard < tag ? wildcard : tag;
- /*
- * We may produce duplicates, but that's up to
- * git-completion.bash to handle
- */
- printf("%.*s\n", (int)(cut - var), var);
+ strbuf_add(&sb, var, cut - var);
+ string_list_append(&keys_uniq, sb.buf);
+ strbuf_release(&sb);
+
}
string_list_clear(&keys, 0);
+ string_list_remove_duplicates(&keys_uniq, 0);
+ for_each_string_list_item(item, &keys_uniq)
+ puts(item->string);
+ string_list_clear(&keys_uniq, 0);
}
static enum help_format parse_help_format(const char *format)
@@ -349,8 +382,6 @@ static int add_man_viewer_info(const char *var, const char *value)
static int git_help_config(const char *var, const char *value, void *cb)
{
- if (starts_with(var, "column."))
- return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) {
if (!value)
return config_error_nonbool(var);
@@ -467,11 +498,14 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
if (!html_path)
html_path = to_free = system_path(GIT_HTML_PATH);
- /* Check that we have a git documentation directory. */
+ /*
+ * Check that the page we're looking for exists.
+ */
if (!strstr(html_path, "://")) {
- if (stat(mkpath("%s/git.html", html_path), &st)
+ if (stat(mkpath("%s/%s.html", html_path, page), &st)
|| !S_ISREG(st.st_mode))
- die("'%s': not a documentation directory.", html_path);
+ die("'%s/%s.html': documentation file not found.",
+ html_path, page);
}
strbuf_init(page_path, 0);
@@ -541,6 +575,13 @@ static const char *check_git_cmd(const char* cmd)
return cmd;
}
+static void no_extra_argc(int argc)
+{
+ if (argc)
+ usage_msg_opt(_("this option doesn't take any other arguments"),
+ builtin_help_usage, builtin_help_options);
+}
+
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
@@ -551,8 +592,8 @@ int cmd_help(int argc, const char **argv, const char *prefix)
builtin_help_usage, 0);
parsed_help_format = help_format;
- if (show_all) {
- git_config(git_help_config, NULL);
+ switch (cmd_mode) {
+ case HELP_ACTION_ALL:
if (verbose) {
setup_pager();
list_all_cmds_help();
@@ -560,30 +601,27 @@ int cmd_help(int argc, const char **argv, const char *prefix)
}
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
load_command_list("git-", &main_cmds, &other_cmds);
- list_commands(colopts, &main_cmds, &other_cmds);
- }
-
- if (show_config) {
- int for_human = show_config == 1;
-
- if (!for_human) {
- list_config_help(for_human);
- return 0;
- }
- setup_pager();
- list_config_help(for_human);
- printf("\n%s\n", _("'git help config' for more information"));
- return 0;
- }
-
- if (show_guides)
+ list_commands(&main_cmds, &other_cmds);
+ printf("%s\n", _(git_more_info_string));
+ break;
+ case HELP_ACTION_GUIDES:
+ no_extra_argc(argc);
list_guides_help();
-
- if (show_all || show_guides) {
printf("%s\n", _(git_more_info_string));
- /*
- * We're done. Ignore any remaining args
- */
+ return 0;
+ case HELP_ACTION_CONFIG_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_VARS);
+ return 0;
+ case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_SECTIONS);
+ return 0;
+ case HELP_ACTION_CONFIG:
+ no_extra_argc(argc);
+ setup_pager();
+ list_config_help(SHOW_CONFIG_HUMAN);
+ printf("\n%s\n", _("'git help config' for more information"));
return 0;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 3fbc5d7077..15ae406e6b 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -122,6 +122,7 @@ static int strict;
static int do_fsck_object;
static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
static int verbose;
+static const char *progress_title;
static int show_resolving_progress;
static int show_stat;
static int check_self_contained_and_connected;
@@ -187,9 +188,7 @@ static void init_thread(void)
pthread_key_create(&key, NULL);
CALLOC_ARRAY(thread_data, nr_threads);
for (i = 0; i < nr_threads; i++) {
- thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
- if (thread_data[i].pack_fd == -1)
- die_errno(_("unable to open %s"), curr_pack);
+ thread_data[i].pack_fd = xopen(curr_pack, O_RDONLY);
}
threads_active = 1;
@@ -338,15 +337,11 @@ static const char *open_pack_file(const char *pack_name)
"pack/tmp_pack_XXXXXX");
pack_name = strbuf_detach(&tmp_file, NULL);
} else {
- output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
- if (output_fd < 0)
- die_errno(_("unable to create '%s'"), pack_name);
+ output_fd = xopen(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
}
nothread_data.pack_fd = output_fd;
} else {
- input_fd = open(pack_name, O_RDONLY);
- if (input_fd < 0)
- die_errno(_("cannot open packfile '%s'"), pack_name);
+ input_fd = xopen(pack_name, O_RDONLY);
output_fd = -1;
nothread_data.pack_fd = input_fd;
}
@@ -369,9 +364,7 @@ static void parse_pack_header(void)
use(sizeof(struct pack_header));
}
-static NORETURN void bad_object(off_t offset, const char *format,
- ...) __attribute__((format (printf, 2, 3)));
-
+__attribute__((format (printf, 2, 3)))
static NORETURN void bad_object(off_t offset, const char *format, ...)
{
va_list params;
@@ -1159,6 +1152,7 @@ static void parse_pack_objects(unsigned char *hash)
if (verbose)
progress = start_progress(
+ progress_title ? progress_title :
from_stdin ? _("Receiving objects") : _("Indexing objects"),
nr_objects);
for (i = 0; i < nr_objects; i++) {
@@ -1421,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));
/*
@@ -1483,6 +1477,22 @@ static void write_special_file(const char *suffix, const char *msg,
strbuf_release(&name_buf);
}
+static void rename_tmp_packfile(const char **final_name,
+ const char *curr_name,
+ struct strbuf *name, unsigned char *hash,
+ const char *ext, int make_read_only_if_same)
+{
+ if (*final_name != curr_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"),
+ ext, *final_name);
+ } else if (make_read_only_if_same) {
+ chmod(*final_name, 0444);
+ }
+}
+
static void final(const char *final_pack_name, const char *curr_pack_name,
const char *final_index_name, const char *curr_index_name,
const char *final_rev_index_name, const char *curr_rev_index_name,
@@ -1511,31 +1521,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
write_special_file("promisor", promisor_msg, final_pack_name,
hash, NULL);
- if (final_pack_name != curr_pack_name) {
- if (!final_pack_name)
- final_pack_name = odb_pack_name(&pack_name, hash, "pack");
- if (finalize_object_file(curr_pack_name, final_pack_name))
- die(_("cannot store pack file"));
- } else if (from_stdin)
- chmod(final_pack_name, 0444);
-
- if (final_index_name != curr_index_name) {
- if (!final_index_name)
- final_index_name = odb_pack_name(&index_name, hash, "idx");
- if (finalize_object_file(curr_index_name, final_index_name))
- die(_("cannot store index file"));
- } else
- chmod(final_index_name, 0444);
-
- if (curr_rev_index_name) {
- if (final_rev_index_name != curr_rev_index_name) {
- if (!final_rev_index_name)
- final_rev_index_name = odb_pack_name(&rev_index_name, hash, "rev");
- if (finalize_object_file(curr_rev_index_name, final_rev_index_name))
- die(_("cannot store reverse index file"));
- } else
- chmod(final_rev_index_name, 0444);
- }
+ rename_tmp_packfile(&final_pack_name, curr_pack_name, &pack_name,
+ hash, "pack", from_stdin);
+ if (curr_rev_index_name)
+ rename_tmp_packfile(&final_rev_index_name, curr_rev_index_name,
+ &rev_index_name, hash, "rev", 1);
+ rename_tmp_packfile(&final_index_name, curr_index_name, &index_name,
+ hash, "idx", 1);
if (do_fsck_object) {
struct packed_git *p;
@@ -1808,6 +1800,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) {
verbose = 1;
+ } else if (!strcmp(arg, "--progress-title")) {
+ if (progress_title || (i+1) >= argc)
+ usage(index_pack_usage);
+ progress_title = argv[++i];
} else if (!strcmp(arg, "--show-resolving-progress")) {
show_resolving_progress = 1;
} else if (!strcmp(arg, "--report-end-of-input")) {
diff --git a/builtin/log.c b/builtin/log.c
index 516a1142dd..f75d87e8d7 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -245,6 +245,9 @@ 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 (decoration_style) {
const struct string_list *config_exclude =
repo_config_get_value_multi(the_repository,
@@ -634,7 +637,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
repo_init_revisions(the_repository, &rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
- rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
+ rev.no_walk = 1;
rev.diffopt.stat_width = -1; /* Scale to real terminal size */
memset(&opt, 0, sizeof(opt));
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 29a26ad8ae..a2000ed6bf 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -209,10 +209,8 @@ static void show_submodule(struct repository *superproject,
struct dir_struct *dir, const char *path)
{
struct repository subrepo;
- const struct submodule *sub = submodule_from_path(superproject,
- null_oid(), path);
- if (repo_submodule_init(&subrepo, superproject, sub))
+ if (repo_submodule_init(&subrepo, superproject, path, null_oid()))
return;
if (repo_read_index(&subrepo) < 0)
@@ -614,7 +612,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
struct option builtin_ls_files_options[] = {
/* Think twice before adding "--nul" synonym to this */
OPT_SET_INT('z', NULL, &line_terminator,
- N_("paths are separated with NUL character"), '\0'),
+ N_("separate paths with the NUL character"), '\0'),
OPT_BOOL('t', NULL, &show_tag,
N_("identify the file status with tags")),
OPT_BOOL('v', NULL, &show_valid_bit,
@@ -651,7 +649,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("skip files matching pattern"),
PARSE_OPT_NONEG, option_parse_exclude),
OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"),
- N_("exclude patterns are read from <file>"),
+ N_("read exclude patterns from <file>"),
PARSE_OPT_NONEG, option_parse_exclude_from),
OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
N_("read additional per-directory exclude patterns in <file>")),
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 1794548c71..318949c3d7 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -7,8 +7,8 @@
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
- " [-q | --quiet] [--exit-code] [--get-url]\n"
- " [--symref] [<repository> [<refs>...]]"),
+ " [-q | --quiet] [--exit-code] [--get-url]\n"
+ " [--symref] [<repository> [<refs>...]]"),
NULL
};
@@ -84,6 +84,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION);
dest = argv[0];
+ packet_trace_identity("ls-remote");
+
UNLEAK(sorting);
if (argc > 1) {
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 664400b816..7baef30569 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -75,9 +75,7 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
fprintf(stderr, "corrupt mailbox\n");
exit(1);
}
- fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd < 0)
- die_errno("cannot open output file '%s'", name);
+ fd = xopen(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
output = xfdopen(fd, "w");
/* Copy it out, while searching for a line that begins with
diff --git a/builtin/merge.c b/builtin/merge.c
index a8a843b1f5..cc4a910c69 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -13,6 +13,7 @@
#include "builtin.h"
#include "lockfile.h"
#include "run-command.h"
+#include "hook.h"
#include "diff.h"
#include "diff-merges.h"
#include "refs.h"
@@ -88,9 +89,9 @@ static int autostash;
static int no_verify;
static struct strategy all_strategy[] = {
- { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "recursive", NO_TRIVIAL },
{ "octopus", DEFAULT_OCTOPUS },
- { "ort", NO_TRIVIAL },
+ { "ort", DEFAULT_TWOHEAD | NO_TRIVIAL },
{ "resolve", 0 },
{ "ours", NO_FAST_FORWARD | NO_TRIVIAL },
{ "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
@@ -469,7 +470,6 @@ static void finish(struct commit *head_commit,
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- close_object_store(the_repository->objects);
run_auto_maintenance(verbosity < 0);
}
}
@@ -503,7 +503,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
struct strbuf bname = STRBUF_INIT;
struct merge_remote_desc *desc;
const char *ptr;
- char *found_ref;
+ char *found_ref = NULL;
int len, early;
strbuf_branchname(&bname, remote, 0);
@@ -586,6 +586,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addf(msg, "%s\t\tcommit '%s'\n",
oid_to_hex(&remote_head->object.oid), remote);
cleanup:
+ free(found_ref);
strbuf_release(&buf);
strbuf_release(&bname);
}
@@ -680,6 +681,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
trees[nr_trees] = parse_tree_indirect(common);
if (!trees[nr_trees++])
return -1;
@@ -738,7 +740,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (x = 0; x < xopts_nr; x++)
if (parse_merge_opt(&o, xopts[x]))
- die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
+ die(_("unknown strategy option: -X%s"), xopts[x]);
o.branch1 = head_arg;
o.branch2 = merge_remote_util(remoteheads->item)->name;
@@ -848,7 +850,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* and write it out as a tree. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- if (find_hook("pre-merge-commit"))
+ if (hook_exists("pre-merge-commit"))
discard_cache();
read_cache_from(index_file);
strbuf_addbuf(&msg, &merge_msg);
@@ -861,9 +863,11 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_commented_addf(&msg, "\n");
}
strbuf_commented_addf(&msg, _(merge_editor_comment));
- strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
- scissors_editor_comment :
- no_scissors_editor_comment), comment_line_char);
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+ strbuf_commented_addf(&msg, _(scissors_editor_comment));
+ else
+ strbuf_commented_addf(&msg,
+ _(no_scissors_editor_comment), comment_line_char);
}
if (signoff)
append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
@@ -1135,9 +1139,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
merge_names = &fetch_head_file;
filename = git_path_fetch_head(the_repository);
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- die_errno(_("could not open '%s' for reading"), filename);
+ fd = xopen(filename, O_RDONLY);
if (strbuf_read(merge_names, fd, 0) < 0)
die_errno(_("could not read '%s'"), filename);
@@ -1275,6 +1277,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
/*
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
@@ -1367,14 +1372,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* There is no unmerged entry, don't advise 'git
* add/rm <file>', just 'git commit'.
*/
- if (advice_resolve_conflict)
+ if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
"Please, commit your changes before you merge."));
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
if (ref_exists("CHERRY_PICK_HEAD")) {
- if (advice_resolve_conflict)
+ if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you merge."));
else
@@ -1484,6 +1489,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
fast_forward = FF_NO;
}
+ if (!use_strategies && !pull_twohead &&
+ remoteheads && !remoteheads->next) {
+ char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
+ if (default_strategy)
+ append_strategy(get_strategy(default_strategy));
+ }
if (!use_strategies) {
if (!remoteheads)
; /* already up-to-date */
@@ -1560,6 +1571,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
&head_commit->object.oid,
&commit->object.oid,
overwrite_ignore)) {
+ apply_autostash(git_path_merge_autostash(the_repository));
ret = 1;
goto done;
}
@@ -1620,7 +1632,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (fast_forward == FF_ONLY)
- die(_("Not possible to fast-forward, aborting."));
+ die_ff_impossible();
if (autostash)
create_autostash(the_repository,
@@ -1708,6 +1720,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
fprintf(stderr, _("Merge with strategy %s failed.\n"),
use_strategies[0]->name);
+ apply_autostash(git_path_merge_autostash(the_repository));
ret = 2;
goto done;
} else if (best_strategy == wt_strategy)
@@ -1715,7 +1728,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else {
printf(_("Rewinding the tree to pristine...\n"));
restore_state(&head_commit->object.oid, &stash);
- printf(_("Using the %s to prepare resolving by hand.\n"),
+ printf(_("Using the %s strategy to prepare resolving by hand.\n"),
best_strategy);
try_merge_strategy(best_strategy, common, remoteheads,
head_commit);
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 5d3ea445fd..075d15d706 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,14 +46,15 @@ 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[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
- OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -61,6 +63,33 @@ static struct option *add_common_options(struct option *prev)
return parse_options_concat(common_opts, prev);
}
+static int git_multi_pack_index_write_config(const char *var, const char *value,
+ void *cb)
+{
+ if (!strcmp(var, "pack.writebitmaphashcache")) {
+ if (git_config_bool(var, value))
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+ else
+ opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE;
+ }
+
+ /*
+ * We should never make a fall-back call to 'git_default_config', since
+ * this was already called in 'cmd_multi_pack_index()'.
+ */
+ 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;
@@ -68,13 +97,27 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
OPT_STRING(0, "preferred-pack", &opts.preferred_pack,
N_("preferred-pack"),
N_("pack for reuse when computing a multi-pack bitmap")),
+ OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"),
+ 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(),
};
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+
+ git_config(git_multi_pack_index_write_config, NULL);
+
options = add_common_options(builtin_multi_pack_index_write_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_write_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -84,16 +127,39 @@ 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)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_verify_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_verify_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_verify_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -106,10 +172,18 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
static int cmd_multi_pack_index_expire(int argc, const char **argv)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_expire_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_expire_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_expire_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -126,6 +200,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
static struct option builtin_multi_pack_index_repack_options[] = {
OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -133,6 +209,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options,
builtin_multi_pack_index_repack_usage,
@@ -154,8 +232,6 @@ int cmd_multi_pack_index(int argc, const char **argv,
git_config(git_default_config, NULL);
- if (isatty(2))
- opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage,
@@ -164,7 +240,7 @@ int cmd_multi_pack_index(int argc, const char **argv,
if (!opts.object_dir)
opts.object_dir = get_object_directory();
- if (argc == 0)
+ if (!argc)
goto usage;
if (!strcmp(argv[0], "repack"))
@@ -175,10 +251,9 @@ int cmd_multi_pack_index(int argc, const char **argv,
return cmd_multi_pack_index_verify(argc, argv);
else if (!strcmp(argv[0], "expire"))
return cmd_multi_pack_index_expire(argc, argv);
- else {
+
+ error(_("unrecognized subcommand: %s"), argv[0]);
usage:
- error(_("unrecognized subcommand: %s"), argv[0]);
- usage_with_options(builtin_multi_pack_index_usage,
- builtin_multi_pack_index_options);
- }
+ usage_with_options(builtin_multi_pack_index_usage,
+ builtin_multi_pack_index_options);
}
diff --git a/builtin/mv.c b/builtin/mv.c
index 3fccdcb645..83a465ba83 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -118,21 +118,23 @@ static int index_range_of_same_dir(const char *src, int length,
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
- int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+ int verbose = 0, show_only = 0, force = 0, ignore_errors = 0, ignore_sparse = 0;
struct option builtin_mv_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT__DRY_RUN(&show_only, N_("dry run")),
OPT__FORCE(&force, N_("force move/rename even if target exists"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
+ OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_END(),
};
const char **source, **destination, **dest_path, **submodule_gitfile;
- enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+ enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX, SPARSE } *modes;
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
@@ -176,14 +178,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
const char *src = source[i], *dst = destination[i];
int length, src_is_dir;
const char *bad = NULL;
+ int skip_sparse = 0;
if (show_only)
printf(_("Checking rename of '%s' to '%s'\n"), src, dst);
length = strlen(src);
- if (lstat(src, &st) < 0)
- bad = _("bad source");
- else if (!strncmp(src, dst, length) &&
+ if (lstat(src, &st) < 0) {
+ /* only error if existence is expected. */
+ if (modes[i] != SPARSE)
+ bad = _("bad source");
+ } else if (!strncmp(src, dst, length) &&
(dst[length] == 0 || dst[length] == '/')) {
bad = _("can not move directory into itself");
} else if ((src_is_dir = S_ISDIR(st.st_mode))
@@ -212,11 +217,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
dst_len = strlen(dst);
for (j = 0; j < last - first; j++) {
- const char *path = active_cache[first + j]->name;
+ const struct cache_entry *ce = active_cache[first + j];
+ const char *path = ce->name;
source[argc + j] = path;
destination[argc + j] =
prefix_path(dst, dst_len, path + length + 1);
- modes[argc + j] = INDEX;
+ modes[argc + j] = ce_skip_worktree(ce) ? SPARSE : INDEX;
submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
@@ -244,14 +250,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
bad = _("multiple sources for the same target");
else if (is_dir_sep(dst[strlen(dst) - 1]))
bad = _("destination directory does not exist");
- else
+ else {
+ /*
+ * We check if the paths are in the sparse-checkout
+ * definition as a very final check, since that
+ * allows us to point the user to the --sparse
+ * option as a way to have a successful run.
+ */
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(src, &the_index)) {
+ string_list_append(&only_match_skip_worktree, src);
+ skip_sparse = 1;
+ }
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(dst, &the_index)) {
+ string_list_append(&only_match_skip_worktree, dst);
+ skip_sparse = 1;
+ }
+
+ if (skip_sparse)
+ goto remove_entry;
+
string_list_insert(&src_for_dst, dst);
+ }
if (!bad)
continue;
if (!ignore_errors)
die(_("%s, source=%s, destination=%s"),
bad, src, dst);
+remove_entry:
if (--argc > 0) {
int n = argc - i;
memmove(source + i, source + i + 1,
@@ -266,6 +294,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
}
+ if (only_match_skip_worktree.nr) {
+ advise_on_updating_sparse_paths(&only_match_skip_worktree);
+ if (!ignore_errors)
+ return 1;
+ }
+
for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
enum update_mode mode = modes[i];
@@ -274,7 +308,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
printf(_("Renaming %s to %s\n"), src, dst);
if (show_only)
continue;
- if (mode != INDEX && rename(src, dst) < 0) {
+ if (mode != INDEX && mode != SPARSE && rename(src, dst) < 0) {
if (ignore_errors)
continue;
die_errno(_("renaming '%s' failed"), src);
@@ -303,5 +337,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("Unable to write new index file"));
+ string_list_clear(&src_for_dst, 0);
+ UNLEAK(source);
+ UNLEAK(dest_path);
+ free(submodule_gitfile);
+ free(modes);
return 0;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 74bba39ca8..71c59583a1 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -172,9 +172,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
/* write the template message before editing: */
d->edit_path = git_pathdup("NOTES_EDITMSG");
- fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0)
- die_errno(_("could not create file '%s'"), d->edit_path);
+ fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (d->given)
write_or_die(fd, d->buf.buf, d->buf.len);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index de00adbb9e..1a3dd445f8 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1124,6 +1124,11 @@ static void write_reused_pack(struct hashfile *f)
break;
offset += ewah_bit_ctz64(word >> offset);
+ /*
+ * Can use bit positions directly, even for MIDX
+ * bitmaps. See comment in try_partial_reuse()
+ * for why.
+ */
write_reused_pack_one(pos + offset, f, &w_curs);
display_progress(progress_state, ++written);
}
@@ -1217,6 +1222,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
struct strbuf tmpname = STRBUF_INIT;
+ char *idx_tmp_name = NULL;
/*
* Packs are runtime accessed in their mtime
@@ -1237,7 +1243,8 @@ static void write_pack_file(void)
warning_errno(_("failed utime() on %s"), pack_tmp_name);
}
- strbuf_addf(&tmpname, "%s-", base_name);
+ strbuf_addf(&tmpname, "%s-%s.", base_name,
+ hash_to_hex(hash));
if (write_bitmap_index) {
bitmap_writer_set_checksum(hash);
@@ -1245,23 +1252,29 @@ static void write_pack_file(void)
&to_pack, written_list, nr_written);
}
- finish_tmp_packfile(&tmpname, pack_tmp_name,
+ stage_tmp_packfiles(&tmpname, pack_tmp_name,
written_list, nr_written,
- &pack_idx_opts, hash);
+ &pack_idx_opts, hash, &idx_tmp_name);
if (write_bitmap_index) {
- strbuf_addf(&tmpname, "%s.bitmap", hash_to_hex(hash));
+ size_t tmpname_len = tmpname.len;
+ strbuf_addstr(&tmpname, "bitmap");
stop_progress(&progress_state);
bitmap_writer_show_progress(progress);
bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
- bitmap_writer_build(&to_pack);
+ if (bitmap_writer_build(&to_pack) < 0)
+ die(_("failed to write bitmap index"));
bitmap_writer_finish(written_list, nr_written,
tmpname.buf, write_bitmap_options);
write_bitmap_index = 0;
+ strbuf_setlen(&tmpname, tmpname_len);
}
+ rename_tmp_packfile_idx(&tmpname, &idx_tmp_name);
+
+ free(idx_tmp_name);
strbuf_release(&tmpname);
free(pack_tmp_name);
puts(hash_to_hex(hash));
@@ -3311,9 +3324,26 @@ static void read_packs_list_from_stdin(void)
}
/*
- * First handle all of the excluded packs, marking them as kept in-core
- * so that later calls to add_object_entry() discards any objects that
- * are also found in excluded packs.
+ * Arguments we got on stdin may not even be packs. First
+ * check that to avoid segfaulting later on in
+ * e.g. pack_mtime_cmp(), excluded packs are handled below.
+ *
+ * Since we first parsed our STDIN and then sorted the input
+ * lines the pack we error on will be whatever line happens to
+ * sort first. This is lazy, it's enough that we report one
+ * bad case here, we don't need to report the first/last one,
+ * or all of them.
+ */
+ for_each_string_list_item(item, &include_packs) {
+ struct packed_git *p = item->util;
+ if (!p)
+ die(_("could not find pack '%s'"), item->string);
+ }
+
+ /*
+ * Then, handle all of the excluded packs, marking them as
+ * kept in-core so that later calls to add_object_entry()
+ * discards any objects that are also found in excluded packs.
*/
for_each_string_list_item(item, &exclude_packs) {
struct packed_git *p = item->util;
@@ -3388,13 +3418,9 @@ static void read_object_list_from_stdin(void)
}
}
-/* Remember to update object flag allocation in object.h */
-#define OBJECT_ADDED (1u<<20)
-
static void show_commit(struct commit *commit, void *data)
{
add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
- commit->object.flags |= OBJECT_ADDED;
if (write_bitmap_index)
index_commit_for_bitmap(commit);
@@ -3407,7 +3433,6 @@ static void show_object(struct object *obj, const char *name, void *data)
{
add_preferred_base_object(name);
add_object_entry(&obj->oid, obj->type, name, 0);
- obj->flags |= OBJECT_ADDED;
if (use_delta_islands) {
const char *p;
@@ -3488,79 +3513,23 @@ static void show_edge(struct commit *commit)
add_preferred_base(&commit->object.oid);
}
-struct in_pack_object {
- off_t offset;
- struct object *object;
-};
-
-struct in_pack {
- unsigned int alloc;
- unsigned int nr;
- struct in_pack_object *array;
-};
-
-static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
-{
- in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->oid.hash, p);
- in_pack->array[in_pack->nr].object = object;
- in_pack->nr++;
-}
-
-/*
- * Compare the objects in the offset order, in order to emulate the
- * "git rev-list --objects" output that produced the pack originally.
- */
-static int ofscmp(const void *a_, const void *b_)
+static int add_object_in_unpacked_pack(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *_data)
{
- struct in_pack_object *a = (struct in_pack_object *)a_;
- struct in_pack_object *b = (struct in_pack_object *)b_;
-
- if (a->offset < b->offset)
- return -1;
- else if (a->offset > b->offset)
- return 1;
- else
- return oidcmp(&a->object->oid, &b->object->oid);
+ add_object_entry(oid, OBJ_NONE, "", 0);
+ return 0;
}
static void add_objects_in_unpacked_packs(void)
{
- struct packed_git *p;
- struct in_pack in_pack;
- uint32_t i;
-
- memset(&in_pack, 0, sizeof(in_pack));
-
- for (p = get_all_packs(the_repository); p; p = p->next) {
- struct object_id oid;
- struct object *o;
-
- if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
- continue;
- if (open_pack_index(p))
- die(_("cannot open pack index"));
-
- ALLOC_GROW(in_pack.array,
- in_pack.nr + p->num_objects,
- in_pack.alloc);
-
- for (i = 0; i < p->num_objects; i++) {
- nth_packed_object_id(&oid, p, i);
- o = lookup_unknown_object(the_repository, &oid);
- if (!(o->flags & OBJECT_ADDED))
- mark_in_pack_object(o, p, &in_pack);
- o->flags |= OBJECT_ADDED;
- }
- }
-
- if (in_pack.nr) {
- QSORT(in_pack.array, in_pack.nr, ofscmp);
- for (i = 0; i < in_pack.nr; i++) {
- struct object *o = in_pack.array[i].object;
- add_object_entry(&o->oid, o->type, "", 0);
- }
- }
- free(in_pack.array);
+ if (for_each_packed_object(add_object_in_unpacked_pack, NULL,
+ FOR_EACH_OBJECT_PACK_ORDER |
+ FOR_EACH_OBJECT_LOCAL_ONLY |
+ FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
+ FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS))
+ die(_("cannot open pack index"));
}
static int add_loose_object(const struct object_id *oid, const char *path,
diff --git a/builtin/prune.c b/builtin/prune.c
index 02c6ab7cba..485c9a3c56 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
expire = TIME_MAX;
save_commit_buffer = 0;
read_replace_refs = 0;
- ref_paranoia = 1;
repo_init_revisions(the_repository, &revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index 3e13f81084..ae9f5bd7cc 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -26,13 +26,13 @@
#include "wt-status.h"
#include "commit-reach.h"
#include "sequencer.h"
+#include "packfile.h"
/**
* Parses the value of --rebase. If value is a false value, returns
* 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)
@@ -126,7 +126,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,
@@ -577,7 +577,7 @@ static int run_fetch(const char *repo, const char **refspecs)
strvec_pushv(&args, refspecs);
} else if (*refspecs)
BUG("refspecs without repo?");
- ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);
strvec_clear(&args);
return ret;
}
@@ -883,8 +883,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)
@@ -893,6 +891,8 @@ static int run_rebase(const struct object_id *newbase,
strvec_pushv(&args, opt_strategy_opts.v);
if (opt_gpg_sign)
strvec_push(&args, opt_gpg_sign);
+ if (opt_signoff)
+ strvec_push(&args, opt_signoff);
if (opt_autostash == 0)
strvec_push(&args, "--no-autostash");
else if (opt_autostash == 1)
@@ -911,12 +911,18 @@ static int run_rebase(const struct object_id *newbase,
return ret;
}
-static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_head)
+static int get_can_ff(struct object_id *orig_head,
+ struct oid_array *merge_heads)
{
int ret;
struct commit_list *list = NULL;
struct commit *merge_head, *head;
+ struct object_id *orig_merge_head;
+ if (merge_heads->nr > 1)
+ return 0;
+
+ orig_merge_head = &merge_heads->oid[0];
head = lookup_commit_reference(the_repository, orig_head);
commit_list_insert(head, &list);
merge_head = lookup_commit_reference(the_repository, orig_merge_head);
@@ -927,9 +933,9 @@ static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_
static void show_advice_pull_non_ff(void)
{
- advise(_("Pulling without specifying how to reconcile divergent branches is\n"
- "discouraged. You can squelch this message by running one of the following\n"
- "commands sometime before your next pull:\n"
+ 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 true # rebase\n"
@@ -966,8 +972,22 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
parse_repo_refspecs(argc, argv, &repo, &refspecs);
- if (!opt_ff)
+ if (!opt_ff) {
opt_ff = xstrdup_or_null(config_get_ff());
+ /*
+ * A subtle point: opt_ff was set on the line above via
+ * reading from config. opt_rebase, in contrast, is set
+ * before this point via command line options. The setting
+ * of opt_rebase via reading from config (using
+ * config_get_rebase()) does not happen until later. We
+ * are relying on the next if-condition happening before
+ * the config_get_rebase() call so that an explicit
+ * "--rebase" can override a config setting of
+ * pull.ff=only.
+ */
+ if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only"))
+ opt_ff = "--ff";
+ }
if (opt_rebase < 0)
opt_rebase = config_get_rebase(&rebase_unspecified);
@@ -1041,14 +1061,25 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die(_("Cannot merge multiple branches into empty head."));
return pull_into_void(merge_heads.oid, &curr_head);
}
- if (opt_rebase && merge_heads.nr > 1)
- die(_("Cannot rebase onto multiple branches."));
+ if (merge_heads.nr > 1) {
+ if (opt_rebase)
+ die(_("Cannot rebase onto multiple branches."));
+ if (opt_ff && !strcmp(opt_ff, "--ff-only"))
+ die(_("Cannot fast-forward to multiple branches."));
+ }
- can_ff = get_can_ff(&orig_head, &merge_heads.oid[0]);
+ can_ff = get_can_ff(&orig_head, &merge_heads);
- if (rebase_unspecified && !opt_ff && !can_ff) {
- if (opt_verbosity >= 0)
- show_advice_pull_non_ff();
+ /* ff-only takes precedence over rebase */
+ if (opt_ff && !strcmp(opt_ff, "--ff-only")) {
+ if (!can_ff)
+ 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) {
+ show_advice_pull_non_ff();
+ die(_("Need to specify how to reconcile divergent branches."));
}
if (opt_rebase) {
diff --git a/builtin/push.c b/builtin/push.c
index e8b10a9b7e..4b026ce6c6 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -289,42 +289,42 @@ static const char message_advice_ref_needs_update[] =
static void advise_pull_before_push(void)
{
- if (!advice_push_non_ff_current || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_NON_FF_CURRENT) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_pull_before_push));
}
static void advise_checkout_pull_push(void)
{
- if (!advice_push_non_ff_matching || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_NON_FF_MATCHING) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_checkout_pull_push));
}
static void advise_ref_already_exists(void)
{
- if (!advice_push_already_exists || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_ALREADY_EXISTS) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_ref_already_exists));
}
static void advise_ref_fetch_first(void)
{
- if (!advice_push_fetch_first || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_FETCH_FIRST) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_ref_fetch_first));
}
static void advise_ref_needs_force(void)
{
- if (!advice_push_needs_force || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_NEEDS_FORCE) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_ref_needs_force));
}
static void advise_ref_needs_update(void)
{
- if (!advice_push_ref_needs_update || !advice_push_update_rejected)
+ if (!advice_enabled(ADVICE_PUSH_REF_NEEDS_UPDATE) || !advice_enabled(ADVICE_PUSH_UPDATE_REJECTED))
return;
advise(_(message_advice_ref_needs_update));
}
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 485e7b0479..2109c4c9e5 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -38,7 +38,7 @@ static int list_tree(struct object_id *oid)
}
static const char * const read_tree_usage[] = {
- N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+ N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
NULL
};
@@ -53,24 +53,16 @@ static int index_output_cb(const struct option *opt, const char *arg,
static int exclude_per_directory_cb(const struct option *opt, const char *arg,
int unset)
{
- struct dir_struct *dir;
struct unpack_trees_options *opts;
BUG_ON_OPT_NEG(unset);
opts = (struct unpack_trees_options *)opt->value;
- if (opts->dir)
- die("more than one --exclude-per-directory given.");
-
- dir = xcalloc(1, sizeof(*opts->dir));
- dir->flags |= DIR_SHOW_IGNORED;
- dir->exclude_per_dir = arg;
- opts->dir = dir;
- /* We do not need to nor want to do read-directory
- * here; we are merely interested in reusing the
- * per directory ignore stack mechanism.
- */
+ if (!opts->update)
+ die("--exclude-per-directory is meaningless unless -u");
+ if (strcmp(arg, ".gitignore"))
+ die("--exclude-per-directory argument must be .gitignore");
return 0;
}
@@ -174,6 +166,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
+ if (opts.reset)
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+
/*
* NEEDSWORK
*
@@ -209,8 +204,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if ((opts.update || opts.index_only) && !opts.merge)
die("%s is meaningless without -m, --reset, or --prefix",
opts.update ? "-u" : "-i");
- if ((opts.dir && !opts.update))
- die("--exclude-per-directory is meaningless unless -u");
+ if (opts.update && !opts.reset)
+ opts.preserve_ignored = 0;
+ /* otherwise, opts.preserve_ignored is irrelevant */
if (opts.merge && !opts.index_only)
setup_work_tree();
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 12f093121d..34b4744e5f 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 {
@@ -139,7 +138,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.ignore_date = opts->ignore_date;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
if (opts->strategy)
- replay.strategy = opts->strategy;
+ replay.strategy = xstrdup_or_null(opts->strategy);
else if (!replay.strategy && replay.default_strategy) {
replay.strategy = replay.default_strategy;
replay.default_strategy = NULL;
@@ -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,8 +322,8 @@ 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;
switch (command) {
case ACTION_NONE: {
@@ -438,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);
}
@@ -477,102 +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);
-
- 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)
@@ -582,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 */
@@ -740,7 +547,6 @@ static int finish_rebase(struct rebase_options *opts)
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
unlink(git_path_auto_merge(the_repository));
apply_autostash(state_dir_path("autostash", opts));
- close_object_store(the_repository->objects);
/*
* We ignore errors in 'git maintenance run --auto', since the
* user should see them.
@@ -762,28 +568,6 @@ static int finish_rebase(struct rebase_options *opts)
return ret;
}
-static struct commit *peel_committish(const char *name)
-{
- struct object *obj;
- struct object_id oid;
-
- if (get_oid(name, &oid))
- return NULL;
- obj = parse_object(the_repository, &oid);
- return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-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;
@@ -940,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 */
@@ -960,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)
@@ -1058,8 +763,6 @@ finished_rebase:
die("Nothing to do");
}
- strbuf_release(&script_snippet);
-
return status ? -1 : 0;
}
@@ -1195,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)
{
@@ -1313,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"),
@@ -1377,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"),
@@ -1430,6 +1134,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
@@ -1448,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());
@@ -1470,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);
@@ -1479,10 +1188,6 @@ 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'"));
@@ -1574,7 +1279,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository, 0);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
@@ -1583,11 +1288,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
- ret = !!sequencer_remove_state(&replay);
+ ret = sequencer_remove_state(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
- ret = !!remove_dir_recursively(&buf, 0);
+ ret = remove_dir_recursively(&buf, 0);
if (ret)
error(_("could not remove '%s'"),
options.state_dir);
@@ -1702,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");
@@ -1713,7 +1418,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
int i;
if (!options.strategy)
- options.strategy = "recursive";
+ options.strategy = "ort";
strbuf_reset(&buf);
for (i = 0; i < strategy_options.nr; i++)
@@ -1728,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:
@@ -1780,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:
@@ -1805,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;
@@ -1845,7 +1530,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!strcmp(options.upstream_name, "-"))
options.upstream_name = "@{-1}";
}
- options.upstream = peel_committish(options.upstream_name);
+ options.upstream =
+ lookup_commit_reference_by_name(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
@@ -1888,7 +1574,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.onto = lookup_commit_or_die(&merge_base,
options.onto_name);
} else {
- options.onto = peel_committish(options.onto_name);
+ options.onto =
+ lookup_commit_reference_by_name(options.onto_name);
if (!options.onto)
die(_("Does not point to a valid commit '%s'"),
options.onto_name);
@@ -1913,13 +1600,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
- } else if (!get_oid(branch_name, &options.orig_head) &&
- lookup_commit_reference(the_repository,
- &options.orig_head))
+ } else {
+ struct commit *commit =
+ lookup_commit_reference_by_name(branch_name);
+ if (!commit)
+ die(_("no such branch/commit '%s'"),
+ branch_name);
+ oidcpy(&options.orig_head, &commit->object.oid);
options.head_name = NULL;
- else
- die(_("fatal: no such branch/commit '%s'"),
- branch_name);
+ }
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
@@ -1959,7 +1648,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (require_clean_work_tree(the_repository, "rebase",
_("Please commit or stash them."), 1, 1)) {
- ret = 1;
+ ret = -1;
goto cleanup;
}
@@ -1994,7 +1683,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, buf.buf,
DEFAULT_REFLOG_ACTION) < 0) {
- ret = !!error(_("could not switch to "
+ ret = error(_("could not switch to "
"%s"),
options.switch_to);
goto cleanup;
@@ -2009,7 +1698,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
else
printf(_("Current branch %s is up to date.\n"),
branch_name);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
} else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
@@ -2087,7 +1776,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
DEFAULT_REFLOG_ACTION);
strbuf_release(&msg);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
@@ -2101,7 +1790,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.revisions = revisions.buf;
run_rebase:
- ret = !!run_specific_rebase(&options, action);
+ ret = run_specific_rebase(&options, action);
cleanup:
strbuf_release(&buf);
@@ -2109,7 +1798,8 @@ cleanup:
free(options.head_name);
free(options.gpg_sign_opt);
free(options.cmd);
+ free(options.strategy);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
- return ret;
+ return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f17c7d2246..49b846d960 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -7,6 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
+#include "hook.h"
#include "exec-cmd.h"
#include "commit.h"
#include "object.h"
@@ -429,9 +430,6 @@ static int proc_receive_ref_matches(struct command *cmd)
return 0;
}
-static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
-
static void report_message(const char *prefix, const char *err, va_list params)
{
int sz;
@@ -449,6 +447,7 @@ static void report_message(const char *prefix, const char *err, va_list params)
xwrite(2, msg, sz);
}
+__attribute__((format (printf, 1, 2)))
static void rp_warning(const char *err, ...)
{
va_list params;
@@ -457,6 +456,7 @@ static void rp_warning(const char *err, ...)
va_end(params);
}
+__attribute__((format (printf, 1, 2)))
static void rp_error(const char *err, ...)
{
va_list params;
@@ -1311,7 +1311,7 @@ static void refuse_unconfigured_deny_delete_current(void)
rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
}
-static int command_singleton_iterator(void *cb_data, struct object_id *oid);
+static const struct object_id *command_singleton_iterator(void *cb_data);
static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT;
@@ -1468,7 +1468,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
- if (!find_hook(push_to_checkout_hook))
+ if (!hook_exists(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
else
retval = push_to_checkout(sha1, &env, work_tree);
@@ -1736,16 +1736,15 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
-static int command_singleton_iterator(void *cb_data, struct object_id *oid)
+static const struct object_id *command_singleton_iterator(void *cb_data)
{
struct command **cmd_list = cb_data;
struct command *cmd = *cmd_list;
if (!cmd || is_null_oid(&cmd->new_oid))
- return -1; /* end of list */
+ return NULL;
*cmd_list = NULL; /* this returns only one */
- oidcpy(oid, &cmd->new_oid);
- return 0;
+ return &cmd->new_oid;
}
static void set_connectivity_errors(struct command *commands,
@@ -1775,7 +1774,7 @@ struct iterate_data {
struct shallow_info *si;
};
-static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_receive_command_list(void *cb_data)
{
struct iterate_data *data = cb_data;
struct command **cmd_list = &data->cmds;
@@ -1786,13 +1785,11 @@ static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
/* to be checked in update_shallow_ref() */
continue;
if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
- oidcpy(oid, &cmd->new_oid);
*cmd_list = cmd->next;
- return 0;
+ return &cmd->new_oid;
}
}
- *cmd_list = NULL;
- return -1; /* end of list */
+ return NULL;
}
static void reject_updates_to_hidden(struct command *commands)
@@ -2482,7 +2479,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")),
OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
- OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL),
+ OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs, NULL),
+ OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
OPT_END()
};
@@ -2585,10 +2583,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
- proc.git_cmd = 1;
+ proc.git_cmd = proc.close_object_store = 1;
proc.argv = argv_gc_auto;
- close_object_store(the_repository->objects);
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 09541d1c80..bd4c669918 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -629,8 +629,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
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, &e->oid, flags,
+ status |= reflog_expire(e->reflog, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -642,13 +643,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (; i < argc; i++) {
char *ref;
- struct object_id oid;
- if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
+ if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -700,7 +700,6 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
for ( ; i < argc; i++) {
const char *spec = strstr(argv[i], "@{");
- struct object_id oid;
char *ep, *ref;
int recno;
@@ -709,7 +708,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
continue;
}
- if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
+ if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
status |= error(_("no reflog for '%s'"), argv[i]);
continue;
}
@@ -724,7 +723,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
cb.cmd.expire_total = 0;
}
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
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 5f9bc74adc..0b2d1e5d82 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);
}
@@ -208,10 +211,10 @@ static struct {
unsigned optional:1;
} exts[] = {
{".pack"},
- {".idx"},
{".rev", 1},
{".bitmap", 1},
{".promisor", 1},
+ {".idx"},
};
static unsigned populate_pack_exts(char *name)
@@ -422,6 +425,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 +455,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 = isatty(2);
/* variables to be filled by option parsing */
int pack_everything = 0;
@@ -454,6 +621,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 +664,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()
};
@@ -512,16 +682,32 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
die(_("--keep-unreachable and -A are incompatible"));
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) &&
+ git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0)) {
+ write_bitmaps = 0;
}
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps > 0;
- 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"));
@@ -561,19 +747,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);
@@ -582,15 +771,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_pushf(&cmd.args,
"--unpack-unreachable=%s",
unpack_unreachable);
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
} else if (pack_everything & LOOSEN_UNREACHABLE) {
strvec_push(&cmd.args,
"--unpack-unreachable");
} else if (keep_unreachable) {
strvec_push(&cmd.args, "--keep-unreachable");
strvec_push(&cmd.args, "--pack-loose-unreachable");
- } else {
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
}
} else if (geometry) {
@@ -676,20 +862,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) {
@@ -710,7 +924,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
}
- if (!po_args.quiet && isatty(2))
+ if (!po_args.quiet && show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -725,12 +939,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
update_server_info(0);
remove_temporary_files();
- if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
- write_midx_file(get_object_directory(), NULL, 0);
+ if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) {
+ 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, 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 cd48765911..946938d011 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -507,7 +507,7 @@ static int convert_graft_file(int force)
if (!fp)
return -1;
- advice_graft_file_deprecated = 0;
+ no_graft_file_deprecated_advice = 1;
while (strbuf_getline(&buf, fp) != EOF) {
if (*buf.buf == '#')
continue;
diff --git a/builtin/reset.c b/builtin/reset.c
index 43e855cb88..7393595349 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -67,12 +67,18 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
case KEEP:
case MERGE:
opts.update = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
break;
case HARD:
opts.update = 1;
- /* fallthrough */
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+ break;
+ case MIXED:
+ opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+ /* but opts.update=0, so working tree not updated */
+ break;
default:
- opts.reset = 1;
+ BUG("invalid reset_type passed to reset_index");
}
read_cache_unmerged();
@@ -412,7 +418,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
refresh_index(&the_index, flags, NULL, NULL,
_("Unstaged changes after reset:"));
t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
- if (advice_reset_quiet_warning && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
+ if (advice_enabled(ADVICE_RESET_QUIET_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
printf(_("\nIt took %.2f seconds to enumerate unstaged changes after reset. You can\n"
"use '--quiet' to avoid this. Set the config setting reset.quiet to true\n"
"to make this the default.\n"), t_delta_in_ms / 1000.0);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 7677b1af5a..36cb909eba 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -127,13 +127,15 @@ static void show_commit(struct commit *commit, void *data)
if (info->header_prefix)
fputs(info->header_prefix, stdout);
- if (!revs->graph)
- fputs(get_revision_mark(revs, commit), stdout);
- if (revs->abbrev_commit && revs->abbrev)
- fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
- stdout);
- else
- fputs(oid_to_hex(&commit->object.oid), stdout);
+ if (revs->include_header) {
+ if (!revs->graph)
+ fputs(get_revision_mark(revs, commit), stdout);
+ if (revs->abbrev_commit && revs->abbrev)
+ fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev),
+ stdout);
+ else
+ fputs(oid_to_hex(&commit->object.oid), stdout);
+ }
if (revs->print_parents) {
struct commit_list *parents = commit->parents;
while (parents) {
@@ -153,7 +155,7 @@ static void show_commit(struct commit *commit, void *data)
show_decorations(revs, commit);
if (revs->commit_format == CMIT_FMT_ONELINE)
putchar(' ');
- else
+ else if (revs->include_header)
putchar('\n');
if (revs->verbose_header) {
@@ -512,6 +514,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
repo_init_revisions(the_repository, &revs, prefix);
revs.abbrev = DEFAULT_ABBREV;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
+ revs.include_header = 1;
/*
* Scan the argument list before invoking setup_revisions(), so that we
@@ -627,6 +630,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
continue;
}
+ if (!strcmp(arg, ("--commit-header"))) {
+ revs.include_header = 1;
+ continue;
+ }
+
+ if (!strcmp(arg, ("--no-commit-header"))) {
+ revs.include_header = 0;
+ continue;
+ }
+
if (!strcmp(arg, "--disk-usage")) {
show_disk_usage = 1;
info.flags |= REV_LIST_QUIET;
@@ -636,10 +649,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
usage(rev_list_usage);
}
+ if (revs.commit_format != CMIT_FMT_USERFORMAT)
+ revs.include_header = 1;
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
/* The command line has a --pretty */
info.hdr_termination = '\n';
- if (revs.commit_format == CMIT_FMT_ONELINE)
+ if (revs.commit_format == CMIT_FMT_ONELINE || !revs.include_header)
info.header_prefix = "";
else
info.header_prefix = "commit ";
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 22c4e1a4ff..8480a59f57 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
- for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
+ for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
+ for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
diff --git a/builtin/revert.c b/builtin/revert.c
index 237f2f18d4..51776abea6 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -136,6 +136,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
/* implies allow_empty */
if (opts->keep_redundant_commits)
opts->allow_empty = 1;
@@ -191,7 +194,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
struct setup_revision_opt s_r_opt;
opts->revs = xmalloc(sizeof(*opts->revs));
repo_init_revisions(the_repository, opts->revs, NULL);
- opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+ opts->revs->no_walk = 1;
+ opts->revs->unsorted_input = 1;
if (argc < 2)
usage_with_options(usage_str, options);
if (!strcmp(argv[1], "-"))
diff --git a/builtin/rm.c b/builtin/rm.c
index 8a24c715e0..3d0967cdc1 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -55,7 +55,7 @@ static void print_error_files(struct string_list *files_list,
strbuf_addf(&err_msg,
"\n %s",
files_list->items[i].string);
- if (advice_rm_hints)
+ if (advice_enabled(ADVICE_RM_HINTS))
strbuf_addstr(&err_msg, hints_msg);
*errs = error("%s", err_msg.buf);
strbuf_release(&err_msg);
@@ -237,6 +237,7 @@ static int check_local_mod(struct object_id *head, int index_only)
static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
static int ignore_unmatch = 0, pathspec_file_nul;
+static int include_sparse;
static char *pathspec_from_file;
static struct option builtin_rm_options[] = {
@@ -247,6 +248,7 @@ static struct option builtin_rm_options[] = {
OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")),
OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
N_("exit with a zero status even if nothing matched")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
@@ -298,7 +300,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
ensure_full_index(&the_index);
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (!ce_path_match(&the_index, ce, &pathspec, seen))
continue;
@@ -322,7 +327,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
seen_any = 1;
else if (ignore_unmatch)
continue;
- else if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
+ else if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
string_list_append(&only_match_skip_worktree, original);
else
die(_("pathspec '%s' did not match any files"), original);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index a7e01667b0..8932142312 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -17,10 +17,10 @@
#include "protocol.h"
static const char * const send_pack_usage[] = {
- N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
- "[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] "
- "[<host>:]<directory> [<ref>...]\n"
- " --all and explicit <ref> specification are mutually exclusive."),
+ N_("git send-pack [--mirror] [--dry-run] [--force]\n"
+ " [--receive-pack=<git-receive-pack>]\n"
+ " [--verbose] [--thin] [--atomic]\n"
+ " [<host>:]<directory> (--all | <ref>...)"),
NULL,
};
@@ -230,6 +230,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.atomic = atomic;
args.stateless_rpc = stateless_rpc;
args.push_options = push_options.nr ? &push_options : NULL;
+ args.url = dest;
if (from_stdin) {
if (args.stateless_rpc) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index d77ce7aeb3..082449293b 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -11,9 +11,9 @@
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
- " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
- " [--more=<n> | --list | --independent | --merge-base]\n"
- " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+ " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+ " [--more=<n> | --list | --independent | --merge-base]\n"
+ " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
NULL
};
@@ -482,10 +482,9 @@ static void snarf_refs(int head, int remotes)
}
}
-static int rev_is_head(const char *head, const char *name,
- unsigned char *head_sha1, unsigned char *sha1)
+static int rev_is_head(const char *head, const char *name)
{
- if (!head || (head_sha1 && sha1 && !hasheq(head_sha1, sha1)))
+ if (!head)
return 0;
skip_prefix(head, "refs/heads/", &head);
if (!skip_prefix(name, "refs/heads/", &name))
@@ -806,9 +805,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
/* We are only interested in adding the branch
* HEAD points at.
*/
- if (rev_is_head(head,
- ref_name[i],
- head_oid.hash, NULL))
+ if (rev_is_head(head, ref_name[i]))
has_head++;
}
if (!has_head) {
@@ -867,10 +864,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (1 < num_rev || extra < 0) {
for (i = 0; i < num_rev; i++) {
int j;
- int is_head = rev_is_head(head,
- ref_name[i],
- head_oid.hash,
- rev[i]->object.oid.hash);
+ int is_head = rev_is_head(head, ref_name[i]) &&
+ oideq(&head_oid, &rev[i]->object.oid);
if (extra < 0)
printf("%c [%s] ",
is_head ? '*' : ' ', ref_name[i]);
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index a4bdd7c494..d0f5c4702b 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -100,6 +100,98 @@ static int sparse_checkout_list(int argc, const char **argv)
return 0;
}
+static void clean_tracked_sparse_directories(struct repository *r)
+{
+ int i, was_full = 0;
+ struct strbuf path = STRBUF_INIT;
+ size_t pathlen;
+ struct string_list_item *item;
+ struct string_list sparse_dirs = STRING_LIST_INIT_DUP;
+
+ /*
+ * If we are not using cone mode patterns, then we cannot
+ * delete directories outside of the sparse cone.
+ */
+ if (!r || !r->index || !r->worktree)
+ return;
+ if (init_sparse_checkout_patterns(r->index) ||
+ !r->index->sparse_checkout_patterns->use_cone_patterns)
+ return;
+
+ /*
+ * Use the sparse index as a data structure to assist finding
+ * directories that are safe to delete. This conversion to a
+ * sparse index will not delete directories that contain
+ * conflicted entries or submodules.
+ */
+ if (!r->index->sparse_index) {
+ /*
+ * If something, such as a merge conflict or other concern,
+ * prevents us from converting to a sparse index, then do
+ * not try deleting files.
+ */
+ if (convert_to_sparse(r->index, SPARSE_INDEX_MEMORY_ONLY))
+ return;
+ was_full = 1;
+ }
+
+ strbuf_addstr(&path, r->worktree);
+ strbuf_complete(&path, '/');
+ pathlen = path.len;
+
+ /*
+ * Collect directories that have gone out of scope but also
+ * exist on disk, so there is some work to be done. We need to
+ * store the entries in a list before exploring, since that might
+ * expand the sparse-index again.
+ */
+ for (i = 0; i < r->index->cache_nr; i++) {
+ struct cache_entry *ce = r->index->cache[i];
+
+ if (S_ISSPARSEDIR(ce->ce_mode) &&
+ repo_file_exists(r, ce->name))
+ string_list_append(&sparse_dirs, ce->name);
+ }
+
+ for_each_string_list_item(item, &sparse_dirs) {
+ struct dir_struct dir = DIR_INIT;
+ struct pathspec p = { 0 };
+ struct strvec s = STRVEC_INIT;
+
+ strbuf_setlen(&path, pathlen);
+ strbuf_addstr(&path, item->string);
+
+ dir.flags |= DIR_SHOW_IGNORED_TOO;
+
+ setup_standard_excludes(&dir);
+ strvec_push(&s, path.buf);
+
+ parse_pathspec(&p, PATHSPEC_GLOB, 0, NULL, s.v);
+ fill_directory(&dir, r->index, &p);
+
+ if (dir.nr) {
+ warning(_("directory '%s' contains untracked files,"
+ " but is not in the sparse-checkout cone"),
+ item->string);
+ } else if (remove_dir_recursively(&path, 0)) {
+ /*
+ * Removal is "best effort". If something blocks
+ * the deletion, then continue with a warning.
+ */
+ warning(_("failed to remove directory '%s'"),
+ item->string);
+ }
+
+ dir_clear(&dir);
+ }
+
+ string_list_clear(&sparse_dirs, 0);
+ strbuf_release(&path);
+
+ if (was_full)
+ ensure_full_index(r->index);
+}
+
static int update_working_directory(struct pattern_list *pl)
{
enum update_sparsity_result result;
@@ -141,6 +233,8 @@ static int update_working_directory(struct pattern_list *pl)
else
rollback_lock_file(&lock_file);
+ clean_tracked_sparse_directories(r);
+
r->index->sparse_checkout_patterns = NULL;
return result;
}
@@ -380,10 +474,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
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_entry_init(&e->ent, fspathhash(e->pattern));
hashmap_add(&pl->recursive_hashmap, &e->ent);
@@ -399,10 +490,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
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));
+ hashmap_entry_init(&e->ent, fspathhash(e->pattern));
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
hashmap_add(&pl->parent_hashmap, &e->ent);
diff --git a/builtin/stash.c b/builtin/stash.c
index 8f42360ca9..a0ccc8654d 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -85,7 +85,7 @@ static const char * const git_stash_push_usage[] = {
static const char * const git_stash_save_usage[] = {
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [<message>]"),
+ " [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -256,8 +256,10 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.merge = 1;
- opts.reset = reset;
+ opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.update = update;
+ if (update)
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = oneway_merge;
if (unpack_trees(nr_trees, t, &opts))
@@ -313,6 +315,17 @@ static int reset_head(void)
return run_command(&cp);
}
+static int is_path_a_directory(const char *path)
+{
+ /*
+ * This function differs from abspath.c:is_directory() in that
+ * here we use lstat() instead of stat(); we do not want to
+ * follow symbolic links here.
+ */
+ struct stat st;
+ return (!lstat(path, &st) && S_ISDIR(st.st_mode));
+}
+
static void add_diff_to_buf(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
@@ -320,6 +333,9 @@ static void add_diff_to_buf(struct diff_queue_struct *q,
int i;
for (i = 0; i < q->nr; i++) {
+ if (is_path_a_directory(q->queue[i]->one->path))
+ continue;
+
strbuf_addstr(data, q->queue[i]->one->path);
/* NUL-terminate: will be fed to update-index -z */
@@ -521,9 +537,6 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
}
}
- if (info->has_u && restore_untracked(&info->u_tree))
- return error(_("could not restore untracked files from stash"));
-
init_merge_options(&o, the_repository);
o.branch1 = "Updated upstream";
@@ -558,6 +571,9 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
unstage_changes_unless_new(&c_tree);
}
+ if (info->has_u && restore_untracked(&info->u_tree))
+ return error(_("could not restore untracked files from stash"));
+
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1519,6 +1535,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
} else {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ /* BUG: this nukes untracked files in the way */
strvec_pushl(&cp.args, "reset", "--hard", "-q",
"--no-recurse-submodules", NULL);
if (run_command(&cp)) {
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f73963ad67..6298cbdd4e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -187,44 +187,40 @@ static char *relative_url(const char *remote_url,
out = xstrdup(sb.buf + 2);
else
out = xstrdup(sb.buf);
- strbuf_reset(&sb);
- if (!up_path || !is_relative)
+ if (!up_path || !is_relative) {
+ strbuf_release(&sb);
return out;
+ }
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s", up_path, out);
free(out);
return strbuf_detach(&sb, NULL);
}
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
{
- char *remoteurl = NULL;
+ char *remoteurl, *resolved_url;
char *remote = get_default_remote();
- const char *up_path = NULL;
- char *res;
- const char *url;
- struct strbuf sb = STRBUF_INIT;
-
- if (argc != 2 && argc != 3)
- die("resolve-relative-url only accepts one or two arguments");
-
- url = argv[1];
- strbuf_addf(&sb, "remote.%s.url", remote);
- free(remote);
+ struct strbuf remotesb = STRBUF_INIT;
- if (git_config_get_string(sb.buf, &remoteurl))
- /* the repository is its own authoritative upstream */
+ strbuf_addf(&remotesb, "remote.%s.url", remote);
+ if (git_config_get_string(remotesb.buf, &remoteurl)) {
+ if (!quiet)
+ warning(_("could not look up configuration '%s'. "
+ "Assuming this repository is its own "
+ "authoritative upstream."),
+ remotesb.buf);
remoteurl = xgetcwd();
+ }
+ resolved_url = relative_url(remoteurl, rel_url, up_path);
- if (argc == 3)
- up_path = argv[2];
-
- res = relative_url(remoteurl, url, up_path);
- puts(res);
- free(res);
+ free(remote);
free(remoteurl);
- return 0;
+ strbuf_release(&remotesb);
+
+ return resolved_url;
}
static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
@@ -311,7 +307,7 @@ struct module_list {
const struct cache_entry **entries;
int alloc, nr;
};
-#define MODULE_LIST_INIT { NULL, 0, 0 }
+#define MODULE_LIST_INIT { 0 }
static int module_list_compute(int argc, const char **argv,
const char *prefix,
@@ -588,31 +584,11 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
return 0;
}
-static char *compute_submodule_clone_url(const char *rel_url)
-{
- char *remoteurl, *relurl;
- char *remote = get_default_remote();
- struct strbuf remotesb = STRBUF_INIT;
-
- strbuf_addf(&remotesb, "remote.%s.url", remote);
- if (git_config_get_string(remotesb.buf, &remoteurl)) {
- warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
- remoteurl = xgetcwd();
- }
- relurl = relative_url(remoteurl, rel_url, NULL);
-
- free(remote);
- free(remoteurl);
- strbuf_release(&remotesb);
-
- return relurl;
-}
-
struct init_cb {
const char *prefix;
unsigned int flags;
};
-#define INIT_CB_INIT { NULL, 0 }
+#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -658,7 +634,7 @@ static void init_submodule(const char *path, const char *prefix,
if (starts_with_dot_dot_slash(url) ||
starts_with_dot_slash(url)) {
char *oldurl = url;
- url = compute_submodule_clone_url(oldurl);
+ url = resolve_relative_url(oldurl, NULL, 0);
free(oldurl);
}
@@ -741,7 +717,7 @@ struct status_cb {
const char *prefix;
unsigned int flags;
};
-#define STATUS_CB_INIT { NULL, 0 }
+#define STATUS_CB_INIT { 0 }
static void print_status(unsigned int flags, char state, const char *path,
const struct object_id *oid, const char *displaypath)
@@ -935,13 +911,13 @@ struct module_cb {
char status;
const char *sm_path;
};
-#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+#define MODULE_CB_INIT { 0 }
struct module_cb_list {
struct module_cb **entries;
int alloc, nr;
};
-#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+#define MODULE_CB_LIST_INIT { 0 }
struct summary_cb {
int argc;
@@ -952,7 +928,7 @@ struct summary_cb {
unsigned int files: 1;
int summary_limit;
};
-#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+#define SUMMARY_CB_INIT { 0 }
enum diff_cmd {
DIFF_INDEX,
@@ -1358,7 +1334,7 @@ struct sync_cb {
const char *prefix;
unsigned int flags;
};
-#define SYNC_CB_INIT { NULL, 0 }
+#define SYNC_CB_INIT { 0 }
static void sync_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1378,20 +1354,10 @@ static void sync_submodule(const char *path, const char *prefix,
if (sub && sub->url) {
if (starts_with_dot_dot_slash(sub->url) ||
starts_with_dot_slash(sub->url)) {
- char *remote_url, *up_path;
- char *remote = get_default_remote();
- strbuf_addf(&sb, "remote.%s.url", remote);
-
- if (git_config_get_string(sb.buf, &remote_url))
- remote_url = xgetcwd();
-
- up_path = get_up_path(path);
- sub_origin_url = relative_url(remote_url, sub->url, up_path);
- super_config_url = relative_url(remote_url, sub->url, NULL);
-
- free(remote);
+ char *up_path = get_up_path(path);
+ sub_origin_url = resolve_relative_url(sub->url, up_path, 1);
+ super_config_url = resolve_relative_url(sub->url, NULL, 1);
free(up_path);
- free(remote_url);
} else {
sub_origin_url = xstrdup(sub->url);
super_config_url = xstrdup(sub->url);
@@ -1514,7 +1480,7 @@ struct deinit_cb {
const char *prefix;
unsigned int flags;
};
-#define DEINIT_CB_INIT { NULL, 0 }
+#define DEINIT_CB_INIT { 0 }
static void deinit_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1657,45 +1623,20 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
return 0;
}
-static int clone_submodule(const char *path, const char *gitdir, const char *url,
- const char *depth, struct string_list *reference, int dissociate,
- int quiet, int progress, int single_branch)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
-
- strvec_push(&cp.args, "clone");
- strvec_push(&cp.args, "--no-checkout");
- if (quiet)
- strvec_push(&cp.args, "--quiet");
- if (progress)
- strvec_push(&cp.args, "--progress");
- if (depth && *depth)
- strvec_pushl(&cp.args, "--depth", depth, NULL);
- if (reference->nr) {
- struct string_list_item *item;
- for_each_string_list_item(item, reference)
- strvec_pushl(&cp.args, "--reference",
- item->string, NULL);
- }
- if (dissociate)
- strvec_push(&cp.args, "--dissociate");
- if (gitdir && *gitdir)
- strvec_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
- if (single_branch >= 0)
- strvec_push(&cp.args, single_branch ?
- "--single-branch" :
- "--no-single-branch");
-
- strvec_push(&cp.args, "--");
- strvec_push(&cp.args, url);
- strvec_push(&cp.args, path);
-
- cp.git_cmd = 1;
- prepare_submodule_repo_env(&cp.env_array);
- cp.no_stdin = 1;
-
- return run_command(&cp);
-}
+struct module_clone_data {
+ const char *prefix;
+ const char *path;
+ const char *name;
+ const char *url;
+ const char *depth;
+ struct string_list reference;
+ unsigned int quiet: 1;
+ unsigned int progress: 1;
+ unsigned int dissociate: 1;
+ unsigned int require_init: 1;
+ int single_branch;
+};
+#define MODULE_CLONE_DATA_INIT { .reference = STRING_LIST_INIT_NODUP, .single_branch = -1 }
struct submodule_alternate_setup {
const char *submodule_name;
@@ -1706,8 +1647,9 @@ struct submodule_alternate_setup {
} error_mode;
struct string_list *reference;
};
-#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
- SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+#define SUBMODULE_ALTERNATE_SETUP_INIT { \
+ .error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE, \
+}
static const char alternate_error_advice[] = N_(
"An alternate computed from a superproject's alternate is invalid.\n"
@@ -1727,18 +1669,24 @@ static int add_possible_reference_from_superproject(
* standard layout with .git/(modules/<name>)+/objects
*/
if (strip_suffix(odb->path, "/objects", &len)) {
+ struct repository alternate;
char *sm_alternate;
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
strbuf_add(&sb, odb->path, len);
+ repo_init(&alternate, sb.buf, NULL);
+
/*
* We need to end the new path with '/' to mark it as a dir,
* otherwise a submodule name containing '/' will be broken
* as the last part of a missing submodule reference would
* be taken as a file name.
*/
- strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
+ strbuf_reset(&sb);
+ submodule_name_to_gitdir(&sb, &alternate, sas->submodule_name);
+ strbuf_addch(&sb, '/');
+ repo_clear(&alternate);
sm_alternate = compute_alternate_path(sb.buf, &err);
if (sm_alternate) {
@@ -1747,7 +1695,7 @@ static int add_possible_reference_from_superproject(
} else {
switch (sas->error_mode) {
case SUBMODULE_ALTERNATE_ERROR_DIE:
- if (advice_submodule_alternate_error_strategy_die)
+ if (advice_enabled(ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE))
advise(_(alternate_error_advice));
die(_("submodule '%s' cannot add alternate: %s"),
sas->submodule_name, err.buf);
@@ -1801,37 +1749,128 @@ static void prepare_possible_alternates(const char *sm_name,
free(error_strategy);
}
-static int module_clone(int argc, const char **argv, const char *prefix)
+static int clone_submodule(struct module_clone_data *clone_data)
{
- const char *name = NULL, *url = NULL, *depth = NULL;
- int quiet = 0;
- int progress = 0;
- char *p, *path = NULL, *sm_gitdir;
- struct strbuf sb = STRBUF_INIT;
- struct string_list reference = STRING_LIST_INIT_NODUP;
- int dissociate = 0, require_init = 0;
+ char *p, *sm_gitdir;
char *sm_alternate = NULL, *error_strategy = NULL;
- int single_branch = -1;
+ struct strbuf sb = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
+ sm_gitdir = absolute_pathdup(sb.buf);
+ strbuf_reset(&sb);
+
+ if (!is_absolute_path(clone_data->path)) {
+ strbuf_addf(&sb, "%s/%s", get_git_work_tree(), clone_data->path);
+ clone_data->path = strbuf_detach(&sb, NULL);
+ } else {
+ clone_data->path = xstrdup(clone_data->path);
+ }
+
+ if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0)
+ die(_("refusing to create/use '%s' in another submodule's "
+ "git dir"), sm_gitdir);
+
+ if (!file_exists(sm_gitdir)) {
+ if (safe_create_leading_directories_const(sm_gitdir) < 0)
+ die(_("could not create directory '%s'"), sm_gitdir);
+
+ prepare_possible_alternates(clone_data->name, &clone_data->reference);
+
+ strvec_push(&cp.args, "clone");
+ strvec_push(&cp.args, "--no-checkout");
+ if (clone_data->quiet)
+ strvec_push(&cp.args, "--quiet");
+ if (clone_data->progress)
+ strvec_push(&cp.args, "--progress");
+ if (clone_data->depth && *(clone_data->depth))
+ strvec_pushl(&cp.args, "--depth", clone_data->depth, NULL);
+ if (clone_data->reference.nr) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, &clone_data->reference)
+ strvec_pushl(&cp.args, "--reference",
+ item->string, NULL);
+ }
+ if (clone_data->dissociate)
+ strvec_push(&cp.args, "--dissociate");
+ if (sm_gitdir && *sm_gitdir)
+ strvec_pushl(&cp.args, "--separate-git-dir", sm_gitdir, NULL);
+ if (clone_data->single_branch >= 0)
+ strvec_push(&cp.args, clone_data->single_branch ?
+ "--single-branch" :
+ "--no-single-branch");
+
+ strvec_push(&cp.args, "--");
+ strvec_push(&cp.args, clone_data->url);
+ strvec_push(&cp.args, clone_data->path);
+
+ cp.git_cmd = 1;
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.no_stdin = 1;
+
+ if(run_command(&cp))
+ die(_("clone of '%s' into submodule path '%s' failed"),
+ clone_data->url, clone_data->path);
+ } else {
+ if (clone_data->require_init && !access(clone_data->path, X_OK) &&
+ !is_empty_dir(clone_data->path))
+ die(_("directory not empty: '%s'"), clone_data->path);
+ if (safe_create_leading_directories_const(clone_data->path) < 0)
+ die(_("could not create directory '%s'"), clone_data->path);
+ strbuf_addf(&sb, "%s/index", sm_gitdir);
+ unlink_or_warn(sb.buf);
+ strbuf_reset(&sb);
+ }
+
+ connect_work_tree_and_git_dir(clone_data->path, sm_gitdir, 0);
+
+ p = git_pathdup_submodule(clone_data->path, "config");
+ if (!p)
+ die(_("could not get submodule directory for '%s'"), clone_data->path);
+
+ /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
+ git_config_get_string("submodule.alternateLocation", &sm_alternate);
+ if (sm_alternate)
+ git_config_set_in_file(p, "submodule.alternateLocation",
+ sm_alternate);
+ git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+ if (error_strategy)
+ git_config_set_in_file(p, "submodule.alternateErrorStrategy",
+ error_strategy);
+
+ free(sm_alternate);
+ free(error_strategy);
+
+ strbuf_release(&sb);
+ free(sm_gitdir);
+ free(p);
+ return 0;
+}
+
+static int module_clone(int argc, const char **argv, const char *prefix)
+{
+ int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
+ struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
struct option module_clone_options[] = {
- OPT_STRING(0, "prefix", &prefix,
+ OPT_STRING(0, "prefix", &clone_data.prefix,
N_("path"),
N_("alternative anchor for relative paths")),
- OPT_STRING(0, "path", &path,
+ OPT_STRING(0, "path", &clone_data.path,
N_("path"),
N_("where the new submodule will be cloned to")),
- OPT_STRING(0, "name", &name,
+ OPT_STRING(0, "name", &clone_data.name,
N_("string"),
N_("name of the new submodule")),
- OPT_STRING(0, "url", &url,
+ OPT_STRING(0, "url", &clone_data.url,
N_("string"),
N_("url where to clone the submodule from")),
- OPT_STRING_LIST(0, "reference", &reference,
+ OPT_STRING_LIST(0, "reference", &clone_data.reference,
N_("repo"),
N_("reference repository")),
OPT_BOOL(0, "dissociate", &dissociate,
N_("use --reference only while cloning")),
- OPT_STRING(0, "depth", &depth,
+ OPT_STRING(0, "depth", &clone_data.depth,
N_("string"),
N_("depth for shallow clones")),
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
@@ -1839,7 +1878,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &require_init,
N_("disallow cloning into non-empty directory")),
- OPT_BOOL(0, "single-branch", &single_branch,
+ OPT_BOOL(0, "single-branch", &clone_data.single_branch,
N_("clone only one branch, HEAD or --branch")),
OPT_END()
};
@@ -1855,67 +1894,16 @@ static int module_clone(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
- if (argc || !url || !path || !*path)
+ clone_data.dissociate = !!dissociate;
+ clone_data.quiet = !!quiet;
+ clone_data.progress = !!progress;
+ clone_data.require_init = !!require_init;
+
+ if (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))
usage_with_options(git_submodule_helper_usage,
module_clone_options);
- strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
- sm_gitdir = absolute_pathdup(sb.buf);
- strbuf_reset(&sb);
-
- if (!is_absolute_path(path)) {
- strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path);
- path = strbuf_detach(&sb, NULL);
- } else
- path = xstrdup(path);
-
- if (validate_submodule_git_dir(sm_gitdir, name) < 0)
- die(_("refusing to create/use '%s' in another submodule's "
- "git dir"), sm_gitdir);
-
- if (!file_exists(sm_gitdir)) {
- if (safe_create_leading_directories_const(sm_gitdir) < 0)
- die(_("could not create directory '%s'"), sm_gitdir);
-
- prepare_possible_alternates(name, &reference);
-
- if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
- quiet, progress, single_branch))
- die(_("clone of '%s' into submodule path '%s' failed"),
- url, path);
- } else {
- if (require_init && !access(path, X_OK) && !is_empty_dir(path))
- die(_("directory not empty: '%s'"), path);
- if (safe_create_leading_directories_const(path) < 0)
- die(_("could not create directory '%s'"), path);
- strbuf_addf(&sb, "%s/index", sm_gitdir);
- unlink_or_warn(sb.buf);
- strbuf_reset(&sb);
- }
-
- connect_work_tree_and_git_dir(path, sm_gitdir, 0);
-
- p = git_pathdup_submodule(path, "config");
- if (!p)
- die(_("could not get submodule directory for '%s'"), path);
-
- /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
- git_config_get_string("submodule.alternateLocation", &sm_alternate);
- if (sm_alternate)
- git_config_set_in_file(p, "submodule.alternateLocation",
- sm_alternate);
- git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
- if (error_strategy)
- git_config_set_in_file(p, "submodule.alternateErrorStrategy",
- error_strategy);
-
- free(sm_alternate);
- free(error_strategy);
-
- strbuf_release(&sb);
- free(sm_gitdir);
- free(path);
- free(p);
+ clone_submodule(&clone_data);
return 0;
}
@@ -2028,6 +2016,20 @@ struct submodule_update_clone {
.max_jobs = 1, \
}
+struct update_data {
+ const char *recursive_prefix;
+ const char *sm_path;
+ const char *displaypath;
+ struct object_id oid;
+ struct object_id suboid;
+ struct submodule_update_strategy update_strategy;
+ int depth;
+ unsigned int force: 1;
+ unsigned int quiet: 1;
+ unsigned int nofetch: 1;
+ unsigned int just_cloned: 1;
+};
+#define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
struct strbuf *out, const char *displaypath)
@@ -2117,7 +2119,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
if (starts_with_dot_slash(sub->url) ||
starts_with_dot_dot_slash(sub->url)) {
- url = compute_submodule_clone_url(sub->url);
+ url = resolve_relative_url(sub->url, NULL, 0);
need_free_url = 1;
} else
url = sub->url;
@@ -2281,6 +2283,181 @@ static int git_update_clone_config(const char *var, const char *value,
return 0;
}
+static int is_tip_reachable(const char *path, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf rev = STRBUF_INIT;
+ char *hex = oid_to_hex(oid);
+
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(path);
+ cp.no_stderr = 1;
+ strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
+
+ prepare_submodule_repo_env(&cp.env_array);
+
+ if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
+ return 0;
+
+ return 1;
+}
+
+static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(module_path);
+
+ strvec_push(&cp.args, "fetch");
+ if (quiet)
+ strvec_push(&cp.args, "--quiet");
+ if (depth)
+ strvec_pushf(&cp.args, "--depth=%d", depth);
+ if (oid) {
+ char *hex = oid_to_hex(oid);
+ char *remote = get_default_remote();
+ strvec_pushl(&cp.args, remote, hex, NULL);
+ }
+
+ return run_command(&cp);
+}
+
+static int run_update_command(struct update_data *ud, int subforce)
+{
+ struct strvec args = STRVEC_INIT;
+ struct strvec child_env = STRVEC_INIT;
+ char *oid = oid_to_hex(&ud->oid);
+ int must_die_on_failure = 0;
+ int git_cmd;
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ git_cmd = 1;
+ strvec_pushl(&args, "checkout", "-q", NULL);
+ if (subforce)
+ strvec_push(&args, "-f");
+ break;
+ case SM_UPDATE_REBASE:
+ git_cmd = 1;
+ strvec_push(&args, "rebase");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_MERGE:
+ git_cmd = 1;
+ strvec_push(&args, "merge");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_COMMAND:
+ git_cmd = 0;
+ strvec_push(&args, ud->update_strategy.command);
+ must_die_on_failure = 1;
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ strvec_push(&args, oid);
+
+ prepare_submodule_repo_env(&child_env);
+ if (run_command_v_opt_cd_env(args.v, git_cmd ? RUN_GIT_CMD : RUN_USING_SHELL,
+ ud->sm_path, child_env.v)) {
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Unable to checkout '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Unable to rebase '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Unable to merge '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Execution of '%s %s' failed in submodule path '%s'"),
+ ud->update_strategy.command, oid, ud->displaypath);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ /*
+ * NEEDSWORK: We are currently printing to stdout with error
+ * return so that the shell caller handles the error output
+ * properly. Once we start handling the error messages within
+ * C, we should use die() instead.
+ */
+ if (must_die_on_failure)
+ return 2;
+ /*
+ * This signifies to the caller in shell that the command
+ * failed without dying
+ */
+ return 1;
+ }
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Submodule path '%s': checked out '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Submodule path '%s': rebased into '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Submodule path '%s': merged in '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Submodule path '%s': '%s %s'\n"),
+ ud->displaypath, ud->update_strategy.command, oid);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+
+ return 0;
+}
+
+static int do_run_update_procedure(struct update_data *ud)
+{
+ int subforce = is_null_oid(&ud->suboid) || ud->force;
+
+ if (!ud->nofetch) {
+ /*
+ * Run fetch only if `oid` isn't present or it
+ * is not reachable from a ref.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, NULL) &&
+ !ud->quiet)
+ fprintf_ln(stderr,
+ _("Unable to fetch in submodule path '%s'; "
+ "trying to directly fetch %s:"),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ /*
+ * Now we tried the usual fetch, but `oid` may
+ * not be reachable from any of the refs.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid))
+ die(_("Fetched in submodule path '%s', but it did not "
+ "contain %s. Direct fetching of that commit failed."),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ }
+
+ return run_update_command(ud, subforce);
+}
+
static void update_submodule(struct update_clone_data *ucd)
{
fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2378,6 +2555,73 @@ static int update_clone(int argc, const char **argv, const char *prefix)
return update_submodules(&suc);
}
+static int run_update_procedure(int argc, const char **argv, const char *prefix)
+{
+ int force = 0, quiet = 0, nofetch = 0, just_cloned = 0;
+ char *prefixed_path, *update = NULL;
+ struct update_data update_data = UPDATE_DATA_INIT;
+
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("suppress output for update by rebase or merge")),
+ OPT__FORCE(&force, N_("force checkout updates"), 0),
+ OPT_BOOL('N', "no-fetch", &nofetch,
+ N_("don't fetch new objects from the remote site")),
+ OPT_BOOL(0, "just-cloned", &just_cloned,
+ N_("overrides update mode in case the repository is a fresh clone")),
+ OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "update", &update,
+ N_("string"),
+ N_("rebase, merge, checkout or none")),
+ OPT_STRING(0, "recursive-prefix", &update_data.recursive_prefix, N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
+ N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_CALLBACK_F(0, "suboid", &update_data.suboid, N_("subsha1"),
+ N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_END()
+ };
+
+ const char *const usage[] = {
+ N_("git submodule--helper run-update-procedure [<options>] <path>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (argc != 1)
+ usage_with_options(usage, options);
+
+ update_data.force = !!force;
+ update_data.quiet = !!quiet;
+ update_data.nofetch = !!nofetch;
+ update_data.just_cloned = !!just_cloned;
+ update_data.sm_path = argv[0];
+
+ if (update_data.recursive_prefix)
+ prefixed_path = xstrfmt("%s%s", update_data.recursive_prefix, update_data.sm_path);
+ else
+ prefixed_path = xstrdup(update_data.sm_path);
+
+ update_data.displaypath = get_submodule_displaypath(prefixed_path, prefix);
+
+ determine_submodule_update_strategy(the_repository, update_data.just_cloned,
+ update_data.sm_path, update,
+ &update_data.update_strategy);
+
+ free(prefixed_path);
+
+ if (!oideq(&update_data.oid, &update_data.suboid) || update_data.force)
+ return do_run_update_procedure(&update_data);
+
+ return 3;
+}
+
static int resolve_relative_path(int argc, const char **argv, const char *prefix)
{
struct strbuf sb = STRBUF_INIT;
@@ -2523,7 +2767,6 @@ static int push_check(int argc, const char **argv, const char *prefix)
static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
{
- const struct submodule *sub;
const char *path;
const char *cw;
struct repository subrepo;
@@ -2533,11 +2776,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
path = argv[1];
- sub = submodule_from_path(the_repository, null_oid(), path);
- if (!sub)
- BUG("We could get the submodule handle before?");
-
- if (repo_submodule_init(&subrepo, the_repository, sub))
+ if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
die(_("could not get a repository handle for submodule '%s'"), path);
if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) {
@@ -2744,6 +2983,368 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
return !!ret;
}
+struct add_data {
+ const char *prefix;
+ const char *branch;
+ const char *reference_path;
+ char *sm_path;
+ const char *sm_name;
+ const char *repo;
+ const char *realrepo;
+ int depth;
+ unsigned int force: 1;
+ unsigned int quiet: 1;
+ unsigned int progress: 1;
+ unsigned int dissociate: 1;
+};
+#define ADD_DATA_INIT { .depth = -1 }
+
+static void show_fetch_remotes(FILE *output, const char *git_dir_path)
+{
+ struct child_process cp_remote = CHILD_PROCESS_INIT;
+ struct strbuf sb_remote_out = STRBUF_INIT;
+
+ cp_remote.git_cmd = 1;
+ strvec_pushf(&cp_remote.env_array,
+ "GIT_DIR=%s", git_dir_path);
+ strvec_push(&cp_remote.env_array, "GIT_WORK_TREE=.");
+ strvec_pushl(&cp_remote.args, "remote", "-v", NULL);
+ if (!capture_command(&cp_remote, &sb_remote_out, 0)) {
+ char *next_line;
+ char *line = sb_remote_out.buf;
+ while ((next_line = strchr(line, '\n')) != NULL) {
+ size_t len = next_line - line;
+ if (strip_suffix_mem(line, &len, " (fetch)"))
+ fprintf(output, " %.*s\n", (int)len, line);
+ line = next_line + 1;
+ }
+ }
+
+ strbuf_release(&sb_remote_out);
+}
+
+static int add_submodule(const struct add_data *add_data)
+{
+ char *submod_gitdir_path;
+ struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+
+ /* perhaps the path already exists and is already a git repo, else clone it */
+ if (is_directory(add_data->sm_path)) {
+ struct strbuf sm_path = STRBUF_INIT;
+ strbuf_addstr(&sm_path, add_data->sm_path);
+ submod_gitdir_path = xstrfmt("%s/.git", add_data->sm_path);
+ if (is_nonbare_repository_dir(&sm_path))
+ printf(_("Adding existing repo at '%s' to the index\n"),
+ add_data->sm_path);
+ else
+ die(_("'%s' already exists and is not a valid git repo"),
+ add_data->sm_path);
+ strbuf_release(&sm_path);
+ free(submod_gitdir_path);
+ } else {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);
+
+ if (is_directory(submod_gitdir_path)) {
+ 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);
+ 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);
+ } else {
+ printf(_("Reactivating local git directory for "
+ "submodule '%s'\n"), add_data->sm_name);
+ }
+ }
+ free(submod_gitdir_path);
+
+ clone_data.prefix = add_data->prefix;
+ clone_data.path = add_data->sm_path;
+ clone_data.name = add_data->sm_name;
+ clone_data.url = add_data->realrepo;
+ clone_data.quiet = add_data->quiet;
+ clone_data.progress = add_data->progress;
+ if (add_data->reference_path)
+ string_list_append(&clone_data.reference,
+ xstrdup(add_data->reference_path));
+ clone_data.dissociate = add_data->dissociate;
+ if (add_data->depth >= 0)
+ clone_data.depth = xstrfmt("%d", add_data->depth);
+
+ if (clone_submodule(&clone_data))
+ return -1;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = add_data->sm_path;
+ /*
+ * NOTE: we only get here if add_data->force is true, so
+ * passing --force to checkout is reasonable.
+ */
+ strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL);
+
+ if (add_data->branch) {
+ strvec_pushl(&cp.args, "-B", add_data->branch, NULL);
+ strvec_pushf(&cp.args, "origin/%s", add_data->branch);
+ }
+
+ if (run_command(&cp))
+ die(_("unable to checkout submodule '%s'"), add_data->sm_path);
+ }
+ return 0;
+}
+
+static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
+{
+ char *key;
+ int ret;
+
+ if (!is_writing_gitmodules_ok())
+ die(_("please make sure that the .gitmodules file is in the working tree"));
+
+ key = xstrfmt("submodule.%s.%s", name, var);
+ ret = config_set_in_gitmodules_file_gently(key, value);
+ free(key);
+
+ return ret;
+}
+
+static void configure_added_submodule(struct add_data *add_data)
+{
+ char *key;
+ char *val = NULL;
+ struct child_process add_submod = CHILD_PROCESS_INIT;
+ struct child_process add_gitmodules = CHILD_PROCESS_INIT;
+
+ key = xstrfmt("submodule.%s.url", add_data->sm_name);
+ git_config_set_gently(key, add_data->realrepo);
+ free(key);
+
+ add_submod.git_cmd = 1;
+ strvec_pushl(&add_submod.args, "add",
+ "--no-warn-embedded-repo", NULL);
+ if (add_data->force)
+ strvec_push(&add_submod.args, "--force");
+ strvec_pushl(&add_submod.args, "--", add_data->sm_path, NULL);
+
+ if (run_command(&add_submod))
+ die(_("Failed to add submodule '%s'"), add_data->sm_path);
+
+ if (config_submodule_in_gitmodules(add_data->sm_name, "path", add_data->sm_path) ||
+ config_submodule_in_gitmodules(add_data->sm_name, "url", add_data->repo))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+
+ if (add_data->branch) {
+ if (config_submodule_in_gitmodules(add_data->sm_name,
+ "branch", add_data->branch))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+ }
+
+ add_gitmodules.git_cmd = 1;
+ strvec_pushl(&add_gitmodules.args,
+ "add", "--force", "--", ".gitmodules", NULL);
+
+ if (run_command(&add_gitmodules))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+
+ /*
+ * NEEDSWORK: In a multi-working-tree world this needs to be
+ * set in the per-worktree config.
+ */
+ /*
+ * NEEDSWORK: In the longer run, we need to get rid of this
+ * pattern of querying "submodule.active" before calling
+ * is_submodule_active(), since that function needs to find
+ * out the value of "submodule.active" again anyway.
+ */
+ if (!git_config_get_string("submodule.active", &val) && val) {
+ /*
+ * If the submodule being added isn't already covered by the
+ * current configured pathspec, set the submodule's active flag
+ */
+ if (!is_submodule_active(the_repository, add_data->sm_path)) {
+ key = xstrfmt("submodule.%s.active", add_data->sm_name);
+ git_config_set_gently(key, "true");
+ free(key);
+ }
+ } else {
+ key = xstrfmt("submodule.%s.active", add_data->sm_name);
+ git_config_set_gently(key, "true");
+ free(key);
+ }
+}
+
+static void die_on_index_match(const char *path, int force)
+{
+ struct pathspec ps;
+ const char *args[] = { path, NULL };
+ parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+ if (read_cache_preload(NULL) < 0)
+ die(_("index file corrupt"));
+
+ if (ps.nr) {
+ int i;
+ char *ps_matched = xcalloc(ps.nr, 1);
+
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(&the_index);
+
+ /*
+ * Since there is only one pathspec, we just need
+ * need to check ps_matched[0] to know if a cache
+ * entry matched.
+ */
+ for (i = 0; i < active_nr; i++) {
+ ce_path_match(&the_index, active_cache[i], &ps,
+ ps_matched);
+
+ if (ps_matched[0]) {
+ if (!force)
+ die(_("'%s' already exists in the index"),
+ path);
+ if (!S_ISGITLINK(active_cache[i]->ce_mode))
+ die(_("'%s' already exists in the index "
+ "and is not a submodule"), path);
+ break;
+ }
+ }
+ free(ps_matched);
+ }
+}
+
+static void die_on_repo_without_commits(const char *path)
+{
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, path);
+ if (is_nonbare_repository_dir(&sb)) {
+ struct object_id oid;
+ if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ die(_("'%s' does not have a commit checked out"), path);
+ }
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+ int force = 0, quiet = 0, progress = 0, dissociate = 0;
+ struct add_data add_data = ADD_DATA_INIT;
+
+ struct option options[] = {
+ OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+ N_("branch of repository to add as submodule")),
+ OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
+ PARSE_OPT_NOCOMPLETE),
+ OPT__QUIET(&quiet, N_("print only error messages")),
+ OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+ OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+ N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+ OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+ N_("sets the submodule’s name to the given string "
+ "instead of defaulting to its path")),
+ OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
+ OPT_END()
+ };
+
+ const char *const usage[] = {
+ N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (!is_writing_gitmodules_ok())
+ die(_("please make sure that the .gitmodules file is in the working tree"));
+
+ if (prefix && *prefix &&
+ add_data.reference_path && !is_absolute_path(add_data.reference_path))
+ add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+ if (argc == 0 || argc > 2)
+ usage_with_options(usage, options);
+
+ add_data.repo = argv[0];
+ if (argc == 1)
+ add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
+ else
+ add_data.sm_path = xstrdup(argv[1]);
+
+ if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+ add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+ if (starts_with_dot_dot_slash(add_data.repo) ||
+ starts_with_dot_slash(add_data.repo)) {
+ if (prefix)
+ die(_("Relative path can only be used from the toplevel "
+ "of the working tree"));
+
+ /* dereference source url relative to parent's url */
+ add_data.realrepo = resolve_relative_url(add_data.repo, NULL, 1);
+ } else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+ add_data.realrepo = add_data.repo;
+ } else {
+ die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+ add_data.repo);
+ }
+
+ /*
+ * normalize path:
+ * multiple //; leading ./; /./; /../;
+ */
+ normalize_path_copy(add_data.sm_path, add_data.sm_path);
+ strip_dir_trailing_slashes(add_data.sm_path);
+
+ die_on_index_match(add_data.sm_path, force);
+ die_on_repo_without_commits(add_data.sm_path);
+
+ if (!force) {
+ int exit_code = -1;
+ struct strbuf sb = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
+ cp.no_stdout = 1;
+ strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+ "--no-warn-embedded-repo", add_data.sm_path, NULL);
+ if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+ strbuf_complete_line(&sb);
+ fputs(sb.buf, stderr);
+ free(add_data.sm_path);
+ return exit_code;
+ }
+ strbuf_release(&sb);
+ }
+
+ if(!add_data.sm_name)
+ add_data.sm_name = add_data.sm_path;
+
+ if (check_submodule_name(add_data.sm_name))
+ die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
+ add_data.prefix = prefix;
+ add_data.force = !!force;
+ add_data.quiet = !!quiet;
+ add_data.progress = !!progress;
+ add_data.dissociate = !!dissociate;
+
+ if (add_submodule(&add_data)) {
+ free(add_data.sm_path);
+ return 1;
+ }
+ configure_added_submodule(&add_data);
+ free(add_data.sm_path);
+
+ return 0;
+}
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
@@ -2756,11 +3357,12 @@ static struct cmd_struct commands[] = {
{"list", module_list, 0},
{"name", module_name, 0},
{"clone", module_clone, 0},
+ {"add", module_add, SUPPORT_SUPER_PREFIX},
{"update-module-mode", module_update_module_mode, 0},
{"update-clone", update_clone, 0},
+ {"run-update-procedure", run_update_procedure, 0},
{"ensure-core-worktree", ensure_core_worktree, 0},
{"relative-path", resolve_relative_path, 0},
- {"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
{"init", module_init, SUPPORT_SUPER_PREFIX},
diff --git a/builtin/tag.c b/builtin/tag.c
index 82fcfc0982..6535ed27ee 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,10 +23,10 @@
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
- "\t\t<tagname> [<head>]"),
+ " <tagname> [<head>]"),
N_("git tag -d <tagname>..."),
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
- "\t\t[--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
+ " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
N_("git tag -v [--format=<format>] <tagname>..."),
NULL
};
@@ -146,7 +146,7 @@ static int verify_tag(const char *name, const char *ref,
const struct object_id *oid, void *cb_data)
{
int flags;
- const struct ref_format *format = cb_data;
+ struct ref_format *format = cb_data;
flags = GPG_VERIFY_VERBOSE;
if (format->format)
@@ -293,9 +293,7 @@ static void create_tag(const struct object_id *object, const char *object_ref,
/* write the template message before editing: */
path = git_pathdup("TAG_EDITMSG");
- fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0)
- die_errno(_("could not create file '%s'"), path);
+ fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (opt->message_given) {
write_or_die(fd, buf->buf, buf->len);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index f1f16f2de5..187203e8bb 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -95,9 +95,7 @@ static int create_file(const char *path)
{
int fd;
path = get_mtime_path(path);
- fd = open(path, O_CREAT | O_RDWR, 0644);
- if (fd < 0)
- die_errno(_("failed to create file %s"), path);
+ fd = xopen(path, O_CREAT | O_RDWR, 0644);
return fd;
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6029a80544..a84e7b47a2 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -302,6 +302,12 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
strbuf_release(&err);
}
+static void report_ok(const char *command)
+{
+ fprintf(stdout, "%s: ok\n", command);
+ fflush(stdout);
+}
+
static void parse_cmd_option(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -317,7 +323,7 @@ static void parse_cmd_start(struct ref_transaction *transaction,
{
if (*next != line_termination)
die("start: extra input: %s", next);
- puts("start: ok");
+ report_ok("start");
}
static void parse_cmd_prepare(struct ref_transaction *transaction,
@@ -328,7 +334,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
die("prepare: extra input: %s", next);
if (ref_transaction_prepare(transaction, &error))
die("prepare: %s", error.buf);
- puts("prepare: ok");
+ report_ok("prepare");
}
static void parse_cmd_abort(struct ref_transaction *transaction,
@@ -339,7 +345,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
die("abort: extra input: %s", next);
if (ref_transaction_abort(transaction, &error))
die("abort: %s", error.buf);
- puts("abort: ok");
+ report_ok("abort");
}
static void parse_cmd_commit(struct ref_transaction *transaction,
@@ -350,7 +356,7 @@ static void parse_cmd_commit(struct ref_transaction *transaction,
die("commit: extra input: %s", next);
if (ref_transaction_commit(transaction, &error))
die("commit: %s", error.buf);
- puts("commit: ok");
+ report_ok("commit");
ref_transaction_free(transaction);
}
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 6da8fa2607..125af53885 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -16,16 +16,18 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
{
const char *dir;
int strict = 0;
- struct upload_pack_options opts = { 0 };
- struct serve_options serve_opts = SERVE_OPTIONS_INIT;
+ int advertise_refs = 0;
+ int stateless_rpc = 0;
+ int timeout = 0;
struct option options[] = {
- OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
N_("quit after a single request/response exchange")),
- OPT_BOOL(0, "advertise-refs", &opts.advertise_refs,
- N_("exit immediately after initial ref advertisement")),
+ OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs,
+ N_("serve up the info/refs for git-http-backend")),
+ OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
OPT_BOOL(0, "strict", &strict,
N_("do not try <directory>/.git/ if <directory> is no Git directory")),
- OPT_INTEGER(0, "timeout", &opts.timeout,
+ OPT_INTEGER(0, "timeout", &timeout,
N_("interrupt transfer after <n> seconds of inactivity")),
OPT_END()
};
@@ -38,9 +40,6 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
if (argc != 1)
usage_with_options(upload_pack_usage, options);
- if (opts.timeout)
- opts.daemon_mode = 1;
-
setup_path();
dir = argv[0];
@@ -50,21 +49,22 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
switch (determine_protocol_version_server()) {
case protocol_v2:
- serve_opts.advertise_capabilities = opts.advertise_refs;
- serve_opts.stateless_rpc = opts.stateless_rpc;
- serve(&serve_opts);
+ if (advertise_refs)
+ protocol_v2_advertise_capabilities();
+ else
+ protocol_v2_serve_loop(stateless_rpc);
break;
case protocol_v1:
/*
* v1 is just the original protocol with a version string,
* so just fall through after writing the version string.
*/
- if (opts.advertise_refs || !opts.stateless_rpc)
+ if (advertise_refs || !stateless_rpc)
packet_write_fmt(1, "version 1\n");
/* fallthrough */
case protocol_v0:
- upload_pack(&opts);
+ upload_pack(advertise_refs, stateless_rpc, timeout);
break;
case protocol_unknown_version:
BUG("unknown protocol version");
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 976bf8ed06..d22ece93e1 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -8,6 +8,7 @@
#include "branch.h"
#include "refs.h"
#include "run-command.h"
+#include "hook.h"
#include "sigchain.h"
#include "submodule.h"
#include "utf8.h"
@@ -30,7 +31,7 @@ struct add_opts {
int detach;
int quiet;
int checkout;
- int keep_locked;
+ const char *keep_locked;
};
static int show_only;
@@ -302,10 +303,10 @@ static int add_worktree(const char *path, const char *refname,
* after the preparation is over.
*/
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
- if (!opts->keep_locked)
- write_file(sb.buf, "initializing");
+ if (opts->keep_locked)
+ write_file(sb.buf, "%s", opts->keep_locked);
else
- write_file(sb.buf, "added with --lock");
+ write_file(sb.buf, _("initializing"));
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
@@ -475,6 +476,8 @@ static int add(int ac, const char **av, const char *prefix)
const char *branch;
const char *new_branch = NULL;
const char *opt_track = NULL;
+ const char *lock_reason = NULL;
+ int keep_locked = 0;
struct option options[] = {
OPT__FORCE(&opts.force,
N_("checkout <branch> even if already checked out in other worktree"),
@@ -485,7 +488,9 @@ static int add(int ac, const char **av, const char *prefix)
N_("create or reset a branch")),
OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
- OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+ OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
+ OPT_STRING(0, "reason", &lock_reason, N_("string"),
+ N_("reason for locking")),
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
OPT_PASSTHRU(0, "track", &opt_track, NULL,
N_("set up tracking mode (see git-branch(1))"),
@@ -500,6 +505,13 @@ static int add(int ac, const char **av, const char *prefix)
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"));
+ if (lock_reason && !keep_locked)
+ die(_("--reason requires --lock"));
+ if (lock_reason)
+ opts.keep_locked = lock_reason;
+ else if (keep_locked)
+ opts.keep_locked = _("added with --lock");
+
if (ac < 1 || ac > 2)
usage_with_options(worktree_usage, options);