summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/am.c6
-rw-r--r--builtin/blame.c2
-rw-r--r--builtin/checkout.c22
-rw-r--r--builtin/clone.c71
-rw-r--r--builtin/commit.c2
-rw-r--r--builtin/diff-index.c10
-rw-r--r--builtin/diff-tree.c17
-rw-r--r--builtin/diff.c49
-rw-r--r--builtin/fast-import.c31
-rw-r--r--builtin/gc.c338
-rw-r--r--builtin/grep.c11
-rw-r--r--builtin/log.c2
-rw-r--r--builtin/merge.c2
-rw-r--r--builtin/pull.c2
-rw-r--r--builtin/push.c27
-rw-r--r--builtin/rebase.c2
-rw-r--r--builtin/remote.c7
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/send-pack.c11
-rw-r--r--builtin/worktree.c5
20 files changed, 551 insertions, 68 deletions
diff --git a/builtin/am.c b/builtin/am.c
index 2c7673f74e..b727449aed 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -161,7 +161,7 @@ static void am_state_init(struct am_state *state)
state->committer_name =
xmemdupz(id.name_begin, id.name_end - id.name_begin);
state->committer_email =
- xmemdupz(id.mail_begin, id.mail_end - id.mail_end);
+ xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
}
/**
@@ -1595,7 +1595,7 @@ static void do_commit(const struct am_state *state)
if (state->committer_date_is_author_date)
committer = fmt_ident(state->committer_name,
- state->author_email, WANT_COMMITTER_IDENT,
+ state->committer_email, WANT_COMMITTER_IDENT,
state->ignore_date ? NULL
: state->author_date,
IDENT_STRICT);
@@ -2237,7 +2237,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
N_("allow fall back on 3way merging if needed")),
OPT__QUIET(&state.quiet, N_("be quiet")),
OPT_SET_INT('s', "signoff", &state.signoff,
- N_("add a Signed-off-by line to the commit message"),
+ N_("add a Signed-off-by trailer to the commit message"),
SIGNOFF_EXPLICIT),
OPT_BOOL('u', "utf8", &state.utf8,
N_("recode into utf8 (default)")),
diff --git a/builtin/blame.c b/builtin/blame.c
index bb0f29300e..b5036ab327 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -820,6 +820,8 @@ static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata)
if (kind != OBJ_TAG)
return -1;
obj = deref_tag(r, parse_object(r, &oid), NULL, 0);
+ if (!obj)
+ return -1;
oidcpy(&oid, &obj->oid);
}
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0951f8fee5..7c311cecb3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -471,6 +471,19 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->patch_mode) {
const char *patch_mode;
+ const char *rev = new_branch_info->name;
+ char rev_oid[GIT_MAX_HEXSZ + 1];
+
+ /*
+ * Since rev can be in the form of `<a>...<b>` (which is not
+ * recognized by diff-index), we will always replace the name
+ * with the hex of the commit (whether it's in `...` form or
+ * not) for the run_add_interactive() machinery to work
+ * properly. However, there is special logic for the HEAD case
+ * so we mustn't replace that.
+ */
+ if (rev && strcmp(rev, "HEAD"))
+ rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
if (opts->checkout_index && opts->checkout_worktree)
patch_mode = "--patch=checkout";
@@ -481,7 +494,7 @@ static int checkout_paths(const struct checkout_opts *opts,
else
BUG("either flag must have been set, worktree=%d, index=%d",
opts->checkout_worktree, opts->checkout_index);
- return run_add_interactive(new_branch_info->name, patch_mode, &opts->pathspec);
+ return run_add_interactive(rev, patch_mode, &opts->pathspec);
}
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -1093,11 +1106,16 @@ static int switch_branches(const struct checkout_opts *opts,
static int git_checkout_config(const char *var, const char *value, void *cb)
{
+ struct checkout_opts *opts = cb;
+
if (!strcmp(var, "diff.ignoresubmodules")) {
- struct checkout_opts *opts = cb;
handle_ignore_submodules_arg(&opts->diff_options, value);
return 0;
}
+ if (!strcmp(var, "checkout.guess")) {
+ opts->dwim_new_local_branch = git_config_bool(var, value);
+ return 0;
+ }
if (starts_with(var, "submodule."))
return git_default_submodule_config(var, value, NULL);
diff --git a/builtin/clone.c b/builtin/clone.c
index 391aa41075..a0841923cf 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -53,6 +53,7 @@ static int option_shallow_submodules;
static int deepen;
static char *option_template, *option_depth, *option_since;
static char *option_origin = NULL;
+static char *remote_name = NULL;
static char *option_branch = NULL;
static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir;
@@ -721,7 +722,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
if (!option_bare) {
update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
- install_branch_config(0, head, option_origin, our->name);
+ install_branch_config(0, head, remote_name, our->name);
}
} else if (our) {
struct commit *c = lookup_commit_reference(the_repository,
@@ -851,8 +852,26 @@ static int checkout(int submodule_progress)
return err;
}
+static int git_clone_config(const char *k, const char *v, void *cb)
+{
+ if (!strcmp(k, "clone.defaultremotename")) {
+ free(remote_name);
+ remote_name = xstrdup(v);
+ }
+ return git_default_config(k, v, cb);
+}
+
static int write_one_config(const char *key, const char *value, void *data)
{
+ /*
+ * give git_clone_config a chance to write config values back to the
+ * environment, since git_config_set_multivar_gently only deals with
+ * config-file writes
+ */
+ int apply_failed = git_clone_config(key, value, data);
+ if (apply_failed)
+ return apply_failed;
+
return git_config_set_multivar_gently(key,
value ? value : "true",
CONFIG_REGEX_NONE, 0);
@@ -905,12 +924,12 @@ static void write_refspec_config(const char *src_ref_prefix,
}
/* Configure the remote */
if (value.len) {
- strbuf_addf(&key, "remote.%s.fetch", option_origin);
+ strbuf_addf(&key, "remote.%s.fetch", remote_name);
git_config_set_multivar(key.buf, value.buf, "^$", 0);
strbuf_reset(&key);
if (option_mirror) {
- strbuf_addf(&key, "remote.%s.mirror", option_origin);
+ strbuf_addf(&key, "remote.%s.mirror", remote_name);
git_config_set(key.buf, "true");
strbuf_reset(&key);
}
@@ -963,6 +982,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct strvec ref_prefixes = STRVEC_INIT;
packet_trace_identity("clone");
+
+ git_config(git_clone_config, NULL);
+
argc = parse_options(argc, argv, prefix, builtin_clone_options,
builtin_clone_usage, 0);
@@ -991,9 +1013,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
option_no_checkout = 1;
}
- if (!option_origin)
- option_origin = "origin";
-
repo_name = argv[0];
path = get_repo_path(repo_name, &is_bundle);
@@ -1124,9 +1143,30 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (real_git_dir)
git_dir = real_git_dir;
+ /*
+ * additional config can be injected with -c, make sure it's included
+ * after init_db, which clears the entire config environment.
+ */
write_config(&option_config);
- git_config(git_default_config, NULL);
+ /*
+ * re-read config after init_db and write_config to pick up any config
+ * injected by --template and --config, respectively.
+ */
+ git_config(git_clone_config, NULL);
+
+ /*
+ * apply the remote name provided by --origin only after this second
+ * call to git_config, to ensure it overrides all config-based values.
+ */
+ if (option_origin != NULL)
+ remote_name = xstrdup(option_origin);
+
+ if (remote_name == NULL)
+ remote_name = xstrdup("origin");
+
+ if (!valid_remote_name(remote_name))
+ die(_("'%s' is not a valid remote name"), remote_name);
if (option_bare) {
if (option_mirror)
@@ -1135,15 +1175,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set("core.bare", "true");
} else {
- strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
+ strbuf_addf(&branch_top, "refs/remotes/%s/", remote_name);
}
- strbuf_addf(&key, "remote.%s.url", option_origin);
+ strbuf_addf(&key, "remote.%s.url", remote_name);
git_config_set(key.buf, repo);
strbuf_reset(&key);
if (option_no_tags) {
- strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+ strbuf_addf(&key, "remote.%s.tagOpt", remote_name);
git_config_set(key.buf, "--no-tags");
strbuf_reset(&key);
}
@@ -1154,7 +1194,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (option_sparse_checkout && git_sparse_checkout_init(dir))
return 1;
- remote = remote_get(option_origin);
+ remote = remote_get(remote_name);
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
branch_top.buf);
@@ -1266,7 +1306,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (!our_head_points_at)
die(_("Remote branch %s not found in upstream %s"),
- option_branch, option_origin);
+ option_branch, remote_name);
}
else
our_head_points_at = remote_head_points_at;
@@ -1274,7 +1314,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
else {
if (option_branch)
die(_("Remote branch %s not found in upstream %s"),
- option_branch, option_origin);
+ option_branch, remote_name);
warning(_("You appear to have cloned an empty repository."));
mapped_refs = NULL;
@@ -1286,7 +1326,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
const char *branch = git_default_branch_name();
char *ref = xstrfmt("refs/heads/%s", branch);
- install_branch_config(0, branch, option_origin, ref);
+ install_branch_config(0, branch, remote_name, ref);
free(ref);
}
}
@@ -1295,7 +1335,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head_points_at, &branch_top);
if (filter_options.choice)
- partial_clone_register(option_origin, &filter_options);
+ partial_clone_register(remote_name, &filter_options);
if (is_local)
clone_local(path, git_dir);
@@ -1327,6 +1367,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
junk_mode = JUNK_LEAVE_REPO;
err = checkout(submodule_progress);
+ free(remote_name);
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
strbuf_release(&key);
diff --git a/builtin/commit.c b/builtin/commit.c
index 1dfd799ec5..505fe60956 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1507,7 +1507,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
- OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
+ OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
OPT_CLEANUP(&cleanup_arg),
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 93ec642423..7f5281c461 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -15,7 +15,7 @@ COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_index(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
- int cached = 0;
+ unsigned int option = 0;
int i;
int result;
@@ -32,7 +32,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
const char *arg = argv[i];
if (!strcmp(arg, "--cached"))
- cached = 1;
+ option |= DIFF_INDEX_CACHED;
+ else if (!strcmp(arg, "--merge-base"))
+ option |= DIFF_INDEX_MERGE_BASE;
else
usage(diff_cache_usage);
}
@@ -46,7 +48,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
if (rev.pending.nr != 1 ||
rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
usage(diff_cache_usage);
- if (!cached) {
+ if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
perror("read_cache_preload");
@@ -56,7 +58,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
perror("read_cache");
return -1;
}
- result = run_diff_index(&rev, cached);
+ result = run_diff_index(&rev, option);
UNLEAK(rev);
return diff_result_code(&rev.diffopt, result);
}
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 802363d0a2..9fc95e959f 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -111,6 +111,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
struct setup_revision_opt s_r_opt;
struct userformat_want w;
int read_stdin = 0;
+ int merge_base = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(diff_tree_usage);
@@ -143,9 +144,18 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
read_stdin = 1;
continue;
}
+ if (!strcmp(arg, "--merge-base")) {
+ merge_base = 1;
+ continue;
+ }
usage(diff_tree_usage);
}
+ if (read_stdin && merge_base)
+ die(_("--stdin and --merge-base are mutually exclusive"));
+ if (merge_base && opt->pending.nr != 2)
+ die(_("--merge-base only works with two commits"));
+
/*
* NOTE! We expect "a..b" to expand to "^a b" but it is
* perfectly valid for revision range parser to yield "b ^a",
@@ -165,7 +175,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
case 2:
tree1 = opt->pending.objects[0].item;
tree2 = opt->pending.objects[1].item;
- if (tree2->flags & UNINTERESTING) {
+ if (merge_base) {
+ struct object_id oid;
+
+ diff_get_merge_base(opt, &oid);
+ tree1 = lookup_object(the_repository, &oid);
+ } else if (tree2->flags & UNINTERESTING) {
SWAP(tree2, tree1);
}
diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
diff --git a/builtin/diff.c b/builtin/diff.c
index cd4083fed9..780c33877f 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -26,7 +26,7 @@
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> [<commit>...] <commit> [--] [<path>...]\n"
+" or: git diff [<options>] <commit> [--merge-base] [<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"
@@ -134,11 +134,13 @@ static int builtin_diff_blobs(struct rev_info *revs,
static int builtin_diff_index(struct rev_info *revs,
int argc, const char **argv)
{
- int cached = 0;
+ unsigned int option = 0;
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
- cached = 1;
+ option |= DIFF_INDEX_CACHED;
+ else if (!strcmp(arg, "--merge-base"))
+ option |= DIFF_INDEX_MERGE_BASE;
else
usage(builtin_diff_usage);
argv++; argc--;
@@ -151,7 +153,7 @@ static int builtin_diff_index(struct rev_info *revs,
revs->max_count != -1 || revs->min_age != -1 ||
revs->max_age != -1)
usage(builtin_diff_usage);
- if (!cached) {
+ if (!(option & DIFF_INDEX_CACHED)) {
setup_work_tree();
if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
perror("read_cache_preload");
@@ -161,7 +163,7 @@ static int builtin_diff_index(struct rev_info *revs,
perror("read_cache");
return -1;
}
- return run_diff_index(revs, cached);
+ return run_diff_index(revs, option);
}
static int builtin_diff_tree(struct rev_info *revs,
@@ -170,19 +172,34 @@ static int builtin_diff_tree(struct rev_info *revs,
struct object_array_entry *ent1)
{
const struct object_id *(oid[2]);
- int swap = 0;
+ struct object_id mb_oid;
+ int merge_base = 0;
- if (argc > 1)
- usage(builtin_diff_usage);
+ while (1 < argc) {
+ const char *arg = argv[1];
+ if (!strcmp(arg, "--merge-base"))
+ merge_base = 1;
+ else
+ usage(builtin_diff_usage);
+ argv++; argc--;
+ }
- /*
- * We saw two trees, ent0 and ent1. If ent1 is uninteresting,
- * swap them.
- */
- if (ent1->item->flags & UNINTERESTING)
- swap = 1;
- oid[swap] = &ent0->item->oid;
- oid[1 - swap] = &ent1->item->oid;
+ if (merge_base) {
+ diff_get_merge_base(revs, &mb_oid);
+ oid[0] = &mb_oid;
+ oid[1] = &revs->pending.objects[1].item->oid;
+ } else {
+ int swap = 0;
+
+ /*
+ * We saw two trees, ent0 and ent1. If ent1 is uninteresting,
+ * swap them.
+ */
+ if (ent1->item->flags & UNINTERESTING)
+ swap = 1;
+ oid[swap] = &ent0->item->oid;
+ oid[1 - swap] = &ent1->item->oid;
+ }
diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
log_tree_diff_flush(revs);
return 0;
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 1bf50a73dc..70d7d25eed 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -150,7 +150,7 @@ struct recent_command {
char *buf;
};
-typedef void (*mark_set_inserter_t)(struct mark_set *s, struct object_id *oid, uintmax_t mark);
+typedef void (*mark_set_inserter_t)(struct mark_set **s, struct object_id *oid, uintmax_t mark);
typedef void (*each_mark_fn_t)(uintmax_t mark, void *obj, void *cbp);
/* Configured limits on output */
@@ -526,13 +526,15 @@ static unsigned int hc_str(const char *s, size_t len)
return r;
}
-static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe)
+static void insert_mark(struct mark_set **top, uintmax_t idnum, struct object_entry *oe)
{
+ struct mark_set *s = *top;
+
while ((idnum >> s->shift) >= 1024) {
s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
- s->shift = marks->shift + 10;
- s->data.sets[0] = marks;
- marks = s;
+ s->shift = (*top)->shift + 10;
+ s->data.sets[0] = *top;
+ *top = s;
}
while (s->shift) {
uintmax_t i = idnum >> s->shift;
@@ -944,7 +946,7 @@ static int store_object(
e = insert_object(&oid);
if (mark)
- insert_mark(marks, mark, e);
+ insert_mark(&marks, mark, e);
if (e->idx.offset) {
duplicate_count_by_type[type]++;
return 1;
@@ -1142,7 +1144,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
e = insert_object(&oid);
if (mark)
- insert_mark(marks, mark, e);
+ insert_mark(&marks, mark, e);
if (e->idx.offset) {
duplicate_count_by_type[OBJ_BLOB]++;
@@ -1717,7 +1719,7 @@ static void dump_marks(void)
}
}
-static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+static void insert_object_entry(struct mark_set **s, struct object_id *oid, uintmax_t mark)
{
struct object_entry *e;
e = find_object(oid);
@@ -1734,12 +1736,12 @@ static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintm
insert_mark(s, mark, e);
}
-static void insert_oid_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+static void insert_oid_entry(struct mark_set **s, struct object_id *oid, uintmax_t mark)
{
insert_mark(s, mark, xmemdupz(oid, sizeof(*oid)));
}
-static void read_mark_file(struct mark_set *s, FILE *f, mark_set_inserter_t inserter)
+static void read_mark_file(struct mark_set **s, FILE *f, mark_set_inserter_t inserter)
{
char line[512];
while (fgets(line, sizeof(line), f)) {
@@ -1772,7 +1774,7 @@ static void read_marks(void)
goto done; /* Marks file does not exist */
else
die_errno("cannot read '%s'", import_marks_file);
- read_mark_file(marks, f, insert_object_entry);
+ read_mark_file(&marks, f, insert_object_entry);
fclose(f);
done:
import_marks_file_done = 1;
@@ -3228,7 +3230,7 @@ static void parse_alias(void)
die(_("Expected 'to' command, got %s"), command_buf.buf);
e = find_object(&b.oid);
assert(e);
- insert_mark(marks, next_mark, e);
+ insert_mark(&marks, next_mark, e);
}
static char* make_fast_import_path(const char *path)
@@ -3321,13 +3323,14 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
*f = '\0';
f++;
ms = xcalloc(1, sizeof(*ms));
- string_list_insert(list, s)->util = ms;
fp = fopen(f, "r");
if (!fp)
die_errno("cannot read '%s'", f);
- read_mark_file(ms, fp, insert_oid_entry);
+ read_mark_file(&ms, fp, insert_oid_entry);
fclose(fp);
+
+ string_list_insert(list, s)->util = ms;
}
static int parse_one_option(const char *option)
diff --git a/builtin/gc.c b/builtin/gc.c
index 090959350e..3629a82299 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -29,6 +29,8 @@
#include "tree.h"
#include "promisor-remote.h"
#include "refs.h"
+#include "remote.h"
+#include "object-store.h"
#define FAILED_RUN "failed to run %s"
@@ -737,9 +739,15 @@ static int dfs_on_ref(const char *refname,
commit = lookup_commit(the_repository, oid);
if (!commit)
return 0;
- if (parse_commit(commit))
+ if (parse_commit(commit) ||
+ commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
return 0;
+ data->num_not_in_graph++;
+
+ if (data->num_not_in_graph >= data->limit)
+ return 1;
+
commit_list_append(commit, &stack);
while (!result && stack) {
@@ -807,6 +815,10 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
{
+ prepare_repo_settings(the_repository);
+ 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"));
@@ -816,6 +828,51 @@ static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
return 0;
}
+static int fetch_remote(const char *remote, struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_pushl(&child.args, "fetch", remote, "--prune", "--no-tags",
+ "--no-write-fetch-head", "--recurse-submodules=no",
+ "--refmap=", NULL);
+
+ if (opts->quiet)
+ strvec_push(&child.args, "--quiet");
+
+ strvec_pushf(&child.args, "+refs/heads/*:refs/prefetch/%s/*", remote);
+
+ return !!run_command(&child);
+}
+
+static int append_remote(struct remote *remote, void *cbdata)
+{
+ struct string_list *remotes = (struct string_list *)cbdata;
+
+ string_list_append(remotes, remote->name);
+ return 0;
+}
+
+static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
+{
+ int result = 0;
+ struct string_list_item *item;
+ struct string_list remotes = STRING_LIST_INIT_DUP;
+
+ if (for_each_remote(append_remote, &remotes)) {
+ error(_("failed to fill remotes"));
+ result = 1;
+ goto cleanup;
+ }
+
+ for_each_string_list_item(item, &remotes)
+ result |= fetch_remote(item->string, opts);
+
+cleanup:
+ string_list_clear(&remotes, 0);
+ return result;
+}
+
static int maintenance_task_gc(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -834,6 +891,268 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
return run_command(&child);
}
+static int prune_packed(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_push(&child.args, "prune-packed");
+
+ if (opts->quiet)
+ strvec_push(&child.args, "--quiet");
+
+ return !!run_command(&child);
+}
+
+struct write_loose_object_data {
+ FILE *in;
+ int count;
+ int batch_size;
+};
+
+static int loose_object_auto_limit = 100;
+
+static int loose_object_count(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ int *count = (int*)data;
+ if (++(*count) >= loose_object_auto_limit)
+ return 1;
+ return 0;
+}
+
+static int loose_object_auto_condition(void)
+{
+ int count = 0;
+
+ git_config_get_int("maintenance.loose-objects.auto",
+ &loose_object_auto_limit);
+
+ if (!loose_object_auto_limit)
+ return 0;
+ if (loose_object_auto_limit < 0)
+ return 1;
+
+ return for_each_loose_file_in_objdir(the_repository->objects->odb->path,
+ loose_object_count,
+ NULL, NULL, &count);
+}
+
+static int bail_on_loose(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ return 1;
+}
+
+static int write_loose_object_to_stdin(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ struct write_loose_object_data *d = (struct write_loose_object_data *)data;
+
+ fprintf(d->in, "%s\n", oid_to_hex(oid));
+
+ return ++(d->count) > d->batch_size;
+}
+
+static int pack_loose(struct maintenance_run_opts *opts)
+{
+ struct repository *r = the_repository;
+ int result = 0;
+ struct write_loose_object_data data;
+ struct child_process pack_proc = CHILD_PROCESS_INIT;
+
+ /*
+ * Do not start pack-objects process
+ * if there are no loose objects.
+ */
+ if (!for_each_loose_file_in_objdir(r->objects->odb->path,
+ bail_on_loose,
+ NULL, NULL, NULL))
+ return 0;
+
+ pack_proc.git_cmd = 1;
+
+ strvec_push(&pack_proc.args, "pack-objects");
+ if (opts->quiet)
+ strvec_push(&pack_proc.args, "--quiet");
+ strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->odb->path);
+
+ pack_proc.in = -1;
+
+ if (start_command(&pack_proc)) {
+ error(_("failed to start 'git pack-objects' process"));
+ return 1;
+ }
+
+ data.in = xfdopen(pack_proc.in, "w");
+ data.count = 0;
+ data.batch_size = 50000;
+
+ for_each_loose_file_in_objdir(r->objects->odb->path,
+ write_loose_object_to_stdin,
+ NULL,
+ NULL,
+ &data);
+
+ fclose(data.in);
+
+ if (finish_command(&pack_proc)) {
+ error(_("failed to finish 'git pack-objects' process"));
+ result = 1;
+ }
+
+ return result;
+}
+
+static int maintenance_task_loose_objects(struct maintenance_run_opts *opts)
+{
+ return prune_packed(opts) || pack_loose(opts);
+}
+
+static int incremental_repack_auto_condition(void)
+{
+ struct packed_git *p;
+ int enabled;
+ int incremental_repack_auto_limit = 10;
+ int count = 0;
+
+ if (git_config_get_bool("core.multiPackIndex", &enabled) ||
+ !enabled)
+ return 0;
+
+ git_config_get_int("maintenance.incremental-repack.auto",
+ &incremental_repack_auto_limit);
+
+ if (!incremental_repack_auto_limit)
+ return 0;
+ if (incremental_repack_auto_limit < 0)
+ return 1;
+
+ for (p = get_packed_git(the_repository);
+ count < incremental_repack_auto_limit && p;
+ p = p->next) {
+ if (!p->multi_pack_index)
+ count++;
+ }
+
+ return count >= incremental_repack_auto_limit;
+}
+
+static int multi_pack_index_write(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_pushl(&child.args, "multi-pack-index", "write", NULL);
+
+ if (opts->quiet)
+ strvec_push(&child.args, "--no-progress");
+
+ if (run_command(&child))
+ return error(_("failed to write multi-pack-index"));
+
+ return 0;
+}
+
+static int multi_pack_index_expire(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 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"));
+
+ return 0;
+}
+
+#define TWO_GIGABYTES (INT32_MAX)
+
+static off_t get_auto_pack_size(void)
+{
+ /*
+ * The "auto" value is special: we optimize for
+ * one large pack-file (i.e. from a clone) and
+ * expect the rest to be small and they can be
+ * repacked quickly.
+ *
+ * The strategy we select here is to select a
+ * size that is one more than the second largest
+ * pack-file. This ensures that we will repack
+ * at least two packs if there are three or more
+ * packs.
+ */
+ off_t max_size = 0;
+ off_t second_largest_size = 0;
+ off_t result_size;
+ struct packed_git *p;
+ struct repository *r = the_repository;
+
+ reprepare_packed_git(r);
+ for (p = get_all_packs(r); p; p = p->next) {
+ if (p->pack_size > max_size) {
+ second_largest_size = max_size;
+ max_size = p->pack_size;
+ } else if (p->pack_size > second_largest_size)
+ second_largest_size = p->pack_size;
+ }
+
+ result_size = second_largest_size + 1;
+
+ /* But limit ourselves to a batch size of 2g */
+ if (result_size > TWO_GIGABYTES)
+ result_size = TWO_GIGABYTES;
+
+ return result_size;
+}
+
+static int multi_pack_index_repack(struct maintenance_run_opts *opts)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ child.git_cmd = 1;
+ strvec_pushl(&child.args, "multi-pack-index", "repack", NULL);
+
+ if (opts->quiet)
+ strvec_push(&child.args, "--no-progress");
+
+ 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"));
+
+ return 0;
+}
+
+static int maintenance_task_increm