aboutsummaryrefslogtreecommitdiffstats
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/add.c22
-rw-r--r--builtin/bisect.c44
-rw-r--r--builtin/blame.c52
-rw-r--r--builtin/cat-file.c5
-rw-r--r--builtin/checkout.c13
-rw-r--r--builtin/commit-graph.c2
-rw-r--r--builtin/commit.c18
-rw-r--r--builtin/count-objects.c3
-rw-r--r--builtin/credential-store.c7
-rw-r--r--builtin/describe.c18
-rw-r--r--builtin/fast-export.c86
-rw-r--r--builtin/fast-import.c327
-rw-r--r--builtin/fetch.c13
-rw-r--r--builtin/fsck.c48
-rw-r--r--builtin/gc.c429
-rw-r--r--builtin/grep.c2
-rw-r--r--builtin/history.c561
-rw-r--r--builtin/hook.c6
-rw-r--r--builtin/interpret-trailers.c81
-rw-r--r--builtin/last-modified.c250
-rw-r--r--builtin/ls-remote.c2
-rw-r--r--builtin/name-rev.c17
-rw-r--r--builtin/pack-objects.c91
-rw-r--r--builtin/pack-redundant.c14
-rw-r--r--builtin/rebase.c50
-rw-r--r--builtin/receive-pack.c302
-rw-r--r--builtin/remote.c44
-rw-r--r--builtin/repack.c1360
-rw-r--r--builtin/replace.c21
-rw-r--r--builtin/replay.c221
-rw-r--r--builtin/repo.c440
-rw-r--r--builtin/reset.c16
-rw-r--r--builtin/rev-parse.c23
-rw-r--r--builtin/show-branch.c35
-rw-r--r--builtin/show-ref.c50
-rw-r--r--builtin/sparse-checkout.c216
-rw-r--r--builtin/stash.c46
-rw-r--r--builtin/submodule--helper.c40
-rw-r--r--builtin/tag.c5
-rw-r--r--builtin/unpack-objects.c7
-rw-r--r--builtin/verify-tag.c2
-rw-r--r--builtin/worktree.c6
42 files changed, 2712 insertions, 2283 deletions
diff --git a/builtin/add.c b/builtin/add.c
index 32709794b3..6f1e213052 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -31,7 +31,7 @@ static const char * const builtin_add_usage[] = {
NULL
};
static int patch_interactive, add_interactive, edit_interactive;
-static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
+static struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
@@ -160,7 +160,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec *
int interactive_add(struct repository *repo,
const char **argv,
const char *prefix,
- int patch, struct add_p_opt *add_p_opt)
+ int patch, struct interactive_options *interactive_opts)
{
struct pathspec pathspec;
int ret;
@@ -172,9 +172,9 @@ int interactive_add(struct repository *repo,
prefix, argv);
if (patch)
- ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec);
+ ret = !!run_add_p(repo, ADD_P_ADD, interactive_opts, NULL, &pathspec);
else
- ret = !!run_add_i(repo, &pathspec, add_p_opt);
+ ret = !!run_add_i(repo, &pathspec, interactive_opts);
clear_pathspec(&pathspec);
return ret;
@@ -256,8 +256,8 @@ static struct option builtin_add_options[] = {
OPT_GROUP(""),
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
- OPT_DIFF_UNIFIED(&add_p_opt.context),
- OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
+ OPT_DIFF_UNIFIED(&interactive_opts.context),
+ OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext),
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
@@ -400,9 +400,9 @@ int cmd_add(int argc,
prepare_repo_settings(repo);
repo->settings.command_requires_full_index = 0;
- if (add_p_opt.context < -1)
+ if (interactive_opts.context < -1)
die(_("'%s' cannot be negative"), "--unified");
- if (add_p_opt.interhunkcontext < -1)
+ if (interactive_opts.interhunkcontext < -1)
die(_("'%s' cannot be negative"), "--inter-hunk-context");
if (patch_interactive)
@@ -412,11 +412,11 @@ int cmd_add(int argc,
die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
if (pathspec_from_file)
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
- exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt));
+ exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &interactive_opts));
} else {
- if (add_p_opt.context != -1)
+ if (interactive_opts.context != -1)
die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
- if (add_p_opt.interhunkcontext != -1)
+ if (interactive_opts.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 8b8d870cd1..4cc118fb57 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -27,13 +27,14 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_START_USAGE \
- N_("git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>]" \
- " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--]" \
- " [<pathspec>...]")
-#define BUILTIN_GIT_BISECT_STATE_USAGE \
- N_("git bisect (good|bad) [<rev>...]")
+ N_("git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]\n" \
+ " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]")
+#define BUILTIN_GIT_BISECT_BAD_USAGE \
+ N_("git bisect (bad|new|<term-new>) [<rev>]")
+#define BUILTIN_GIT_BISECT_GOOD_USAGE \
+ N_("git bisect (good|old|<term-old>) [<rev>...]")
#define BUILTIN_GIT_BISECT_TERMS_USAGE \
- "git bisect terms [--term-good | --term-bad]"
+ "git bisect terms [--term-(good|old) | --term-(bad|new)]"
#define BUILTIN_GIT_BISECT_SKIP_USAGE \
N_("git bisect skip [(<rev>|<range>)...]")
#define BUILTIN_GIT_BISECT_NEXT_USAGE \
@@ -41,17 +42,20 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
#define BUILTIN_GIT_BISECT_RESET_USAGE \
N_("git bisect reset [<commit>]")
#define BUILTIN_GIT_BISECT_VISUALIZE_USAGE \
- "git bisect visualize"
+ "git bisect (visualize|view)"
#define BUILTIN_GIT_BISECT_REPLAY_USAGE \
N_("git bisect replay <logfile>")
#define BUILTIN_GIT_BISECT_LOG_USAGE \
"git bisect log"
#define BUILTIN_GIT_BISECT_RUN_USAGE \
N_("git bisect run <cmd> [<arg>...]")
+#define BUILTIN_GIT_BISECT_HELP_USAGE \
+ "git bisect help"
static const char * const git_bisect_usage[] = {
BUILTIN_GIT_BISECT_START_USAGE,
- BUILTIN_GIT_BISECT_STATE_USAGE,
+ BUILTIN_GIT_BISECT_BAD_USAGE,
+ BUILTIN_GIT_BISECT_GOOD_USAGE,
BUILTIN_GIT_BISECT_TERMS_USAGE,
BUILTIN_GIT_BISECT_SKIP_USAGE,
BUILTIN_GIT_BISECT_NEXT_USAGE,
@@ -60,6 +64,7 @@ static const char * const git_bisect_usage[] = {
BUILTIN_GIT_BISECT_REPLAY_USAGE,
BUILTIN_GIT_BISECT_LOG_USAGE,
BUILTIN_GIT_BISECT_RUN_USAGE,
+ BUILTIN_GIT_BISECT_HELP_USAGE,
NULL
};
@@ -358,10 +363,7 @@ static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
return 0;
}
-static int inc_nr(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int inc_nr(const struct reference *ref UNUSED, void *cb_data)
{
unsigned int *nr = (unsigned int *)cb_data;
(*nr)++;
@@ -549,12 +551,11 @@ finish:
return res;
}
-static int add_bisect_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb)
+static int add_bisect_ref(const struct reference *ref, void *cb)
{
struct add_bisect_ref_data *data = cb;
- add_pending_oid(data->revs, refname, oid, data->object_flags);
+ add_pending_oid(data->revs, ref->name, ref->oid, data->object_flags);
return 0;
}
@@ -1165,12 +1166,9 @@ static int bisect_visualize(struct bisect_terms *terms, int argc,
return run_command(&cmd);
}
-static int get_first_good(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int get_first_good(const struct reference *ref, void *cb_data)
{
- oidcpy(cb_data, oid);
+ oidcpy(cb_data, ref->oid);
return 1;
}
@@ -1453,9 +1451,13 @@ int cmd_bisect(int argc,
if (!argc)
usage_msg_opt(_("need a command"), git_bisect_usage, options);
+ if (!strcmp(argv[0], "help"))
+ usage_with_options(git_bisect_usage, options);
+
set_terms(&terms, "bad", "good");
get_terms(&terms);
- if (check_and_set_terms(&terms, argv[0]))
+ if (check_and_set_terms(&terms, argv[0]) ||
+ !one_of(argv[0], terms.term_good, terms.term_bad, NULL))
usage_msg_optf(_("unknown command: '%s'"), git_bisect_usage,
options, argv[0]);
res = bisect_state(&terms, argc, argv);
diff --git a/builtin/blame.c b/builtin/blame.c
index 2703820258..27b513d27f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -779,6 +779,19 @@ static int git_blame_config(const char *var, const char *value,
}
}
+ if (!strcmp(var, "diff.algorithm")) {
+ long diff_algorithm;
+ if (!value)
+ return config_error_nonbool(var);
+ diff_algorithm = parse_algorithm_value(value);
+ if (diff_algorithm < 0)
+ return error(_("unknown value for config '%s': %s"),
+ var, value);
+ xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+ xdl_opts |= diff_algorithm;
+ return 0;
+ }
+
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
if (userdiff_config(var, value) < 0)
@@ -824,6 +837,38 @@ static int blame_move_callback(const struct option *option, const char *arg, int
return 0;
}
+static int blame_diff_algorithm_minimal(const struct option *option,
+ const char *arg, int unset)
+{
+ int *opt = option->value;
+
+ BUG_ON_OPT_ARG(arg);
+
+ *opt &= ~XDF_DIFF_ALGORITHM_MASK;
+ if (!unset)
+ *opt |= XDF_NEED_MINIMAL;
+
+ return 0;
+}
+
+static int blame_diff_algorithm_callback(const struct option *option,
+ const char *arg, int unset)
+{
+ int *opt = option->value;
+ long value = parse_algorithm_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+
+ if (value < 0)
+ return error(_("option diff-algorithm accepts \"myers\", "
+ "\"minimal\", \"patience\" and \"histogram\""));
+
+ *opt &= ~XDF_DIFF_ALGORITHM_MASK;
+ *opt |= value;
+
+ return 0;
+}
+
static int is_a_rev(const char *name)
{
struct object_id oid;
@@ -915,11 +960,16 @@ int cmd_blame(int argc,
OPT_BIT('s', NULL, &output_option, N_("suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
OPT_BIT('e', "show-email", &output_option, N_("show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
OPT_BIT('w', NULL, &xdl_opts, N_("ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+ OPT_CALLBACK_F(0, "diff-algorithm", &xdl_opts, N_("<algorithm>"),
+ N_("choose a diff algorithm"),
+ PARSE_OPT_NONEG, blame_diff_algorithm_callback),
OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("ignore <rev> when blaming")),
OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("ignore revisions from <file>")),
OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE),
OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR),
- OPT_BIT(0, "minimal", &xdl_opts, N_("spend extra cycles to find better match"), XDF_NEED_MINIMAL),
+ OPT_CALLBACK_F(0, "minimal", &xdl_opts, NULL,
+ N_("spend extra cycles to find a better match"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, blame_diff_algorithm_minimal),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("use <file>'s contents as the final image")),
OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index ee6715fa52..983ecec837 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -495,7 +495,7 @@ static void batch_object_write(const char *obj_name,
OBJECT_INFO_LOOKUP_REPLACE);
if (ret < 0) {
if (data->mode == S_IFGITLINK)
- report_object_status(opt, oid_to_hex(&data->oid), &data->oid, "submodule");
+ report_object_status(opt, NULL, &data->oid, "submodule");
else
report_object_status(opt, obj_name, &data->oid, "missing");
return;
@@ -852,10 +852,9 @@ static void batch_each_object(struct batch_options *opt,
if (bitmap && !for_each_bitmapped_object(bitmap, &opt->objects_filter,
batch_one_object_bitmapped, &payload)) {
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *pack;
- for (pack = packfile_store_get_all_packs(packs); pack; pack = pack->next) {
+ repo_for_each_pack(the_repository, pack) {
if (bitmap_index_contains_pack(bitmap, pack) ||
open_pack_index(pack))
continue;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe..c09065cc61 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -546,7 +546,7 @@ static int checkout_paths(const struct checkout_opts *opts,
if (opts->patch_mode) {
enum add_p_mode patch_mode;
- struct add_p_opt add_p_opt = {
+ struct interactive_options interactive_opts = {
.context = opts->patch_context,
.interhunkcontext = opts->patch_interhunk_context,
};
@@ -575,7 +575,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_p(the_repository, patch_mode, &add_p_opt,
+ return !!run_add_p(the_repository, patch_mode, &interactive_opts,
rev, &opts->pathspec);
}
@@ -902,7 +902,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
0);
init_ui_merge_options(&o, the_repository);
o.verbosity = 0;
- work = write_in_core_index_as_tree(the_repository);
+ work = write_in_core_index_as_tree(the_repository,
+ the_repository->index);
ret = reset_tree(new_tree,
opts, 1,
@@ -1063,11 +1064,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
report_tracking(new_branch_info);
}
-static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
{
- add_pending_oid(cb_data, refname, oid, UNINTERESTING);
+ add_pending_oid(cb_data, ref->name, ref->oid, UNINTERESTING);
return 0;
}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index fe3ebaadad..d62005edc0 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -210,6 +210,8 @@ static int git_commit_graph_write_config(const char *var, const char *value,
{
if (!strcmp(var, "commitgraph.maxnewfilters"))
write_opts.max_new_filters = git_config_int(var, value, ctx->kvi);
+ else if (!strcmp(var, "commitgraph.changedpaths"))
+ opts.enable_changed_paths = git_config_bool(var, value) ? 1 : -1;
/*
* No need to fall-back to 'git_default_config', since this was already
* called in 'cmd_commit_graph()'.
diff --git a/builtin/commit.c b/builtin/commit.c
index 0243f17d53..78e8fb91f4 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -123,7 +123,7 @@ static const char *edit_message, *use_message;
static char *fixup_message, *fixup_commit, *squash_message;
static const char *fixup_prefix;
static int all, also, interactive, patch_interactive, only, amend, signoff;
-static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
+static struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
static int edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
@@ -356,9 +356,9 @@ static const char *prepare_index(const char **argv, const char *prefix,
const char *ret;
char *path = NULL;
- if (add_p_opt.context < -1)
+ if (interactive_opts.context < -1)
die(_("'%s' cannot be negative"), "--unified");
- if (add_p_opt.interhunkcontext < -1)
+ if (interactive_opts.interhunkcontext < -1)
die(_("'%s' cannot be negative"), "--inter-hunk-context");
if (is_status)
@@ -407,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
- if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0)
+ if (interactive_add(the_repository, argv, prefix, patch_interactive, &interactive_opts) != 0)
die(_("interactive add failed"));
the_repository->index_file = old_repo_index_file;
@@ -432,9 +432,9 @@ static const char *prepare_index(const char **argv, const char *prefix,
ret = get_lock_file_path(&index_lock);
goto out;
} else {
- if (add_p_opt.context != -1)
+ if (interactive_opts.context != -1)
die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
- if (add_p_opt.interhunkcontext != -1)
+ if (interactive_opts.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
}
@@ -1719,7 +1719,7 @@ int cmd_commit(int argc,
OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword 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_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
+ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, parse_opt_strvec),
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")),
@@ -1742,8 +1742,8 @@ int cmd_commit(int argc,
OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
- OPT_DIFF_UNIFIED(&add_p_opt.context),
- OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
+ OPT_DIFF_UNIFIED(&interactive_opts.context),
+ OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext),
OPT_BOOL('o', "only", &only, N_("commit only specified files")),
OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index f2f407c2a7..18f6e33b6f 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -122,7 +122,6 @@ int cmd_count_objects(int argc,
count_loose, count_cruft, NULL, NULL);
if (verbose) {
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
unsigned long num_pack = 0;
off_t size_pack = 0;
@@ -130,7 +129,7 @@ int cmd_count_objects(int argc,
struct strbuf pack_buf = STRBUF_INIT;
struct strbuf garbage_buf = STRBUF_INIT;
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (!p->pack_local)
continue;
if (open_pack_index(p))
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index b74e06cc93..bc1453c6b2 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -7,6 +7,7 @@
#include "path.h"
#include "string-list.h"
#include "parse-options.h"
+#include "url.h"
#include "write-or-die.h"
static struct lock_file credential_lock;
@@ -76,12 +77,6 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
die_errno("unable to write credential store");
}
-static int is_rfc3986_unreserved(char ch)
-{
- return isalnum(ch) ||
- ch == '-' || ch == '_' || ch == '.' || ch == '~';
-}
-
static int is_rfc3986_reserved_or_unreserved(char ch)
{
if (is_rfc3986_unreserved(ch))
diff --git a/builtin/describe.c b/builtin/describe.c
index ffaf8d9f0a..443546aaac 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -154,20 +154,19 @@ static void add_to_known_names(const char *path,
}
}
-static int get_name(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int get_name(const struct reference *ref, void *cb_data UNUSED)
{
int is_tag = 0;
struct object_id peeled;
int is_annotated, prio;
const char *path_to_match = NULL;
- if (skip_prefix(path, "refs/tags/", &path_to_match)) {
+ if (skip_prefix(ref->name, "refs/tags/", &path_to_match)) {
is_tag = 1;
} else if (all) {
if ((exclude_patterns.nr || patterns.nr) &&
- !skip_prefix(path, "refs/heads/", &path_to_match) &&
- !skip_prefix(path, "refs/remotes/", &path_to_match)) {
+ !skip_prefix(ref->name, "refs/heads/", &path_to_match) &&
+ !skip_prefix(ref->name, "refs/remotes/", &path_to_match)) {
/* Only accept reference of known type if there are match/exclude patterns */
return 0;
}
@@ -209,10 +208,10 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
}
/* Is it annotated? */
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
- is_annotated = !oideq(oid, &peeled);
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
+ is_annotated = !oideq(ref->oid, &peeled);
} else {
- oidcpy(&peeled, oid);
+ oidcpy(&peeled, ref->oid);
is_annotated = 0;
}
@@ -229,7 +228,8 @@ static int get_name(const char *path, const char *referent UNUSED, const struct
else
prio = 0;
- add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
+ add_to_known_names(all ? ref->name + 5 : ref->name + 10,
+ &peeled, prio, ref->oid);
return 0;
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index dc2486f9a8..0421360ab7 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -65,7 +65,7 @@ static int parse_opt_sign_mode(const struct option *opt,
return 0;
if (parse_sign_mode(arg, val))
- return error("Unknown %s mode: %s", opt->long_name, arg);
+ return error(_("unknown %s mode: %s"), opt->long_name, arg);
return 0;
}
@@ -82,7 +82,7 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt,
else if (!strcmp(arg, "rewrite"))
*val = REWRITE;
else
- return error("Unknown tag-of-filtered mode: %s", arg);
+ return error(_("unknown tag-of-filtered mode: %s"), arg);
return 0;
}
@@ -107,7 +107,7 @@ static int parse_opt_reencode_mode(const struct option *opt,
if (!strcasecmp(arg, "abort"))
*val = REENCODE_ABORT;
else
- return error("Unknown reencoding mode: %s", arg);
+ return error(_("unknown reencoding mode: %s"), arg);
}
return 0;
@@ -318,16 +318,16 @@ static void export_blob(const struct object_id *oid)
} else {
buf = odb_read_object(the_repository->objects, oid, &type, &size);
if (!buf)
- die("could not read blob %s", oid_to_hex(oid));
+ die(_("could not read blob %s"), oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
type) < 0)
- die("oid mismatch in blob %s", oid_to_hex(oid));
+ die(_("oid mismatch in blob %s"), oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
}
if (!object)
- die("Could not read blob %s", oid_to_hex(oid));
+ die(_("could not read blob %s"), oid_to_hex(oid));
mark_next_object(object);
@@ -336,7 +336,7 @@ static void export_blob(const struct object_id *oid)
printf("original-oid %s\n", oid_to_hex(oid));
printf("data %"PRIuMAX"\n", (uintmax_t)size);
if (size && fwrite(buf, size, 1, stdout) != 1)
- die_errno("could not write blob '%s'", oid_to_hex(oid));
+ die_errno(_("could not write blob '%s'"), oid_to_hex(oid));
printf("\n");
show_progress();
@@ -499,10 +499,10 @@ static void show_filemodify(struct diff_queue_struct *q,
break;
default:
- die("Unexpected comparison status '%c' for %s, %s",
- q->queue[i]->status,
- ospec->path ? ospec->path : "none",
- spec->path ? spec->path : "none");
+ die(_("unexpected comparison status '%c' for %s, %s"),
+ q->queue[i]->status,
+ ospec->path ? ospec->path : _("none"),
+ spec->path ? spec->path : _("none"));
}
}
}
@@ -699,14 +699,14 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
author = strstr(commit_buffer_cursor, "\nauthor ");
if (!author)
- die("could not find author in commit %s",
+ die(_("could not find author in commit %s"),
oid_to_hex(&commit->object.oid));
author++;
commit_buffer_cursor = author_end = strchrnul(author, '\n');
committer = strstr(commit_buffer_cursor, "\ncommitter ");
if (!committer)
- die("could not find committer in commit %s",
+ die(_("could not find committer in commit %s"),
oid_to_hex(&commit->object.oid));
committer++;
commit_buffer_cursor = committer_end = strchrnul(committer, '\n');
@@ -781,8 +781,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
case REENCODE_NO:
break;
case REENCODE_ABORT:
- die("Encountered commit-specific encoding %.*s in commit "
- "%s; use --reencode=[yes|no] to handle it",
+ die(_("encountered commit-specific encoding %.*s in commit "
+ "%s; use --reencode=[yes|no] to handle it"),
(int)encoding_len, encoding,
oid_to_hex(&commit->object.oid));
}
@@ -798,11 +798,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
if (signatures.nr) {
switch (signed_commit_mode) {
case SIGN_ABORT:
- die("encountered signed commit %s; use "
- "--signed-commits=<mode> to handle it",
+ die(_("encountered signed commit %s; use "
+ "--signed-commits=<mode> to handle it"),
oid_to_hex(&commit->object.oid));
case SIGN_WARN_VERBATIM:
- warning("exporting %"PRIuMAX" signature(s) for commit %s",
+ warning(_("exporting %"PRIuMAX" signature(s) for commit %s"),
(uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid));
/* fallthru */
case SIGN_VERBATIM:
@@ -812,7 +812,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
}
break;
case SIGN_WARN_STRIP:
- warning("stripping signature(s) from commit %s",
+ warning(_("stripping signature(s) from commit %s"),
oid_to_hex(&commit->object.oid));
/* fallthru */
case SIGN_STRIP:
@@ -890,7 +890,8 @@ static void handle_tag(const char *name, struct tag *tag)
tagged = ((struct tag *)tagged)->tagged;
}
if (tagged->type == OBJ_TREE) {
- warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
+ warning(_("omitting tag %s,\nsince tags of trees (or tags "
+ "of tags of trees, etc.) are not supported."),
oid_to_hex(&tag->object.oid));
return;
}
@@ -898,7 +899,7 @@ static void handle_tag(const char *name, struct tag *tag)
buf = odb_read_object(the_repository->objects, &tag->object.oid,
&type, &size);
if (!buf)
- die("could not read tag %s", oid_to_hex(&tag->object.oid));
+ die(_("could not read tag %s"), oid_to_hex(&tag->object.oid));
message = memmem(buf, size, "\n\n", 2);
if (message) {
message += 2;
@@ -931,26 +932,25 @@ static void handle_tag(const char *name, struct tag *tag)
/* handle signed tags */
if (message) {
- const char *signature = strstr(message,
- "\n-----BEGIN PGP SIGNATURE-----\n");
- if (signature)
+ size_t sig_offset = parse_signed_buffer(message, message_size);
+ if (sig_offset < message_size)
switch (signed_tag_mode) {
case SIGN_ABORT:
- die("encountered signed tag %s; use "
- "--signed-tags=<mode> to handle it",
+ die(_("encountered signed tag %s; use "
+ "--signed-tags=<mode> to handle it"),
oid_to_hex(&tag->object.oid));
case SIGN_WARN_VERBATIM:
- warning("exporting signed tag %s",
+ warning(_("exporting signed tag %s"),
oid_to_hex(&tag->object.oid));
/* fallthru */
case SIGN_VERBATIM:
break;
case SIGN_WARN_STRIP:
- warning("stripping signature from tag %s",
+ warning(_("stripping signature from tag %s"),
oid_to_hex(&tag->object.oid));
/* fallthru */
case SIGN_STRIP:
- message_size = signature + 1 - message;
+ message_size = sig_offset;
break;
}
}
@@ -961,8 +961,8 @@ static void handle_tag(const char *name, struct tag *tag)
if (!tagged_mark) {
switch (tag_of_filtered_mode) {
case TAG_FILTERING_ABORT:
- die("tag %s tags unexported object; use "
- "--tag-of-filtered-object=<mode> to handle it",
+ die(_("tag %s tags unexported object; use "
+ "--tag-of-filtered-object=<mode> to handle it"),
oid_to_hex(&tag->object.oid));
case DROP:
/* Ignore this tag altogether */
@@ -970,7 +970,7 @@ static void handle_tag(const char *name, struct tag *tag)
return;
case REWRITE:
if (tagged->type == OBJ_TAG && !mark_tags) {
- die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
+ die(_("cannot export nested tags unless --mark-tags is specified."));
} else if (tagged->type == OBJ_COMMIT) {
p = rewrite_commit((struct commit *)tagged);
if (!p) {
@@ -1026,7 +1026,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, const char *full_n
tag = (struct tag *)tag->tagged;
}
if (!tag)
- die("Tag %s points nowhere?", e->name);
+ die(_("tag %s points nowhere?"), e->name);
return (struct commit *)tag;
}
default:
@@ -1064,7 +1064,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
commit = get_commit(e, full_name);
if (!commit) {
- warning("%s: Unexpected object of type %s, skipping.",
+ warning(_("%s: unexpected object of type %s, skipping."),
e->name,
type_name(e->item->type));
free(full_name);
@@ -1079,7 +1079,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
free(full_name);
continue;
default: /* OBJ_TAG (nested tags) is already handled */
- warning("Tag points to object of unexpected type %s, skipping.",
+ warning(_("tag points to object of unexpected type %s, skipping."),
type_name(commit->object.type));
free(full_name);
continue;
@@ -1175,7 +1175,7 @@ static void export_marks(char *file)
f = fopen_for_writing(file);
if (!f)
- die_errno("Unable to open marks file %s for writing.", file);
+ die_errno(_("unable to open marks file %s for writing."), file);
for (i = 0; i < idnums.size; i++) {
if (deco->base && deco->base->type == 1) {
@@ -1192,7 +1192,7 @@ static void export_marks(char *file)
e |= ferror(f);
e |= fclose(f);
if (e)
- error("Unable to write marks file %s.", file);
+ error(_("unable to write marks file %s."), file);
}
static void import_marks(char *input_file, int check_exists)
@@ -1215,20 +1215,20 @@ static void import_marks(char *input_file, int check_exists)
line_end = strchr(line, '\n');
if (line[0] != ':' || !line_end)
- die("corrupt mark line: %s", line);
+ die(_("corrupt mark line: %s"), line);
*line_end = '\0';
mark = strtoumax(line + 1, &mark_end, 10);
if (!mark || mark_end == line + 1
|| *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
- die("corrupt mark line: %s", line);
+ die(_("corrupt mark line: %s"), line);
if (last_idnum < mark)
last_idnum = mark;
type = odb_read_object_info(the_repository->objects, &oid, NULL);
if (type < 0)
- die("object not found: %s", oid_to_hex(&oid));
+ die(_("object not found: %s"), oid_to_hex(&oid));
if (type != OBJ_COMMIT)
/* only commits */
@@ -1236,12 +1236,12 @@ static void import_marks(char *input_file, int check_exists)
commit = lookup_commit(the_repository, &oid);
if (!commit)
- die("not a commit? can't happen: %s", oid_to_hex(&oid));
+ die(_("not a commit? can't happen: %s"), oid_to_hex(&oid));
object = &commit->object;
if (object->flags & SHOWN)
- error("Object %s already has a mark", oid_to_hex(&oid));
+ error(_("object %s already has a mark"), oid_to_hex(&oid));
mark_object(object, mark);
@@ -1395,7 +1395,7 @@ int cmd_fast_export(int argc,
get_tags_and_duplicates(&revs.cmdline);
if (prepare_revision_walk(&revs))
- die("revision walk setup failed");
+ die(_("revision walk setup failed"));
revs.reverse = 1;
revs.diffopt.format_callback = show_filemodify;
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 606c6aea82..4cd0b079b6 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -188,6 +188,7 @@ static int global_argc;
static const char **global_argv;
static const char *global_prefix;
+static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
/* Memory pools */
@@ -338,12 +339,12 @@ static void write_crash_report(const char *err)
struct recent_command *rc;
if (!rpt) {
- error_errno("can't write crash report %s", loc);
+ error_errno(_("can't write crash report %s"), loc);
free(loc);
return;
}
- fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
+ fprintf(stderr, _("fast-import: dumping crash report to %s\n"), loc);
fprintf(rpt, "fast-import crash report:\n");
fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
@@ -587,7 +588,7 @@ static void *find_mark(struct mark_set *s, uintmax_t idnum)
oe = s->data.marked[idnum];
}
if (!oe)
- die("mark :%" PRIuMAX " not declared", orig_idnum);
+ die(_("mark :%" PRIuMAX " not declared"), orig_idnum);
return oe;
}
@@ -627,9 +628,9 @@ static struct branch *new_branch(const char *name)
struct branch *b = lookup_branch(name);
if (b)
- die("Invalid attempt to create duplicate branch: %s", name);
+ die(_("invalid attempt to create duplicate branch: %s"), name);
if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL))
- die("Branch name doesn't conform to GIT standards: %s", name);
+ die(_("branch name doesn't conform to Git standards: %s"), name);
b = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct branch));
b->name = mem_pool_strdup(&fi_mem_pool, name);
@@ -800,7 +801,7 @@ static const char *create_index(void)
*c++ = &e->idx;
last = idx + object_count;
if (c != last)
- die("internal consistency error creating the index");
+ die(_("internal consistency error creating the index"));
tmpfile = write_idx_file(the_repository, NULL, idx, object_count,
&pack_idx_opts, pack_data->hash);
@@ -818,18 +819,18 @@ static char *keep_pack(const char *curr_index_name)
keep_fd = safe_create_file_with_leading_directories(pack_data->repo,
name.buf);
if (keep_fd < 0)
- die_errno("cannot create keep file");
+ die_errno(_("cannot create keep file"));
write_or_die(keep_fd, keep_msg, strlen(keep_msg));
if (close(keep_fd))
- die_errno("failed to write keep file");
+ die_errno(_("failed to write keep file"));
odb_pack_name(pack_data->repo, &name, pack_data->hash, "pack");
if (finalize_object_file(pack_data->repo, pack_data->pack_name, name.buf))
- die("cannot store pack file");
+ die(_("cannot store pack file"));
odb_pack_name(pack_data->repo, &name, pack_data->hash, "idx");
if (finalize_object_file(pack_data->repo, curr_index_name, name.buf))
- die("cannot store index file");
+ die(_("cannot store index file"));
free((void *)curr_index_name);
return strbuf_detach(&name, NULL);
}
@@ -852,7 +853,7 @@ static int loosen_small_pack(const struct packed_git *p)
struct child_process unpack = CHILD_PROCESS_INIT;
if (lseek(p->pack_fd, 0, SEEK_SET) < 0)
- die_errno("Failed seeking to start of '%s'", p->pack_name);
+ die_errno(_("failed seeking to start of '%s'"), p->pack_name);
unpack.in = p->pack_fd;
unpack.git_cmd = 1;
@@ -902,7 +903,7 @@ static void end_packfile(void)
new_p = packfile_store_load_pack(pack_data->repo->objects->packfiles,
idx_name, 1);
if (!new_p)
- die("core git rejected index %s", idx_name);
+ die(_("core Git rejected index %s"), idx_name);
all_packs[pack_id] = new_p;
free(idx_name);
@@ -978,7 +979,7 @@ static int store_object(
if (e->idx.offset) {
duplicate_count_by_type[type]++;
return 1;
- } else if (find_oid_pack(&oid, packfile_store_get_all_packs(packs))) {
+ } else if (packfile_list_find_oid(packfile_store_get_packs(packs), &oid)) {
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -1089,7 +1090,7 @@ static int store_object(
static void truncate_pack(struct hashfile_checkpoint *checkpoint)
{
if (hashfile_truncate(pack_file, checkpoint))
- die_errno("cannot truncate pack to skip duplicate");
+ die_errno(_("cannot truncate pack to skip duplicate"));
pack_size = checkpoint->offset;
}
@@ -1137,7 +1138,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
size_t cnt = in_sz < len ? in_sz : (size_t)len;
size_t n = fread(in_buf, 1, cnt, stdin);
if (!n && feof(stdin))
- die("EOF in data (%" PRIuMAX " bytes remaining)", len);
+ die(_("EOF in data (%" PRIuMAX " bytes remaining)"), len);
git_hash_update(&c, in_buf, n);
s.next_in = in_buf;
@@ -1161,7 +1162,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
case Z_STREAM_END:
continue;
default:
- die("unexpected deflate failure: %d", status);
+ die(_("unexpected deflate failure: %d"), status);
}
}
git_deflate_end(&s);
@@ -1179,7 +1180,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
duplicate_count_by_type[OBJ_BLOB]++;
truncate_pack(&checkpoint);
- } else if (find_oid_pack(&oid, packfile_store_get_all_packs(packs))) {
+ } else if (packfile_list_find_oid(packfile_store_get_packs(packs), &oid)) {
e->type = OBJ_BLOB;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -1263,16 +1264,16 @@ static void load_tree(struct tree_entry *root)
myoe = find_object(oid);
if (myoe && myoe->pack_id != MAX_PACK_ID) {
if (myoe->type != OBJ_TREE)
- die("Not a tree: %s", oid_to_hex(oid));
+ die(_("not a tree: %s"), oid_to_hex(oid));
t->delta_depth = myoe->depth;
buf = gfi_unpack_entry(myoe, &size);
if (!buf)
- die("Can't load tree %s", oid_to_hex(oid));
+ die(_("can't load tree %s"), oid_to_hex(oid));
} else {
enum object_type type;
buf = odb_read_object(the_repository->objects, oid, &type, &size);
if (!buf || type != OBJ_TREE)
- die("Can't load tree %s", oid_to_hex(oid));
+ die(_("can't load tree %s"), oid_to_hex(oid));
}
c = buf;
@@ -1286,7 +1287,7 @@ static void load_tree(struct tree_entry *root)
e->tree = NULL;
c = parse_mode(c, &e->versions[1].mode);
if (!c)
- die("Corrupt mode in %s", oid_to_hex(oid));
+ die(_("corrupt mode in %s"), oid_to_hex(oid));
e->versions[0].mode = e->versions[1].mode;
e->name = to_atom(c, strlen(c));
c += e->name->str_len + 1;
@@ -1398,7 +1399,7 @@ static void tree_content_replace(
struct tree_content *newtree)
{
if (!S_ISDIR(mode))
- die("Root cannot be a non-directory");
+ die(_("root cannot be a non-directory"));
oidclr(&root->versions[0].oid, the_repository->hash_algo);
oidcpy(&root->versions[1].oid, oid);
if (root->tree)
@@ -1421,9 +1422,9 @@ static int tree_content_set(
slash1 = strchrnul(p, '/');
n = slash1 - p;
if (!n)
- die("Empty path component found in input");
+ die(_("empty path component found in input"));
if (!*slash1 && !S_ISDIR(mode) && subtree)
- die("Non-directories cannot have subtrees");
+ die(_("non-directories cannot have subtrees"));
if (!root->tree)
load_tree(root);
@@ -1575,7 +1576,7 @@ static int tree_content_get(
slash1 = strchrnul(p, '/');
n = slash1 - p;
if (!n && !allow_root)
- die("Empty path component found in input");
+ die(_("empty path component found in input"));
if (!root->tree)
load_tree(root);
@@ -1621,8 +1622,8 @@ static int update_branch(struct branch *b)
!strcmp(b->name + strlen(replace_prefix),
oid_to_hex(&b->oid))) {
if (!quiet)
- warning("Dropping %s since it would point to "
- "itself (i.e. to %s)",
+ warning(_("dropping %s since it would point to "
+ "itself (i.e. to %s)"),
b->name, oid_to_hex(&b->oid));
refs_delete_ref(get_main_ref_store(the_repository),
NULL, b->name, NULL, 0);
@@ -1645,14 +1646,14 @@ static int update_branch(struct branch *b)
new_cmit = lookup_commit_reference_gently(the_repository,
&b->oid, 0);
if (!old_cmit || !new_cmit)
- return error("Branch %s is missing commits.", b->name);
+ return error(_("branch %s is missing commits."), b->name);
ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit);
if (ret < 0)
exit(128);
if (!ret) {
- warning("Not updating %s"
- " (new tip %s does not contain %s)",
+ warning(_("not updating %s"
+ " (new tip %s does not contain %s)"),
b->name, oid_to_hex(&b->oid),
oid_to_hex(&old_oid));
return -1;
@@ -1728,13 +1729,13 @@ static void dump_marks(void)
return;
if (safe_create_leading_directories_const(the_repository, export_marks_file)) {
- failure |= error_errno("unable to create leading directories of %s",
+ failure |= error_errno(_("unable to create leading directories of %s"),
export_marks_file);
return;
}
if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
- failure |= error_errno("Unable to write marks file %s",
+ failure |= error_errno(_("unable to write marks file %s"),
export_marks_file);
return;
}
@@ -1743,14 +1744,14 @@ static void dump_marks(void)
if (!f) {
int saved_errno = errno;
rollback_lock_file(&mark_lock);
- failure |= error("Unable to write marks file %s: %s",
+ failure |= error(_("unable to write marks file %s: %s"),
export_marks_file, strerror(saved_errno));
return;
}
for_each_mark(marks, 0, dump_marks_fn, f);
if (commit_lock_file(&mark_lock)) {
- failure |= error_errno("Unable to write file %s",
+ failure |= error_errno(_("unable to write file %s"),
export_marks_file);
return;
}
@@ -1764,7 +1765,7 @@ static void insert_object_entry(struct mark_set **s, struct object_id *oid, uint
enum object_type type = odb_read_object_info(the_repository->objects,
oid, NULL);
if (type < 0)
- die("object not found: %s", oid_to_hex(oid));
+ die(_("object not found: %s"), oid_to_hex(oid));
e = insert_object(oid);
e->type = type;
e->pack_id = MAX_PACK_ID;
@@ -1791,13 +1792,13 @@ static void read_mark_file(struct mark_set **s, FILE *f, mark_set_inserter_t ins
end = strchr(line, '\n');
if (line[0] != ':' || !end)
- die("corrupt mark line: %s", line);
+ die(_("corrupt mark line: %s"), line);
*end = 0;
mark = strtoumax(line + 1, &end, 10);
if (!mark || end == line + 1
|| *end != ' '
|| get_oid_hex_any(end + 1, &oid) == GIT_HASH_UNKNOWN)
- die("corrupt mark line: %s", line);
+ die(_("corrupt mark line: %s"), line);
inserter(s, &oid, mark);
}
}
@@ -1810,7 +1811,7 @@ static void read_marks(void)
else if (import_marks_file_ignore_missing && errno == ENOENT)
goto done; /* Marks file does not exist */
else
- die_errno("cannot read '%s'", import_marks_file);
+ die_errno(_("cannot read '%s'"), import_marks_file);
read_mark_file(&marks, f, insert_object_entry);
fclose(f);
done:
@@ -1896,7 +1897,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
strbuf_reset(sb);
if (!skip_prefix(command_buf.buf, "data ", &data))
- die("Expected 'data n' command, found: %s", command_buf.buf);
+ die(_("expected 'data n' command, found: %s"), command_buf.buf);
if (skip_prefix(data, "<<", &data)) {
char *term = xstrdup(data);
@@ -1904,7 +1905,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
for (;;) {
if (strbuf_getline_lf(&command_buf, stdin) == EOF)
- die("EOF in data (terminator '%s' not found)", term);
+ die(_("EOF in data (terminator '%s' not found)"), term);
if (term_len == command_buf.len
&& !strcmp(term, command_buf.buf))
break;
@@ -1922,12 +1923,12 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
return 0;
}
if (length < len)
- die("data is too large to use in this context");
+ die(_("data is too large to use in this context"));
while (n < length) {
size_t s = strbuf_fread(sb, length - n, stdin);
if (!s && feof(stdin))
- die("EOF in data (%lu bytes remaining)",
+ die(_("EOF in data (%lu bytes remaining)"),
(unsigned long)(length - n));
n += s;
}
@@ -1984,15 +1985,15 @@ static char *parse_ident(const char *buf)
ltgt = buf + strcspn(buf, "<>");
if (*ltgt != '<')
- die("Missing < in ident string: %s", buf);
+ die(_("missing < in ident string: %s"), buf);
if (ltgt != buf && ltgt[-1] != ' ')
- die("Missing space before < in ident string: %s", buf);
+ die(_("missing space before < in ident string: %s"), buf);
ltgt = ltgt + 1 + strcspn(ltgt + 1, "<>");
if (*ltgt != '>')
- die("Missing > in ident string: %s", buf);
+ die(_("missing > in ident string: %s"), buf);
ltgt++;
if (*ltgt != ' ')
- die("Missing space after > in ident string: %s", buf);
+ die(_("missing space after > in ident string: %s"), buf);
ltgt++;
name_len = ltgt - buf;
strbuf_add(&ident, buf, name_len);
@@ -2000,19 +2001,19 @@ static char *parse_ident(const char *buf)
switch (whenspec) {
case WHENSPEC_RAW:
if (validate_raw_date(ltgt, &ident, 1) < 0)
- die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
+ die(_("invalid raw date \"%s\" in ident: %s"), ltgt, buf);
break;
case WHENSPEC_RAW_PERMISSIVE:
if (validate_raw_date(ltgt, &ident, 0) < 0)
- die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
+ die(_("invalid raw date \"%s\" in ident: %s"), ltgt, buf);
break;
case WHENSPEC_RFC2822:
if (parse_date(ltgt, &ident) < 0)
- die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
+ die(_("invalid rfc2822 date \"%s\" in ident: %s"), ltgt, buf);
break;
case WHENSPEC_NOW:
if (strcmp("now", ltgt))
- die("Date in ident must be 'now': %s", buf);
+ die(_("date in ident must be 'now': %s"), buf);
datestamp(&ident);
break;
}
@@ -2106,7 +2107,7 @@ static void construct_path_with_fanout(const char *hex_sha1,
{
unsigned int i = 0, j = 0;
if (fanout >= the_hash_algo->rawsz)
- die("Too large fanout (%u)", fanout);
+ die(_("too large fanout (%u)"), fanout);
while (fanout) {
path[i++] = hex_sha1[j++];
path[i++] = hex_sha1[j++];
@@ -2180,7 +2181,7 @@ static uintmax_t do_change_note_fanout(
/* Rename fullpath to realpath */
if (!tree_content_remove(orig_root, fullpath, &leaf, 0))
- die("Failed to remove path %s", fullpath);
+ die(_("failed to remove path %s"), fullpath);
tree_content_set(orig_root, realpath,
&leaf.versions[1].oid,
leaf.versions[1].mode,
@@ -2253,7 +2254,7 @@ static uintmax_t parse_mark_ref(const char *p, char **endptr)
p++;
mark = strtoumax(p, endptr, 10);
if (*endptr == p)
- die("No value after ':' in mark: %s", command_buf.buf);
+ die(_("no value after ':' in mark: %s"), command_buf.buf);
return mark;
}
@@ -2268,7 +2269,7 @@ static uintmax_t parse_mark_ref_eol(const char *p)
mark = parse_mark_ref(p, &end);
if (*end != '\0')
- die("Garbage after mark: %s", command_buf.buf);
+ die(_("garbage after mark: %s"), command_buf.buf);
return mark;
}
@@ -2283,7 +2284,7 @@ static uintmax_t parse_mark_ref_space(const char **p)
mark = parse_mark_ref(*p, &end);
if (*end++ != ' ')
- die("Missing space after mark: %s", command_buf.buf);
+ die(_("missing space after mark: %s"), command_buf.buf);
*p = end;
return mark;
}
@@ -2299,9 +2300,9 @@ static void parse_path(struct strbuf *sb, const char *p, const char **endp,
{
if (*p == '"') {
if (unquote_c_style(sb, p, endp))
- die("Invalid %s: %s", field, command_buf.buf);
+ die(_("invalid %s: %s"), field, command_buf.buf);
if (strlen(sb->buf) != sb->len)
- die("NUL in %s: %s", field, command_buf.buf);
+ die(_("NUL in %s: %s"), field, command_buf.buf);
} else {
/*
* Unless we are parsing the last field of a line,
@@ -2324,7 +2325,7 @@ static void parse_path_eol(struct strbuf *sb, const char *p, const char *field)
parse_path(sb, p, &end, 1, field);
if (*end)
- die("Garbage after %s: %s", field, command_buf.buf);
+ die(_("garbage after %s: %s"), field, command_buf.buf);
}
/*
@@ -2337,7 +2338,7 @@ static void parse_path_space(struct strbuf *sb, const char *p,
{
parse_path(sb, p, endp, 0, field);
if (**endp != ' ')
- die("Missing space after %s: %s", field, command_buf.buf);
+ die(_("missing space after %s: %s"), field, command_buf.buf);
(*endp)++;
}
@@ -2350,7 +2351,7 @@ static void file_change_m(const char *p, struct branch *b)
p = parse_mode(p, &mode);
if (!p)
- die("Corrupt mode: %s", command_buf.buf);
+ die(_("corrupt mode: %s"), command_buf.buf);
switch (mode) {
case 0644:
case 0755:
@@ -2363,7 +2364,7 @@ static void file_change_m(const char *p, struct branch *b)
/* ok */
break;
default:
- die("Corrupt mode: %s", command_buf.buf);
+ die(_("corrupt mode: %s"), command_buf.buf);
}
if (*p == ':') {
@@ -2374,10 +2375,10 @@ static void file_change_m(const char *p, struct branch *b)
oe = NULL; /* not used with inline_data, but makes gcc happy */
} else {
if (parse_mapped_oid_hex(p, &oid, &p))
- die("Invalid dataref: %s", command_buf.buf);
+ die(_("invalid dataref: %s"), command_buf.buf);
oe = find_object(&oid);
if (*p++ != ' ')
- die("Missing space after SHA1: %s", command_buf.buf);
+ die(_("missing space after SHA1: %s"), command_buf.buf);
}
strbuf_reset(&path);
@@ -2393,11 +2394,11 @@ static void file_change_m(const char *p, struct branch *b)
if (S_ISGITLINK(mode)) {
if (inline_data)
- die("Git links cannot be specified 'inline': %s",
+ die(_("Git links cannot be specified 'inline': %s"),
command_buf.buf);
else if (oe) {
if (oe->type != OBJ_COMMIT)
- die("Not a commit (actually a %s): %s",
+ die(_("not a commit (actually a %s): %s"),
type_name(oe->type), command_buf.buf);
}
/*
@@ -2406,7 +2407,7 @@ static void file_change_m(const char *p, struct branch *b)
*/
} else if (inline_data) {
if (S_ISDIR(mode))
- die("Directories cannot be specified 'inline': %s",
+ die(_("directories cannot be specified 'inline': %s"),
command_buf.buf);
while (read_next_command() != EOF) {
const char *v;
@@ -2424,11 +2425,11 @@ static void file_change_m(const char *p, struct branch *b)
odb_read_object_info(the_repository->objects,
&oid, NULL);
if (type < 0)
- die("%s not found: %s",
- S_ISDIR(mode) ? "Tree" : "Blob",
- command_buf.buf);
+ die(_("%s not found: %s"),
+ S_ISDIR(mode) ? _("tree") : _("blob"),
+ command_buf.buf);
if (type != expected)
- die("Not a %s (actually a %s): %s",
+ die(_("not a %s (actually a %s): %s"),
type_name(expected), type_name(type),
command_buf.buf);
}
@@ -2439,7 +2440,7 @@ static void file_change_m(const char *p, struct branch *b)
}
if (!verify_path(path.buf, mode))
- die("invalid path '%s'", path.buf);
+ die(_("invalid path '%s'"), path.buf);
tree_content_set(&b->branch_tree, path.buf, &oid, mode, NULL);
}
@@ -2469,7 +2470,7 @@ static void file_change_cr(const char *p, struct branch *b, int rename)
else
tree_content_get(&b->branch_tree, source.buf, &leaf, 1);
if (!leaf.versions[1].mode)
- die("Path %s not in branch", source.buf);
+ die(_("path %s not in branch"), source.buf);
if (!*dest.buf) { /* C "path/to/subdir" "" */
tree_content_replace(&b->branch_tree,
&leaf.versions[1].oid,
@@ -2478,7 +2479,7 @@ static void file_change_cr(const char *p, struct branch *b, int rename)
return;
}
if (!verify_path(dest.buf, leaf.versions[1].mode))
- die("invalid path '%s'", dest.buf);
+ die(_("invalid path '%s'"), dest.buf);
tree_content_set(&b->branch_tree, dest.buf,
&leaf.versions[1].oid,
leaf.versions[1].mode,
@@ -2520,23 +2521,23 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
oe = NULL; /* not used with inline_data, but makes gcc happy */
} else {
if (parse_mapped_oid_hex(p, &oid, &p))
- die("Invalid dataref: %s", command_buf.buf);
+ die(_("invalid dataref: %s"), command_buf.buf);
oe = find_object(&oid);
if (*p++ != ' ')
- die("Missing space after SHA1: %s", command_buf.buf);
+ die(_("missing space after SHA1: %s"), command_buf.buf);
}
/* <commit-ish> */
s = lookup_branch(p);
if (s) {
if (is_null_oid(&s->oid))
- die("Can't add a note on empty branch.");
+ die(_("can't add a note on empty branch."));
oidcpy(&commit_oid, &s->oid);
} else if (*p == ':') {
uintmax_t commit_mark = parse_mark_ref_eol(p);
struct object_entry *commit_oe = find_mark(marks, commit_mark);
if (commit_oe->type != OBJ_COMMIT)
- die("Mark :%" PRIuMAX " not a commit", commit_mark);
+ die(_("mark :%" PRIuMAX " not a commit"), commit_mark);
oidcpy(&commit_oid, &commit_oe->idx.oid);
} else if (!repo_get_oid(the_repository, p, &commit_oid)) {
unsigned long size;
@@ -2544,25 +2545,25 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
&commit_oid, OBJ_COMMIT, &size,
&commit_oid);
if (!buf || size < the_hash_algo->hexsz + 6)
- die("Not a valid commit: %s", p);
+ die(_("not a valid commit: %s"), p);
free(buf);
} else
- die("Invalid ref name or SHA1 expression: %s", p);
+ die(_("invalid ref name or SHA1 expression: %s"), p);
if (inline_data) {
read_next_command();
parse_and_store_blob(&last_blob, &oid, 0);
} else if (oe) {
if (oe->type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
+ die(_("not a blob (actually a %s): %s"),
type_name(oe->type), command_buf.buf);
} else if (!is_null_oid(&oid)) {
enum object_type type = odb_read_object_info(the_repository->objects, &oid,
NULL);
if (type < 0)
- die("Blob not found: %s", command_buf.buf);
+ die(_("blob not found: %s"), command_buf.buf);
if (type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
+ die(_("not a blob (actually a %s): %s"),
type_name(type), command_buf.buf);
}
@@ -2591,10 +2592,10 @@ static void file_change_deleteall(struct branch *b)
static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
{
if (!buf || size < the_hash_algo->hexsz + 6)
- die("Not a valid commit: %s", oid_to_hex(&b->oid));
+ die(_("not a valid commit: %s"), oid_to_hex(&b->oid));
if (memcmp("tree ", buf, 5)
|| get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid))
- die("The commit %s is corrupt", oid_to_hex(&b->oid));
+ die(_("the commit %s is corrupt"), oid_to_hex(&b->oid));
oidcpy(&b->branch_tree.versions[0].oid,
&b->branch_tree.versions[1].oid);
}
@@ -2624,7 +2625,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
s = lookup_branch(objectish);
if (b == s)
- die("Can't create a branch from itself: %s", b->name);
+ die(_("can't create a branch from itself: %s"), b->name);
else if (s) {
struct object_id *t = &s->branch_tree.versions[1].oid;
oidcpy(&b->oid, &s->oid);
@@ -2634,7 +2635,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
uintmax_t idnum = parse_mark_ref_eol(objectish);
struct object_entry *oe = find_mark(marks, idnum);
if (oe->type != OBJ_COMMIT)
- die("Mark :%" PRIuMAX " not a commit", idnum);
+ die(_("mark :%" PRIuMAX " not a commit"), idnum);
if (!oideq(&b->oid, &oe->idx.oid)) {
oidcpy(&b->oid, &oe->idx.oid);
if (oe->pack_id != MAX_PACK_ID) {
@@ -2651,7 +2652,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
b->delete = 1;
}
else
- die("Invalid ref name or SHA1 expression: %s", objectish);
+ die(_("invalid ref name or SHA1 expression: %s"), objectish);
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
release_tree_content_recursive(b->branch_tree.tree);
@@ -2698,7 +2699,7 @@ static struct hash_list *parse_merge(unsigned int *count)
uintmax_t idnum = parse_mark_ref_eol(from);
struct object_entry *oe = find_mark(marks, idnum);
if (oe->type != OBJ_COMMIT)
- die("Mark :%" PRIuMAX " not a commit", idnum);
+ die(_("mark :%" PRIuMAX " not a commit"), idnum);
oidcpy(&n->oid, &oe->idx.oid);
} else if (!repo_get_oid(the_repository, from, &n->oid)) {
unsigned long size;
@@ -2706,10 +2707,10 @@ static struct hash_list *parse_merge(unsigned int *count)
&n->oid, OBJ_COMMIT,
&size, &n->oid);
if (!buf || size < the_hash_algo->hexsz + 6)
- die("Not a valid commit: %s", from);
+ die(_("not a valid commit: %s"), from);
free(buf);
} else
- die("Invalid ref name or SHA1 expression: %s", from);
+ die(_("invalid ref name or SHA1 expression: %s"), from);
n->next = NULL;
*tail = n;
@@ -2733,8 +2734,8 @@ static void parse_one_signature(struct signature_data *sig, const char *v)
char *space = strchr(args, ' ');
if (!space)
- die("Expected gpgsig format: 'gpgsig <hash-algo> <signature-format>', "
- "got 'gpgsig %s'", args);
+ die(_("expected gpgsig format: 'gpgsig <hash-algo> <signature-format>', "
+ "got 'gpgsig %s'"), args);
*space = '\0';
sig->hash_algo = args;
@@ -2743,13 +2744,13 @@ static void parse_one_signature(struct signature_data *sig, const char *v)
/* Validate hash algorithm */
if (strcmp(sig->hash_algo, "sha1") &&
strcmp(sig->hash_algo, "sha256"))
- die("Unknown git hash algorithm in gpgsig: '%s'", sig->hash_algo);
+ die(_("unknown git hash algorithm in gpgsig: '%s'"), sig->hash_algo);
/* Validate signature format */
if (!valid_signature_format(sig->sig_format))
- die("Invalid signature format in gpgsig: '%s'", sig->sig_format);
+ die(_("invalid signature format in gpgsig: '%s'"), sig->sig_format);
if (!strcmp(sig->sig_format, "unknown"))
- warning("'unknown' signature format in gpgsig");
+ warning(_("'unknown' signature format in gpgsig"));
/* Read signature data */
read_next_command();
@@ -2788,8 +2789,8 @@ static void store_signature(struct signature_data *stored_sig,
const char *hash_type)
{
if (stored_sig->hash_algo) {
- warning("multiple %s signatures found, "
- "ignoring additional signature",
+ warning(_("multiple %s signatures found, "
+ "ignoring additional signature"),
hash_type);
strbuf_release(&new_sig->data);
free(new_sig->hash_algo);
@@ -2844,15 +2845,15 @@ static void parse_new_commit(const char *arg)
read_next_command();
}
if (!committer)
- die("Expected committer but didn't get one");
+ die(_("expected committer but didn't get one"));
while (skip_prefix(command_buf.buf, "gpgsig ", &v)) {
switch (signed_commit_mode) {
/* First, modes that don't need the signature to be parsed */
case SIGN_ABORT:
- die("encountered signed commit; use "
- "--signed-commits=<mode> to handle it");
+ die(_("encountered signed commit; use "
+ "--signed-commits=<mode> to handle it"));
case SIGN_WARN_STRIP:
warning(_("stripping a commit signature"));
/* fallthru */
@@ -2963,6 +2964,43 @@ static void parse_new_commit(const char *arg)
b->last_commit = object_count_by_type[OBJ_COMMIT];
}
+static void handle_tag_signature(struct strbuf *msg, const char *name)
+{
+ size_t sig_offset = parse_signed_buffer(msg->buf, msg->len);
+
+ /* If there is no signature, there is nothing to do. */
+ if (sig_offset >= msg->len)
+ return;
+
+ switch (signed_tag_mode) {
+
+ /* First, modes that don't change anything */
+ case SIGN_ABORT:
+ die(_("encountered signed tag; use "
+ "--signed-tags=<mode> to handle it"));
+ case SIGN_WARN_VERBATIM:
+ warning(_("importing a tag signature verbatim for tag '%s'"), name);
+ /* fallthru */
+ case SIGN_VERBATIM:
+ /* Nothing to do, the signature will be put into the imported tag. */
+ break;
+
+ /* Second, modes that remove the signature */
+ case SIGN_WARN_STRIP:
+ warning(_("stripping a tag signature for tag '%s'"), name);
+ /* fallthru */
+ case SIGN_STRIP:
+ /* Truncate the buffer to remove the signature */
+ strbuf_setlen(msg, sig_offset);
+ break;
+
+ /* Third, BUG */
+ default:
+ BUG("invalid signed_tag_mode value %d from tag '%s'",
+ signed_tag_mode, name);
+ }
+}
+
static void parse_new_tag(const char *arg)
{
static struct strbuf msg = STRBUF_INIT;
@@ -2987,11 +3025,11 @@ static void parse_new_tag(const char *arg)
/* from ... */
if (!skip_prefix(command_buf.buf, "from ", &from))
- die("Expected from command, got %s", command_buf.buf);
+ die(_("expected 'from' command, got '%s'"), command_buf.buf);
s = lookup_branch(from);
if (s) {
if (is_null_oid(&s->oid))
- die("Can't tag an empty branch.");
+ die(_("can't tag an empty branch."));
oidcpy(&oid, &s->oid);
type = OBJ_COMMIT;
} else if (*from == ':') {
@@ -3006,11 +3044,11 @@ static void parse_new_tag(const char *arg)
type = odb_read_object_info(the_repository->objects,
&oid, NULL);
if (type < 0)
- die("Not a valid object: %s", from);
+ die(_("not a valid object: %s"), from);
} else
type = oe->type;
} else
- die("Invalid ref name or SHA1 expression: %s", from);
+ die(_("invalid ref name or SHA1 expression: %s"), from);
read_next_command();
/* original-oid ... */
@@ -3026,6 +3064,8 @@ static void parse_new_tag(const char *arg)
/* tag payload/message */
parse_data(&msg, 0, NULL);
+ handle_tag_signature(&msg, t->name);
+
/* build the tag object */
strbuf_reset(&new_data);
@@ -3099,7 +3139,7 @@ static void parse_reset_branch(const char *arg)
static void cat_blob_write(const char *buf, unsigned long size)
{
if (write_in_full(cat_blob_fd, buf, size) < 0)
- die_errno("Write to frontend failed");
+ die_errno(_("write to frontend failed"));
}
static void cat_blob(struct object_entry *oe, struct object_id *oid)
@@ -3128,9 +3168,9 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid)
return;
}
if (!buf)
- die("Can't read object %s", oid_to_hex(oid));
+ die(_("can't read object %s"), oid_to_hex(oid));
if (type != OBJ_BLOB)
- die("Object %s is a %s but a blob was expected.",
+ die(_("object %s is a %s but a blob was expected."),
oid_to_hex(oid), type_name(type));
strbuf_reset(&line);
strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid),
@@ -3154,11 +3194,11 @@ static void parse_get_mark(const char *p)
/* get-mark SP <object> LF */
if (*p != ':')
- die("Not a mark: %s", p);
+ die(_("not a mark: %s"), p);
oe = find_mark(marks, parse_mark_ref_eol(p));
if (!oe)
- die("Unknown mark: %s", command_buf.buf);
+ die(_("unknown mark: %s"), command_buf.buf);
xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid));
cat_blob_write(output, the_hash_algo->hexsz + 1);
@@ -3173,13 +3213,13 @@ static void parse_cat_blob(const char *p)
if (*p == ':') {
oe = find_mark(marks, parse_mark_ref_eol(p));
if (!oe)
- die("Unknown mark: %s", command_buf.buf);
+ die(_("unknown mark: %s"), command_buf.buf);
oidcpy(&oid, &oe->idx.oid);
} else {
if (parse_mapped_oid_hex(p, &oid, &p))
- die("Invalid dataref: %s", command_buf.buf);
+ die(_("invalid dataref: %s"), command_buf.buf);
if (*p)
- die("Garbage after SHA1: %s", command_buf.buf);
+ die(_("garbage after SHA1: %s"), command_buf.buf);
oe = find_object(&oid);
}
@@ -3197,7 +3237,7 @@ static struct object_entry *dereference(struct object_entry *oe,
enum object_type type = odb_read_object_info(the_repository->objects,
oid, NULL);
if (type < 0)
- die("object not found: %s", oid_to_hex(oid));
+ die(_("object not found: %s"), oid_to_hex(oid));
/* cache it! */
oe = insert_object(oid);
oe->type = type;
@@ -3211,7 +3251,7 @@ static struct object_entry *dereference(struct object_entry *oe,
case OBJ_TAG:
break;
default:
- die("Not a tree-ish: %s", command_buf.buf);
+ die(_("not a tree-ish: %s"), command_buf.buf);
}
if (oe->pack_id != MAX_PACK_ID) { /* in a pack being written */
@@ -3222,19 +3262,19 @@ static struct object_entry *dereference(struct object_entry *oe,
&unused, &size);
}
if (!buf)
- die("Can't load object %s", oid_to_hex(oid));
+ die(_("can't load object %s"), oid_to_hex(oid));
/* Peel one layer. */
switch (oe->type) {
case OBJ_TAG:
if (size < hexsz + strlen("object ") ||
get_oid_hex(buf + strlen("object "), oid))
- die("Invalid SHA1 in tag: %s", command_buf.buf);
+ die(_("invalid SHA1 in tag: %s"), command_buf.buf);
break;
case OBJ_COMMIT:
if (size < hexsz + strlen("tree ") ||
get_oid_hex(buf + strlen("tree "), oid))
- die("Invalid SHA1 in commit: %s", command_buf.buf);
+ die(_("invalid SHA1 in commit: %s"), command_buf.buf);
}
free(buf);
@@ -3269,9 +3309,9 @@ static void build_mark_map(struct string_list *from, struct string_list *to)
for_each_string_list_item(fromp, from) {
top = string_list_lookup(to, fromp->string);
if (!fromp->util) {
- die(_("Missing from marks for submodule '%s'"), fromp->string);
+ die(_("missing from marks for submodule '%s'"), fromp->string);
} else if (!top || !top->util) {
- die(_("Missing to marks for submodule '%s'"), fromp->string);
+ die(_("missing to marks for submodule '%s'"), fromp->string);
}
build_mark_map_one(fromp->util, top->util);
}
@@ -3285,14 +3325,14 @@ static struct object_entry *parse_treeish_dataref(const char **p)
if (**p == ':') { /* <mark> */
e = find_mark(marks, parse_mark_ref_space(p));
if (!e)
- die("Unknown mark: %s", command_buf.buf);
+ die(_("unknown mark: %s"), command_buf.buf);
oidcpy(&oid, &e->idx.oid);
} else { /* <sha1> */
if (parse_mapped_oid_hex(*p, &oid, p))
- die("Invalid dataref: %s", command_buf.buf);
+ die(_("invalid dataref: %s"), command_buf.buf);
e = find_object(&oid);
if (*(*p)++ != ' ')
- die("Missing space after tree-ish: %s", command_buf.buf);
+ die(_("missing space after tree-ish: %s"), command_buf.buf);
}
while (!e || e->type != OBJ_TREE)
@@ -3336,7 +3376,7 @@ static void parse_ls(const char *p, struct branch *b)
/* ls SP (<tree-ish> SP)? <path> */
if (*p == '"') {
if (!b)
- die("Not in a commit: %s", command_buf.buf);
+ die(_("not in a commit: %s"), command_buf.buf);
root = &b->branch_tree;
} else {
struct object_entry *e = parse_treeish_dataref(&p);
@@ -3399,12 +3439,12 @@ static void parse_alias(void)
/* mark ... */
parse_mark();
if (!next_mark)
- die(_("Expected 'mark' command, got %s"), command_buf.buf);
+ die(_("expected 'mark' command, got %s"), command_buf.buf);
/* to ... */
memset(&b, 0, sizeof(b));
if (!parse_objectish_with_prefix(&b, "to "))
- die(_("Expected 'to' command, got %s"), command_buf.buf);
+ die(_("expected 'to' command, got %s"), command_buf.buf);
e = find_object(&b.oid);
assert(e);
insert_mark(&marks, next_mark, e);
@@ -3422,7 +3462,7 @@ static void option_import_marks(const char *marks,
{
if (import_marks_file) {
if (from_stream)
- die("Only one import-marks command allowed per stream");
+ die(_("only one import-marks command allowed per stream"));
/* read previous mark file */
if(!import_marks_file_from_stream)
@@ -3446,7 +3486,7 @@ static void option_date_format(const char *fmt)
else if (!strcmp(fmt, "now"))
whenspec = WHENSPEC_NOW;
else
- die("unknown --date-format argument %s", fmt);
+ die(_("unknown --date-format argument %s"), fmt);
}
static unsigned long ulong_arg(const char *option, const char *arg)
@@ -3454,7 +3494,7 @@ static unsigned long ulong_arg(const char *option, const char *arg)
char *endptr;
unsigned long rv = strtoul(arg, &endptr, 0);
if (strchr(arg, '-') || endptr == arg || *endptr)
- die("%s: argument must be a non-negative integer", option);
+ die(_("%s: argument must be a non-negative integer"), option);
return rv;
}
@@ -3462,7 +3502,7 @@ static void option_depth(const char *depth)
{
max_depth = ulong_arg("--depth", depth);
if (max_depth > MAX_DEPTH)
- die("--depth cannot exceed %u", MAX_DEPTH);
+ die(_("--depth cannot exceed %u"), MAX_DEPTH);
}
static void option_active_branches(const char *branches)
@@ -3480,7 +3520,7 @@ static void option_cat_blob_fd(const char *fd)
{
unsigned long n = ulong_arg("--cat-blob-fd", fd);
if (n > (unsigned long) INT_MAX)
- die("--cat-blob-fd cannot exceed %d", INT_MAX);
+ die(_("--cat-blob-fd cannot exceed %d"), INT_MAX);
cat_blob_fd = (int) n;
}
@@ -3500,7 +3540,7 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
char *s = xstrdup(arg);
char *f = strchr(s, ':');
if (!f)
- die(_("Expected format name:filename for submodule rewrite option"));
+ die(_("expected format name:filename for submodule rewrite option"));
*f = '\0';
f++;
CALLOC_ARRAY(ms, 1);
@@ -3508,7 +3548,7 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
f = prefix_filename(global_prefix, f);
fp = fopen(f, "r");
if (!fp)
- die_errno("cannot read '%s'", f);
+ die_errno(_("cannot read '%s'"), f);
read_mark_file(&ms, fp, insert_oid_entry);
fclose(fp);
free(f);
@@ -3525,10 +3565,10 @@ static int parse_one_option(const char *option)
if (!git_parse_ulong(option, &v))
return 0;
if (v < 8192) {
- warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
+ warning(_("max-pack-size is now in bytes, assuming --max-pack-size=%lum"), v);
v *= 1024 * 1024;
} else if (v < 1024 * 1024) {
- warning("minimum max-pack-size is 1 MiB");
+ warning(_("minimum max-pack-size is 1 MiB"));
v = 1024 * 1024;
}
max_packsize = v;
@@ -3546,6 +3586,9 @@ static int parse_one_option(const char *option)
} else if (skip_prefix(option, "signed-commits=", &option)) {
if (parse_sign_mode(option, &signed_commit_mode))
usagef(_("unknown --signed-commits mode '%s'"), option);
+ } else if (skip_prefix(option, "signed-tags=", &option)) {
+ if (parse_sign_mode(option, &signed_tag_mode))
+ usagef(_("unknown --signed-tags mode '%s'"), option);
} else if (!strcmp(option, "quiet")) {
show_stats = 0;
quiet = 1;
@@ -3612,23 +3655,23 @@ static int parse_one_feature(const char *feature, int from_stream)
static void parse_feature(const char *feature)
{
if (seen_data_command)
- die("Got feature command '%s' after data command", feature);
+ die(_("got feature command '%s' after data command"), feature);
if (parse_one_feature(feature, 1))
return;
- die("This version of fast-import does not support feature %s.", feature);
+ die(_("this version of fast-import does not support feature %s."), feature);
}
static void parse_option(const char *option)
{
if (seen_data_command)
- die("Got option command '%s' after data command", option);
+ die(_("got option command '%s' after data command"), option);
if (parse_one_option(option))
return;
- die("This version of fast-import does not support option: %s", option);
+ die(_("this version of fast-import does not support option: %s"), option);
}
static void git_pack_config(void)
@@ -3672,7 +3715,7 @@ static void parse_argv(void)
break;
if (!skip_prefix(a, "--", &a))
- die("unknown option %s", a);
+ die(_("unknown option %s"), a);
if (parse_one_option(a))
continue;
@@ -3685,7 +3728,7 @@ static void parse_argv(void)
continue;
}
- die("unknown option --%s", a);
+ die(_("unknown option --%s"), a);
}
if (i != global_argc)
usage(fast_import_usage);
@@ -3774,7 +3817,7 @@ int cmd_fast_import(int argc,
else if (starts_with(command_buf.buf, "option "))
/* ignore non-git options*/;
else
- die("Unsupported command: %s", command_buf.buf);
+ die(_("unsupported command: %s"), command_buf.buf);
if (checkpoint_requested)
checkpoint();
@@ -3785,7 +3828,7 @@ int cmd_fast_import(int argc,
parse_argv();
if (require_explicit_termination && feof(stdin))
- die("stream ends early");
+ die(_("stream ends early"));
end_packfile();
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c7ff3480fb..7052e6ff21 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -289,13 +289,11 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
return ent;
}
-static int add_one_refname(const char *refname, const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int add_one_refname(const struct reference *ref, void *cbdata)
{
struct hashmap *refname_map = cbdata;
- (void) refname_hash_add(refname_map, refname, oid);
+ (void) refname_hash_add(refname_map, ref->name, ref->oid);
return 0;
}
@@ -1416,14 +1414,11 @@ static void set_option(struct transport *transport, const char *name, const char
}
-static int add_oid(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int add_oid(const struct reference *ref, void *cb_data)
{
struct oid_array *oids = cb_data;
- oid_array_append(oids, oid);
+ oid_array_append(oids, ref->oid);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8ee95e0d67..c489582faa 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -530,14 +530,13 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
return 0;
}
-static int fsck_handle_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object *obj;
- obj = parse_object(the_repository, oid);
+ obj = parse_object(the_repository, ref->oid);
if (!obj) {
- if (is_promisor_object(the_repository, oid)) {
+ if (is_promisor_object(the_repository, ref->oid)) {
/*
* Increment default_refs anyway, because this is a
* valid ref.
@@ -546,19 +545,19 @@ static int fsck_handle_ref(const char *refname, const char *referent UNUSED, con
return 0;
}
error(_("%s: invalid sha1 pointer %s"),
- refname, oid_to_hex(oid));
+ ref->name, oid_to_hex(ref->oid));
errors_found |= ERROR_REACHABLE;
/* We'll continue with the rest despite the error.. */
return 0;
}
- if (obj->type != OBJ_COMMIT && is_branch(refname)) {
- error(_("%s: not a commit"), refname);
+ if (obj->type != OBJ_COMMIT && is_branch(ref->name)) {
+ error(_("%s: not a commit"), ref->name);
errors_found |= ERROR_REFS;
}
default_refs++;
obj->flags |= USED;
fsck_put_object_name(&fsck_walk_options,
- oid, "%s", refname);
+ ref->oid, "%s", ref->name);
mark_object_reachable(obj);
return 0;
@@ -580,13 +579,19 @@ static void get_default_heads(void)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct strbuf ref = STRBUF_INIT;
+ struct strbuf refname = STRBUF_INIT;
- strbuf_worktree_ref(wt, &ref, "HEAD");
- fsck_head_link(ref.buf, &head_points_at, &head_oid);
- if (head_points_at && !is_null_oid(&head_oid))
- fsck_handle_ref(ref.buf, NULL, &head_oid, 0, NULL);
- strbuf_release(&ref);
+ strbuf_worktree_ref(wt, &refname, "HEAD");
+ fsck_head_link(refname.buf, &head_points_at, &head_oid);
+ if (head_points_at && !is_null_oid(&head_oid)) {
+ struct reference ref = {
+ .name = refname.buf,
+ .oid = &head_oid,
+ };
+
+ fsck_handle_ref(&ref, NULL);
+ }
+ strbuf_release(&refname);
if (include_reflogs)
refs_for_each_reflog(get_worktree_ref_store(wt),
@@ -867,20 +872,20 @@ static int mark_packed_for_connectivity(const struct object_id *oid,
static int check_pack_rev_indexes(struct repository *r, int show_progress)
{
- struct packfile_store *packs = r->objects->packfiles;
struct progress *progress = NULL;
+ struct packed_git *p;
uint32_t pack_count = 0;
int res = 0;
if (show_progress) {
- for (struct packed_git *p = packfile_store_get_all_packs(packs); p; p = p->next)
+ repo_for_each_pack(r, p)
pack_count++;
progress = start_delayed_progress(the_repository,
"Verifying reverse pack-indexes", pack_count);
pack_count = 0;
}
- for (struct packed_git *p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(r, p) {
int load_error = load_pack_revindex_from_disk(p);
if (load_error < 0) {
@@ -1000,8 +1005,6 @@ int cmd_fsck(int argc,
for_each_packed_object(the_repository,
mark_packed_for_connectivity, NULL, 0);
} else {
- struct packfile_store *packs = the_repository->objects->packfiles;
-
odb_prepare_alternates(the_repository->objects);
for (source = the_repository->objects->sources; source; source = source->next)
fsck_source(source);
@@ -1012,8 +1015,7 @@ int cmd_fsck(int argc,
struct progress *progress = NULL;
if (show_progress) {
- for (p = packfile_store_get_all_packs(packs); p;
- p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (open_pack_index(p))
continue;
total += p->num_objects;
@@ -1022,8 +1024,8 @@ int cmd_fsck(int argc,
progress = start_progress(the_repository,
_("Checking objects"), total);
}
- for (p = packfile_store_get_all_packs(packs); p;
- p = p->next) {
+
+ repo_for_each_pack(the_repository, p) {
/* verify gives error messages itself */
if (verify_pack(the_repository,
p, fsck_obj_buffer,
diff --git a/builtin/gc.c b/builtin/gc.c
index e19e13d978..5fa0653ed0 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -34,7 +34,9 @@
#include "pack-objects.h"
#include "path.h"
#include "reflog.h"
+#include "repack.h"
#include "rerere.h"
+#include "revision.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
@@ -55,7 +57,6 @@ static const char * const builtin_gc_usage[] = {
};
static timestamp_t gc_log_expire_time;
-static struct strvec repack = STRVEC_INIT;
static struct tempfile *pidfile;
static struct lock_file log_lock;
static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
@@ -255,6 +256,7 @@ enum maintenance_task_label {
TASK_PREFETCH,
TASK_LOOSE_OBJECTS,
TASK_INCREMENTAL_REPACK,
+ TASK_GEOMETRIC_REPACK,
TASK_GC,
TASK_COMMIT_GRAPH,
TASK_PACK_REFS,
@@ -285,12 +287,26 @@ static void maintenance_run_opts_release(struct maintenance_run_opts *opts)
static int pack_refs_condition(UNUSED struct gc_config *cfg)
{
- /*
- * The auto-repacking logic for refs is handled by the ref backends and
- * exposed via `git pack-refs --auto`. We thus always return truish
- * here and let the backend decide for us.
- */
- return 1;
+ struct string_list included_refs = STRING_LIST_INIT_NODUP;
+ struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+ struct refs_optimize_opts optimize_opts = {
+ .exclusions = &excludes,
+ .includes = &included_refs,
+ .flags = REFS_OPTIMIZE_PRUNE | REFS_OPTIMIZE_AUTO,
+ };
+ bool required;
+
+ /* Check for all refs, similar to 'git refs optimize --all'. */
+ string_list_append(optimize_opts.includes, "*");
+
+ if (refs_optimize_required(get_main_ref_store(the_repository),
+ &optimize_opts, &required))
+ return 0;
+
+ clear_ref_exclusions(&excludes);
+ string_list_clear(&included_refs, 0);
+
+ return required == true;
}
static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
@@ -448,7 +464,7 @@ out:
return should_gc;
}
-static int too_many_loose_objects(struct gc_config *cfg)
+static int too_many_loose_objects(int limit)
{
/*
* Quickly check if a "gc" is needed, by estimating how
@@ -470,7 +486,7 @@ static int too_many_loose_objects(struct gc_config *cfg)
if (!dir)
return 0;
- auto_threshold = DIV_ROUND_UP(cfg->gc_auto_threshold, 256);
+ auto_threshold = DIV_ROUND_UP(limit, 256);
while ((ent = readdir(dir)) != NULL) {
if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose ||
ent->d_name[hexsz_loose] != '\0')
@@ -487,10 +503,9 @@ static int too_many_loose_objects(struct gc_config *cfg)
static struct packed_git *find_base_packs(struct string_list *packs,
unsigned long limit)
{
- struct packfile_store *packfiles = the_repository->objects->packfiles;
struct packed_git *p, *base = NULL;
- for (p = packfile_store_get_all_packs(packfiles); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (!p->pack_local || p->is_cruft)
continue;
if (limit) {
@@ -509,14 +524,13 @@ static struct packed_git *find_base_packs(struct string_list *packs,
static int too_many_packs(struct gc_config *cfg)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
- int cnt;
+ int cnt = 0;
if (cfg->gc_auto_pack_limit <= 0)
return 0;
- for (cnt = 0, p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (!p->pack_local)
continue;
if (p->pack_keep)
@@ -618,48 +632,50 @@ static uint64_t estimate_repack_memory(struct gc_config *cfg,
return os_cache + heap;
}
-static int keep_one_pack(struct string_list_item *item, void *data UNUSED)
+static int keep_one_pack(struct string_list_item *item, void *data)
{
- strvec_pushf(&repack, "--keep-pack=%s", basename(item->string));
+ struct strvec *args = data;
+ strvec_pushf(args, "--keep-pack=%s", basename(item->string));
return 0;
}
static void add_repack_all_option(struct gc_config *cfg,
- struct string_list *keep_pack)
+ struct string_list *keep_pack,
+ struct strvec *args)
{
if (cfg->prune_expire && !strcmp(cfg->prune_expire, "now")
&& !(cfg->cruft_packs && cfg->repack_expire_to))
- strvec_push(&repack, "-a");
+ strvec_push(args, "-a");
else if (cfg->cruft_packs) {
- strvec_push(&repack, "--cruft");
+ strvec_push(args, "--cruft");
if (cfg->prune_expire)
- strvec_pushf(&repack, "--cruft-expiration=%s", cfg->prune_expire);
+ strvec_pushf(args, "--cruft-expiration=%s", cfg->prune_expire);
if (cfg->max_cruft_size)
- strvec_pushf(&repack, "--max-cruft-size=%lu",
+ strvec_pushf(args, "--max-cruft-size=%lu",
cfg->max_cruft_size);
if (cfg->repack_expire_to)
- strvec_pushf(&repack, "--expire-to=%s", cfg->repack_expire_to);
+ strvec_pushf(args, "--expire-to=%s", cfg->repack_expire_to);
} else {
- strvec_push(&repack, "-A");
+ strvec_push(args, "-A");
if (cfg->prune_expire)
- strvec_pushf(&repack, "--unpack-unreachable=%s", cfg->prune_expire);
+ strvec_pushf(args, "--unpack-unreachable=%s", cfg->prune_expire);
}
if (keep_pack)
- for_each_string_list(keep_pack, keep_one_pack, NULL);
+ for_each_string_list(keep_pack, keep_one_pack, args);
if (cfg->repack_filter && *cfg->repack_filter)
- strvec_pushf(&repack, "--filter=%s", cfg->repack_filter);
+ strvec_pushf(args, "--filter=%s", cfg->repack_filter);
if (cfg->repack_filter_to && *cfg->repack_filter_to)
- strvec_pushf(&repack, "--filter-to=%s", cfg->repack_filter_to);
+ strvec_pushf(args, "--filter-to=%s", cfg->repack_filter_to);
}
-static void add_repack_incremental_option(void)
+static void add_repack_incremental_option(struct strvec *args)
{
- strvec_push(&repack, "--no-write-bitmap-index");
+ strvec_push(args, "--no-write-bitmap-index");
}
-static int need_to_gc(struct gc_config *cfg)
+static int need_to_gc(struct gc_config *cfg, struct strvec *repack_args)
{
/*
* Setting gc.auto to 0 or negative can disable the
@@ -700,10 +716,10 @@ static int need_to_gc(struct gc_config *cfg)
string_list_clear(&keep_pack, 0);
}
- add_repack_all_option(cfg, &keep_pack);
+ add_repack_all_option(cfg, &keep_pack, repack_args);
string_list_clear(&keep_pack, 0);
- } else if (too_many_loose_objects(cfg))
- add_repack_incremental_option();
+ } else if (too_many_loose_objects(cfg->gc_auto_threshold))
+ add_repack_incremental_option(repack_args);
else
return 0;
@@ -852,6 +868,7 @@ int cmd_gc(int argc,
int keep_largest_pack = -1;
int skip_foreground_tasks = 0;
timestamp_t dummy;
+ struct strvec repack_args = STRVEC_INIT;
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
struct gc_config cfg = GC_CONFIG_INIT;
const char *prune_expire_sentinel = "sentinel";
@@ -891,7 +908,7 @@ int cmd_gc(int argc,
show_usage_with_options_if_asked(argc, argv,
builtin_gc_usage, builtin_gc_options);
- strvec_pushl(&repack, "repack", "-d", "-l", NULL);
+ strvec_pushl(&repack_args, "repack", "-d", "-l", NULL);
gc_config(&cfg);
@@ -914,14 +931,14 @@ int cmd_gc(int argc,
die(_("failed to parse prune expiry value %s"), cfg.prune_expire);
if (aggressive) {
- strvec_push(&repack, "-f");
+ strvec_push(&repack_args, "-f");
if (cfg.aggressive_depth > 0)
- strvec_pushf(&repack, "--depth=%d", cfg.aggressive_depth);
+ strvec_pushf(&repack_args, "--depth=%d", cfg.aggressive_depth);
if (cfg.aggressive_window > 0)
- strvec_pushf(&repack, "--window=%d", cfg.aggressive_window);
+ strvec_pushf(&repack_args, "--window=%d", cfg.aggressive_window);
}
if (opts.quiet)
- strvec_push(&repack, "-q");
+ strvec_push(&repack_args, "-q");
if (opts.auto_flag) {
if (cfg.detach_auto && opts.detach < 0)
@@ -930,7 +947,7 @@ int cmd_gc(int argc,
/*
* Auto-gc should be least intrusive as possible.
*/
- if (!need_to_gc(&cfg)) {
+ if (!need_to_gc(&cfg, &repack_args)) {
ret = 0;
goto out;
}
@@ -952,7 +969,7 @@ int cmd_gc(int argc,
find_base_packs(&keep_pack, cfg.big_pack_threshold);
}
- add_repack_all_option(&cfg, &keep_pack);
+ add_repack_all_option(&cfg, &keep_pack, &repack_args);
string_list_clear(&keep_pack, 0);
}
@@ -1014,9 +1031,9 @@ int cmd_gc(int argc,
repack_cmd.git_cmd = 1;
repack_cmd.close_object_store = 1;
- strvec_pushv(&repack_cmd.args, repack.v);
+ strvec_pushv(&repack_cmd.args, repack_args.v);
if (run_command(&repack_cmd))
- die(FAILED_RUN, repack.v[0]);
+ die(FAILED_RUN, repack_args.v[0]);
if (cfg.prune_expire) {
struct child_process prune_cmd = CHILD_PROCESS_INIT;
@@ -1055,7 +1072,7 @@ int cmd_gc(int argc,
!opts.quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
NULL);
- if (opts.auto_flag && too_many_loose_objects(&cfg))
+ if (opts.auto_flag && too_many_loose_objects(cfg.gc_auto_threshold))
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
@@ -1067,6 +1084,7 @@ int cmd_gc(int argc,
out:
maintenance_run_opts_release(&opts);
+ strvec_clear(&repack_args);
gc_config_release(&cfg);
return 0;
}
@@ -1092,32 +1110,26 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
return 0;
}
-/* Remember to update object flag allocation in object.h */
-#define SEEN (1u<<0)
-
struct cg_auto_data {
int num_not_in_graph;
int limit;
};
-static int dfs_on_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int dfs_on_ref(const struct reference *ref, void *cb_data)
{
struct cg_auto_data *data = (struct cg_auto_data *)cb_data;
int result = 0;
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct commit_list *stack = NULL;
struct commit *commit;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
- if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT)
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
+ maybe_peeled = &peeled;
+ if (odb_read_object_info(the_repository->objects, maybe_peeled, NULL) != OBJ_COMMIT)
return 0;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(the_repository, maybe_peeled);
if (!commit)
return 0;
if (repo_parse_commit(the_repository, commit) ||
@@ -1269,6 +1281,19 @@ static int maintenance_task_gc_background(struct maintenance_run_opts *opts,
return run_command(&child);
}
+static int gc_condition(struct gc_config *cfg)
+{
+ /*
+ * Note that it's fine to drop the repack arguments here, as we execute
+ * git-gc(1) as a separate child process anyway. So it knows to compute
+ * these arguments again.
+ */
+ struct strvec repack_args = STRVEC_INIT;
+ int ret = need_to_gc(cfg, &repack_args);
+ strvec_clear(&repack_args);
+ return ret;
+}
+
static int prune_packed(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -1425,9 +1450,9 @@ static int incremental_repack_auto_condition(struct gc_config *cfg UNUSED)
if (incremental_repack_auto_limit < 0)
return 1;
- for (p = packfile_store_get_packs(the_repository->objects->packfiles);
- count < incremental_repack_auto_limit && p;
- p = p->next) {
+ repo_for_each_pack(the_repository, p) {
+ if (count >= incremental_repack_auto_limit)
+ break;
if (!p->multi_pack_index)
count++;
}
@@ -1494,7 +1519,7 @@ static off_t get_auto_pack_size(void)
struct repository *r = the_repository;
odb_reprepare(r->objects);
- for (p = packfile_store_get_all_packs(r->objects->packfiles); p; p = p->next) {
+ repo_for_each_pack(r, p) {
if (p->pack_size > max_size) {
second_largest_size = max_size;
max_size = p->pack_size;
@@ -1550,6 +1575,108 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
return 0;
}
+static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts,
+ struct gc_config *cfg)
+{
+ struct pack_geometry geometry = {
+ .split_factor = 2,
+ };
+ struct pack_objects_args po_args = {
+ .local = 1,
+ };
+ struct existing_packs existing_packs = EXISTING_PACKS_INIT;
+ struct string_list kept_packs = STRING_LIST_INIT_DUP;
+ struct child_process child = CHILD_PROCESS_INIT;
+ int ret;
+
+ repo_config_get_int(the_repository, "maintenance.geometric-repack.splitFactor",
+ &geometry.split_factor);
+
+ existing_packs.repo = the_repository;
+ existing_packs_collect(&existing_packs, &kept_packs);
+ pack_geometry_init(&geometry, &existing_packs, &po_args);
+ pack_geometry_split(&geometry);
+
+ child.git_cmd = 1;
+
+ strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
+ if (geometry.split < geometry.pack_nr)
+ strvec_pushf(&child.args, "--geometric=%d",
+ geometry.split_factor);
+ else
+ add_repack_all_option(cfg, NULL, &child.args);
+ if (opts->quiet)
+ strvec_push(&child.args, "--quiet");
+ if (the_repository->settings.core_multi_pack_index)
+ strvec_push(&child.args, "--write-midx");
+
+ if (run_command(&child)) {
+ ret = error(_("failed to perform geometric repack"));
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ existing_packs_release(&existing_packs);
+ pack_geometry_release(&geometry);
+ return ret;
+}
+
+static int geometric_repack_auto_condition(struct gc_config *cfg UNUSED)
+{
+ struct pack_geometry geometry = {
+ .split_factor = 2,
+ };
+ struct pack_objects_args po_args = {
+ .local = 1,
+ };
+ struct existing_packs existing_packs = EXISTING_PACKS_INIT;
+ struct string_list kept_packs = STRING_LIST_INIT_DUP;
+ int auto_value = 100;
+ int ret;
+
+ repo_config_get_int(the_repository, "maintenance.geometric-repack.auto",
+ &auto_value);
+ if (!auto_value)
+ return 0;
+ if (auto_value < 0)
+ return 1;
+
+ repo_config_get_int(the_repository, "maintenance.geometric-repack.splitFactor",
+ &geometry.split_factor);
+
+ existing_packs.repo = the_repository;
+ existing_packs_collect(&existing_packs, &kept_packs);
+ pack_geometry_init(&geometry, &existing_packs, &po_args);
+ pack_geometry_split(&geometry);
+
+ /*
+ * When we'd merge at least two packs with one another we always
+ * perform the repack.
+ */
+ if (geometry.split) {
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * Otherwise, we estimate the number of loose objects to determine
+ * whether we want to create a new packfile or not.
+ */
+ if (too_many_loose_objects(auto_value)) {
+ ret = 1;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ existing_packs_release(&existing_packs);
+ pack_geometry_release(&geometry);
+ return ret;
+}
+
typedef int (*maintenance_task_fn)(struct maintenance_run_opts *opts,
struct gc_config *cfg);
typedef int (*maintenance_auto_fn)(struct gc_config *cfg);
@@ -1592,11 +1719,16 @@ static const struct maintenance_task tasks[] = {
.background = maintenance_task_incremental_repack,
.auto_condition = incremental_repack_auto_condition,
},
+ [TASK_GEOMETRIC_REPACK] = {
+ .name = "geometric-repack",
+ .background = maintenance_task_geometric_repack,
+ .auto_condition = geometric_repack_auto_condition,
+ },
[TASK_GC] = {
.name = "gc",
.foreground = maintenance_task_gc_foreground,
.background = maintenance_task_gc_background,
- .auto_condition = need_to_gc,
+ .auto_condition = gc_condition,
},
[TASK_COMMIT_GRAPH] = {
.name = "commit-graph",
@@ -1702,39 +1834,116 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts,
return result;
}
+enum maintenance_type {
+ /* As invoked via `git maintenance run --schedule=`. */
+ MAINTENANCE_TYPE_SCHEDULED = (1 << 0),
+ /* As invoked via `git maintenance run` and with `--auto`. */
+ MAINTENANCE_TYPE_MANUAL = (1 << 1),
+};
+
struct maintenance_strategy {
struct {
- int enabled;
+ unsigned type;
enum schedule_priority schedule;
} tasks[TASK__COUNT];
};
static const struct maintenance_strategy none_strategy = { 0 };
-static const struct maintenance_strategy default_strategy = {
+
+static const struct maintenance_strategy gc_strategy = {
.tasks = {
- [TASK_GC].enabled = 1,
+ [TASK_GC] = {
+ .type = MAINTENANCE_TYPE_MANUAL | MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_DAILY,
+ },
},
};
+
static const struct maintenance_strategy incremental_strategy = {
.tasks = {
- [TASK_COMMIT_GRAPH].enabled = 1,
- [TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY,
- [TASK_PREFETCH].enabled = 1,
- [TASK_PREFETCH].schedule = SCHEDULE_HOURLY,
- [TASK_INCREMENTAL_REPACK].enabled = 1,
- [TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY,
- [TASK_LOOSE_OBJECTS].enabled = 1,
- [TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY,
- [TASK_PACK_REFS].enabled = 1,
- [TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY,
+ [TASK_COMMIT_GRAPH] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_HOURLY,
+ },
+ [TASK_PREFETCH] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_HOURLY,
+ },
+ [TASK_INCREMENTAL_REPACK] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_DAILY,
+ },
+ [TASK_LOOSE_OBJECTS] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_DAILY,
+ },
+ [TASK_PACK_REFS] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED,
+ .schedule = SCHEDULE_WEEKLY,
+ },
+ /*
+ * Historically, the "incremental" strategy was only available
+ * in the context of scheduled maintenance when set up via
+ * "maintenance.strategy". We have later expanded that config
+ * to also cover manual maintenance.
+ *
+ * To retain backwards compatibility with the previous status
+ * quo we thus run git-gc(1) in case manual maintenance was
+ * requested. This is the same as the default strategy, which
+ * would have been in use beforehand.
+ */
+ [TASK_GC] = {
+ .type = MAINTENANCE_TYPE_MANUAL,
+ },
+ },
+};
+
+static const struct maintenance_strategy geometric_strategy = {
+ .tasks = {
+ [TASK_COMMIT_GRAPH] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_HOURLY,
+ },
+ [TASK_GEOMETRIC_REPACK] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_DAILY,
+ },
+ [TASK_PACK_REFS] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_DAILY,
+ },
+ [TASK_RERERE_GC] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_WEEKLY,
+ },
+ [TASK_REFLOG_EXPIRE] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_WEEKLY,
+ },
+ [TASK_WORKTREE_PRUNE] = {
+ .type = MAINTENANCE_TYPE_SCHEDULED | MAINTENANCE_TYPE_MANUAL,
+ .schedule = SCHEDULE_WEEKLY,
+ },
},
};
+static struct maintenance_strategy parse_maintenance_strategy(const char *name)
+{
+ if (!strcasecmp(name, "incremental"))
+ return incremental_strategy;
+ if (!strcasecmp(name, "gc"))
+ return gc_strategy;
+ if (!strcasecmp(name, "geometric"))
+ return geometric_strategy;
+ die(_("unknown maintenance strategy: '%s'"), name);
+}
+
static void initialize_task_config(struct maintenance_run_opts *opts,
const struct string_list *selected_tasks)
{
struct strbuf config_name = STRBUF_INIT;
struct maintenance_strategy strategy;
+ enum maintenance_type type;
const char *config_str;
/*
@@ -1762,19 +1971,20 @@ static void initialize_task_config(struct maintenance_run_opts *opts,
* - Unscheduled maintenance uses our default strategy.
*
* Both of these are affected by the gitconfig though, which may
- * override specific aspects of our strategy.
+ * override specific aspects of our strategy. Furthermore, both
+ * strategies can be overridden by setting "maintenance.strategy".
*/
if (opts->schedule) {
strategy = none_strategy;
-
- if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str)) {
- if (!strcasecmp(config_str, "incremental"))
- strategy = incremental_strategy;
- }
+ type = MAINTENANCE_TYPE_SCHEDULED;
} else {
- strategy = default_strategy;
+ strategy = gc_strategy;
+ type = MAINTENANCE_TYPE_MANUAL;
}
+ if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str))
+ strategy = parse_maintenance_strategy(config_str);
+
for (size_t i = 0; i < TASK__COUNT; i++) {
int config_value;
@@ -1782,8 +1992,8 @@ static void initialize_task_config(struct maintenance_run_opts *opts,
strbuf_addf(&config_name, "maintenance.%s.enabled",
tasks[i].name);
if (!repo_config_get_bool(the_repository, config_name.buf, &config_value))
- strategy.tasks[i].enabled = config_value;
- if (!strategy.tasks[i].enabled)
+ strategy.tasks[i].type = config_value ? type : 0;
+ if (!(strategy.tasks[i].type & type))
continue;
if (opts->schedule) {
@@ -3246,7 +3456,59 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
return update_background_schedule(NULL, 0);
}
-static const char * const builtin_maintenance_usage[] = {
+static const char *const builtin_maintenance_is_needed_usage[] = {
+ "git maintenance is-needed [--task=<task>] [--schedule]",
+ NULL
+};
+
+static int maintenance_is_needed(int argc, const char **argv, const char *prefix,
+ struct repository *repo UNUSED)
+{
+ struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
+ struct string_list selected_tasks = STRING_LIST_INIT_DUP;
+ struct gc_config cfg = GC_CONFIG_INIT;
+ struct option options[] = {
+ OPT_BOOL(0, "auto", &opts.auto_flag,
+ N_("run tasks based on the state of the repository")),
+ OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"),
+ N_("check a specific task"),
+ PARSE_OPT_NONEG, task_option_parse),
+ OPT_END()
+ };
+ bool is_needed = false;
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_is_needed_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (argc)
+ usage_with_options(builtin_maintenance_is_needed_usage, options);
+
+ gc_config(&cfg);
+ initialize_task_config(&opts, &selected_tasks);
+
+ if (opts.auto_flag) {
+ for (size_t i = 0; i < opts.tasks_nr; i++) {
+ if (tasks[opts.tasks[i]].auto_condition &&
+ tasks[opts.tasks[i]].auto_condition(&cfg)) {
+ is_needed = true;
+ break;
+ }
+ }
+ } else {
+ /* When not using --auto, we should always require maintenance. */
+ is_needed = true;
+ }
+
+ string_list_clear(&selected_tasks, 0);
+ maintenance_run_opts_release(&opts);
+ gc_config_release(&cfg);
+
+ if (is_needed)
+ return 0;
+ return 1;
+}
+
+static const char *const builtin_maintenance_usage[] = {
N_("git maintenance <subcommand> [<options>]"),
NULL,
};
@@ -3263,6 +3525,7 @@ int cmd_maintenance(int argc,
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
OPT_SUBCOMMAND("register", &fn, maintenance_register),
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
+ OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
OPT_END(),
};
diff --git a/builtin/grep.c b/builtin/grep.c
index 13841fbf00..53cccf2d25 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1214,7 +1214,7 @@ int cmd_grep(int argc,
if (recurse_submodules)
repo_read_gitmodules(the_repository, 1);
if (startup_info->have_repository)
- (void)packfile_store_get_packs(the_repository->objects->packfiles);
+ packfile_store_prepare(the_repository->objects->packfiles);
start_threads(&opt);
} else {
diff --git a/builtin/history.c b/builtin/history.c
new file mode 100644
index 0000000000..cae841707d
--- /dev/null
+++ b/builtin/history.c
@@ -0,0 +1,561 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "builtin.h"
+#include "cache-tree.h"
+#include "commit-reach.h"
+#include "commit.h"
+#include "config.h"
+#include "editor.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "oidmap.h"
+#include "parse-options.h"
+#include "path.h"
+#include "read-cache.h"
+#include "refs.h"
+#include "replay.h"
+#include "reset.h"
+#include "revision.h"
+#include "run-command.h"
+#include "sequencer.h"
+#include "strvec.h"
+#include "tree.h"
+#include "wt-status.h"
+
+#define GIT_HISTORY_REWORD_USAGE N_("git history reword <commit>")
+#define GIT_HISTORY_SPLIT_USAGE N_("git history split <commit> [--] [<pathspec>...]")
+
+static int collect_commits(struct repository *repo,
+ struct commit *old_commit,
+ struct commit *new_commit,
+ struct strvec *out)
+{
+ struct setup_revision_opt revision_opts = {
+ .assume_dashdash = 1,
+ };
+ struct strvec revisions = STRVEC_INIT;
+ struct commit *child;
+ struct rev_info rev = { 0 };
+ int ret;
+
+ repo_init_revisions(repo, &rev, NULL);
+ strvec_push(&revisions, "");
+ strvec_push(&revisions, oid_to_hex(&new_commit->object.oid));
+ if (old_commit)
+ strvec_pushf(&revisions, "^%s", oid_to_hex(&old_commit->object.oid));
+
+ setup_revisions_from_strvec(&revisions, &rev, &revision_opts);
+ if (revisions.nr != 1 || prepare_revision_walk(&rev)) {
+ ret = error(_("revision walk setup failed"));
+ goto out;
+ }
+
+ while ((child = get_revision(&rev))) {
+ if (old_commit && !child->parents)
+ BUG("revision walk did not find child commit");
+ if (child->parents && child->parents->next) {
+ ret = error(_("cannot rearrange commit history with merges"));
+ goto out;
+ }
+
+ strvec_push(out, oid_to_hex(&child->object.oid));
+
+ if (child->parents && old_commit &&
+ commit_list_contains(old_commit, child->parents))
+ break;
+ }
+
+ /*
+ * Revisions are in newest-order-first. We have to reverse the
+ * array though so that we pick the oldest commits first.
+ */
+ for (size_t i = 0, j = out->nr - 1; i < j; i++, j--)
+ SWAP(out->v[i], out->v[j]);
+
+ ret = 0;
+
+out:
+ strvec_clear(&revisions);
+ release_revisions(&rev);
+ reset_revision_walk();
+ return ret;
+}
+
+static void replace_commits(struct strvec *commits,
+ const struct object_id *commit_to_replace,
+ const struct object_id *replacements,
+ size_t replacements_nr)
+{
+ char commit_to_replace_oid[GIT_MAX_HEXSZ + 1];
+ struct strvec replacement_oids = STRVEC_INIT;
+ bool found = false;
+
+ oid_to_hex_r(commit_to_replace_oid, commit_to_replace);
+ for (size_t i = 0; i < replacements_nr; i++)
+ strvec_push(&replacement_oids, oid_to_hex(&replacements[i]));
+
+ for (size_t i = 0; i < commits->nr; i++) {
+ if (strcmp(commits->v[i], commit_to_replace_oid))
+ continue;
+ strvec_splice(commits, i, 1, replacement_oids.v, replacement_oids.nr);
+ found = true;
+ break;
+ }
+ if (!found)
+ BUG("could not find commit to replace");
+
+ strvec_clear(&replacement_oids);
+}
+
+static int apply_commits(struct repository *repo,
+ const struct strvec *commits,
+ struct commit *onto,
+ struct commit *orig_head,
+ const char *action)
+{
+ struct reset_head_opts reset_opts = { 0 };
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+
+ for (size_t i = 0; i < commits->nr; i++) {
+ struct object_id commit_id;
+ struct commit *commit;
+ const char *end;
+
+ if (parse_oid_hex_algop(commits->v[i], &commit_id, &end,
+ repo->hash_algo)) {
+ ret = error(_("invalid object ID: %s"), commits->v[i]);
+ goto out;
+ }
+
+ commit = lookup_commit(repo, &commit_id);
+ if (!commit || repo_parse_commit(repo, commit)) {
+ ret = error(_("failed to look up commit: %s"), oid_to_hex(&commit_id));
+ goto out;
+ }
+
+ if (!onto) {
+ onto = commit;
+ } else {
+ struct tree *tree = repo_get_commit_tree(repo, commit);
+ onto = replay_create_commit(repo, tree, commit, onto);
+ if (!onto)
+ break;
+ }
+ }
+
+ reset_opts.oid = &onto->object.oid;
+ strbuf_addf(&buf, "%s: switch to rewritten %s", action, oid_to_hex(reset_opts.oid));
+ reset_opts.flags = RESET_HEAD_REFS_ONLY | RESET_ORIG_HEAD;
+ reset_opts.orig_head = &orig_head->object.oid;
+ reset_opts.default_reflog_action = action;
+ if (reset_head(repo, &reset_opts) < 0) {
+ ret = error(_("could not switch to %s"), oid_to_hex(reset_opts.oid));
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ strbuf_release(&buf);
+ return ret;
+}
+
+static void change_data_free(void *util, const char *str UNUSED)
+{
+ struct wt_status_change_data *d = util;
+ free(d->rename_source);
+ free(d);
+}
+
+static int fill_commit_message(struct repository *repo,
+ const struct object_id *old_tree,
+ const struct object_id *new_tree,
+ const char *default_message,
+ const char *action,
+ struct strbuf *out)
+{
+ const char *path = git_path_commit_editmsg();
+ const char *hint =
+ _("Please enter the commit message for the %s changes."
+ " Lines starting\nwith '%s' will be ignored.\n");
+ struct wt_status s;
+
+ strbuf_addstr(out, default_message);
+ strbuf_addch(out, '\n');
+ strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
+ write_file_buf(path, out->buf, out->len);
+
+ wt_status_prepare(repo, &s);
+ FREE_AND_NULL(s.branch);
+ s.ahead_behind_flags = AHEAD_BEHIND_QUICK;
+ s.commit_template = 1;
+ s.colopts = 0;
+ s.display_comment_prefix = 1;
+ s.hints = 0;
+ s.use_color = 0;
+ s.whence = FROM_COMMIT;
+ s.committable = 1;
+
+ s.fp = fopen(git_path_commit_editmsg(), "a");
+ if (!s.fp)
+ return error_errno(_("could not open '%s'"), git_path_commit_editmsg());
+
+ wt_status_collect_changes_trees(&s, old_tree, new_tree);
+ wt_status_print(&s);
+ wt_status_collect_free_buffers(&s);
+ string_list_clear_func(&s.change, change_data_free);
+
+ strbuf_reset(out);
+ if (launch_editor(path, out, NULL)) {
+ fprintf(stderr, _("Please supply the message using the -m option.\n"));
+ return -1;
+ }
+ strbuf_stripspace(out, comment_line_str);
+
+ cleanup_message(out, COMMIT_MSG_CLEANUP_ALL, 0);
+
+ if (!out->len) {
+ fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cmd_history_reword(int argc,
+ const char **argv,
+ const char *prefix,
+ struct repository *repo)
+{
+ const char * const usage[] = {
+ GIT_HISTORY_REWORD_USAGE,
+ NULL,
+ };
+ struct option options[] = {
+ OPT_END(),
+ };
+ struct strbuf final_message = STRBUF_INIT;
+ struct commit *original_commit, *parent, *head;
+ struct strvec commits = STRVEC_INIT;
+ struct object_id parent_tree_oid, original_commit_tree_oid;
+ struct object_id rewritten_commit;
+ struct commit_list *from_list = NULL;
+ const char *original_message, *original_body, *ptr;
+ char *original_author = NULL;
+ size_t len;
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ if (argc != 1) {
+ ret = error(_("command expects a single revision"));
+ goto out;
+ }
+ repo_config(repo, git_default_config, NULL);
+
+ original_commit = lookup_commit_reference_by_name(argv[0]);
+ if (!original_commit) {
+ ret = error(_("commit to be reworded cannot be found: %s"), argv[0]);
+ goto out;
+ }
+ original_commit_tree_oid = repo_get_commit_tree(repo, original_commit)->object.oid;
+
+ parent = original_commit->parents ? original_commit->parents->item : NULL;
+ if (parent) {
+ if (repo_parse_commit(repo, parent)) {
+ ret = error(_("unable to parse commit %s"),
+ oid_to_hex(&parent->object.oid));
+ goto out;
+ }
+ parent_tree_oid = repo_get_commit_tree(repo, parent)->object.oid;
+ } else {
+ oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree);
+ }
+
+ head = lookup_commit_reference_by_name("HEAD");
+ if (!head) {
+ ret = error(_("could not resolve HEAD to a commit"));
+ goto out;
+ }
+
+ commit_list_append(original_commit, &from_list);
+ if (!repo_is_descendant_of(repo, head, from_list)) {
+ ret = error (_("split commit must be reachable from current HEAD commit"));
+ goto out;
+ }
+
+ /*
+ * Collect the list of commits that we'll have to reapply now already.
+ * This ensures that we'll abort early on in case the range of commits
+ * contains merges, which we do not yet handle.
+ */
+ ret = collect_commits(repo, parent, head, &commits);
+ if (ret < 0)
+ goto out;
+
+ /* We retain authorship of the original commit. */
+ original_message = repo_logmsg_reencode(repo, original_commit, NULL, NULL);
+ ptr = find_commit_header(original_message, "author", &len);
+ if (ptr)
+ original_author = xmemdupz(ptr, len);
+ find_commit_subject(original_message, &original_body);
+
+ ret = fill_commit_message(repo, &parent_tree_oid, &original_commit_tree_oid,
+ original_body, "reworded", &final_message);
+ if (ret < 0)
+ goto out;
+
+ ret = commit_tree(final_message.buf, final_message.len, &original_commit_tree_oid,
+ original_commit->parents, &rewritten_commit, original_author, NULL);
+ if (ret < 0) {
+ ret = error(_("failed writing reworded commit"));
+ goto out;
+ }
+
+ replace_commits(&commits, &original_commit->object.oid, &rewritten_commit, 1);
+
+ ret = apply_commits(repo, &commits, parent, head, "reword");
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+
+out:
+ strbuf_release(&final_message);
+ free_commit_list(from_list);
+ strvec_clear(&commits);
+ free(original_author);
+ return ret;
+}
+
+static int split_commit(struct repository *repo,
+ struct commit *original_commit,
+ struct pathspec *pathspec,
+ struct object_id *out)
+{
+ struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
+ struct strbuf index_file = STRBUF_INIT, split_message = STRBUF_INIT;
+ struct child_process read_tree_cmd = CHILD_PROCESS_INIT;
+ struct index_state index = INDEX_STATE_INIT(repo);
+ struct object_id original_commit_tree_oid, parent_tree_oid;
+ const char *original_message, *original_body, *ptr;
+ char original_commit_oid[GIT_MAX_HEXSZ + 1];
+ char *original_author = NULL;
+ struct commit_list *parents = NULL;
+ struct commit *first_commit;
+ struct tree *split_tree;
+ size_t len;
+ int ret;
+
+ if (original_commit->parents)
+ parent_tree_oid = *get_commit_tree_oid(original_commit->parents->item);
+ else
+ oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree);
+ original_commit_tree_oid = *get_commit_tree_oid(original_commit);
+
+ /*
+ * Construct the first commit. This is done by taking the original
+ * commit parent's tree and selectively patching changes from the diff
+ * between that parent and its child.
+ */
+ repo_git_path_replace(repo, &index_file, "%s", "history-split.index");
+
+ read_tree_cmd.git_cmd = 1;
+ strvec_pushf(&read_tree_cmd.env, "GIT_INDEX_FILE=%s", index_file.buf);
+ strvec_push(&read_tree_cmd.args, "read-tree");
+ strvec_push(&read_tree_cmd.args, oid_to_hex(&parent_tree_oid));
+ ret = run_command(&read_tree_cmd);
+ if (ret < 0)
+ goto out;
+
+ ret = read_index_from(&index, index_file.buf, repo->gitdir);
+ if (ret < 0) {
+ ret = error(_("failed reading temporary index"));
+ goto out;
+ }
+
+ oid_to_hex_r(original_commit_oid, &original_commit->object.oid);
+ ret = run_add_p_index(repo, &index, index_file.buf, &interactive_opts,
+ original_commit_oid, pathspec);
+ if (ret < 0)
+ goto out;
+
+ split_tree = write_in_core_index_as_tree(repo, &index);
+ if (!split_tree) {
+ ret = error(_("failed split tree"));
+ goto out;
+ }
+
+ unlink(index_file.buf);
+
+ /*
+ * We disallow the cases where either the split-out commit or the
+ * original commit would become empty. Consequently, if we see that the
+ * new tree ID matches either of those trees we abort.
+ */
+ if (oideq(&split_tree->object.oid, &parent_tree_oid)) {
+ ret = error(_("split commit is empty"));
+ goto out;
+ } else if (oideq(&split_tree->object.oid, &original_commit_tree_oid)) {
+ ret = error(_("split commit tree matches original commit"));
+ goto out;
+ }
+
+ /* We retain authorship of the original commit. */
+ original_message = repo_logmsg_reencode(repo, original_commit, NULL, NULL);
+ ptr = find_commit_header(original_message, "author", &len);
+ if (ptr)
+ original_author = xmemdupz(ptr, len);
+
+ ret = fill_commit_message(repo, &parent_tree_oid, &split_tree->object.oid,
+ "", "split-out", &split_message);
+ if (ret < 0)
+ goto out;
+
+ ret = commit_tree(split_message.buf, split_message.len, &split_tree->object.oid,
+ original_commit->parents, &out[0], original_author, NULL);
+ if (ret < 0) {
+ ret = error(_("failed writing split-out commit"));
+ goto out;
+ }
+
+ /*
+ * The second commit is much simpler to construct, as we can simply use
+ * the original commit details, except that we adjust its parent to be
+ * the newly split-out commit.
+ */
+ find_commit_subject(original_message, &original_body);
+ first_commit = lookup_commit_reference(repo, &out[0]);
+ commit_list_append(first_commit, &parents);
+
+ ret = commit_tree(original_body, strlen(original_body), &original_commit_tree_oid,
+ parents, &out[1], original_author, NULL);
+ if (ret < 0) {
+ ret = error(_("failed writing second commit"));
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ if (index_file.len)
+ unlink(index_file.buf);
+ strbuf_release(&split_message);
+ strbuf_release(&index_file);
+ free_commit_list(parents);
+ free(original_author);
+ release_index(&index);
+ return ret;
+}
+
+static int cmd_history_split(int argc,
+ const char **argv,
+ const char *prefix,
+ struct repository *repo)
+{
+ const char * const usage[] = {
+ GIT_HISTORY_SPLIT_USAGE,
+ NULL,
+ };
+ struct option options[] = {
+ OPT_END(),
+ };
+ struct oidmap rewritten_commits = OIDMAP_INIT;
+ struct commit *original_commit, *parent, *head;
+ struct strvec commits = STRVEC_INIT;
+ struct commit_list *from_list = NULL;
+ struct object_id split_commits[2];
+ struct pathspec pathspec = { 0 };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ if (argc < 1) {
+ ret = error(_("command expects a revision"));
+ goto out;
+ }
+ repo_config(repo, git_default_config, NULL);
+
+ original_commit = lookup_commit_reference_by_name(argv[0]);
+ if (!original_commit) {
+ ret = error(_("commit to be split cannot be found: %s"), argv[0]);
+ goto out;
+ }
+
+ parent = original_commit->parents ? original_commit->parents->item : NULL;
+ if (parent && repo_parse_commit(repo, parent)) {
+ ret = error(_("unable to parse commit %s"),
+ oid_to_hex(&parent->object.oid));
+ goto out;
+ }
+
+ head = lookup_commit_reference_by_name("HEAD");
+ if (!head) {
+ ret = error(_("could not resolve HEAD to a commit"));
+ goto out;
+ }
+
+ commit_list_append(original_commit, &from_list);
+ if (!repo_is_descendant_of(repo, head, from_list)) {
+ ret = error(_("split commit must be reachable from current HEAD commit"));
+ goto out;
+ }
+
+ parse_pathspec(&pathspec, 0,
+ PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH | PATHSPEC_PREFIX_ORIGIN,
+ prefix, argv + 1);
+
+ /*
+ * Collect the list of commits that we'll have to reapply now already.
+ * This ensures that we'll abort early on in case the range of commits
+ * contains merges, which we do not yet handle.
+ */
+ ret = collect_commits(repo, parent, head, &commits);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Then we split up the commit and replace the original commit with the
+ * new ones.
+ */
+ ret = split_commit(repo, original_commit, &pathspec, split_commits);
+ if (ret < 0)
+ goto out;
+
+ replace_commits(&commits, &original_commit->object.oid,
+ split_commits, ARRAY_SIZE(split_commits));
+
+ ret = apply_commits(repo, &commits, parent, head, "split");
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+
+out:
+ oidmap_clear(&rewritten_commits, 0);
+ free_commit_list(from_list);
+ clear_pathspec(&pathspec);
+ strvec_clear(&commits);
+ return ret;
+}
+
+int cmd_history(int argc,
+ const char **argv,
+ const char *prefix,
+ struct repository *repo)
+{
+ const char * const usage[] = {
+ GIT_HISTORY_REWORD_USAGE,
+ GIT_HISTORY_SPLIT_USAGE,
+ NULL,
+ };
+ parse_opt_subcommand_fn *fn = NULL;
+ struct option options[] = {
+ OPT_SUBCOMMAND("reword", &fn, cmd_history_reword),
+ OPT_SUBCOMMAND("split", &fn, cmd_history_split),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ return fn(argc, argv, prefix, repo);
+}
diff --git a/builtin/hook.c b/builtin/hook.c
index 7afec380d2..73e7b8c2e8 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -44,6 +44,12 @@ static int run(int argc, const char **argv, const char *prefix,
goto usage;
/*
+ * All current "hook run" use-cases require ungrouped child output.
+ * If this changes, a hook run argument can be added to toggle it.
+ */
+ opt.ungroup = 1;
+
+ /*
* Having a -- for "run" when providing <hook-args> is
* mandatory.
*/
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 41b0750e5a..268a43372b 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -10,7 +10,6 @@
#include "gettext.h"
#include "parse-options.h"
#include "string-list.h"
-#include "tempfile.h"
#include "trailer.h"
#include "config.h"
@@ -93,37 +92,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
return 0;
}
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
-{
- struct stat st;
- struct strbuf filename_template = STRBUF_INIT;
- const char *tail;
- FILE *outfile;
-
- if (stat(file, &st))
- die_errno(_("could not stat %s"), file);
- if (!S_ISREG(st.st_mode))
- die(_("file %s is not a regular file"), file);
- if (!(st.st_mode & S_IWUSR))
- die(_("file %s is not writable by user"), file);
-
- /* Create temporary file in the same directory as the original */
- tail = strrchr(file, '/');
- if (tail)
- strbuf_add(&filename_template, file, tail - file + 1);
- strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
- trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
- strbuf_release(&filename_template);
- outfile = fdopen_tempfile(trailers_tempfile, "w");
- if (!outfile)
- die_errno(_("could not open temporary file"));
-
- return outfile;
-}
-
static void read_input_file(struct strbuf *sb, const char *file)
{
if (file) {
@@ -140,55 +108,20 @@ static void interpret_trailers(const struct process_trailer_options *opts,
struct list_head *new_trailer_head,
const char *file)
{
- LIST_HEAD(head);
struct strbuf sb = STRBUF_INIT;
- struct strbuf trailer_block_sb = STRBUF_INIT;
- struct trailer_block *trailer_block;
- FILE *outfile = stdout;
-
- trailer_config_init();
+ struct strbuf out = STRBUF_INIT;
read_input_file(&sb, file);
- if (opts->in_place)
- outfile = create_in_place_tempfile(file);
-
- trailer_block = parse_trailers(opts, sb.buf, &head);
-
- /* Print the lines before the trailer block */
- if (!opts->only_trailers)
- fwrite(sb.buf, 1, trailer_block_start(trailer_block), outfile);
-
- if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block))
- fprintf(outfile, "\n");
-
-
- if (!opts->only_input) {
- LIST_HEAD(config_head);
- LIST_HEAD(arg_head);
- parse_trailers_from_config(&config_head);
- parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
- list_splice(&config_head, &arg_head);
- process_trailers_lists(&head, &arg_head);
- }
-
- /* Print trailer block. */
- format_trailers(opts, &head, &trailer_block_sb);
- free_trailers(&head);
- fwrite(trailer_block_sb.buf, 1, trailer_block_sb.len, outfile);
- strbuf_release(&trailer_block_sb);
-
- /* Print the lines after the trailer block as is. */
- if (!opts->only_trailers)
- fwrite(sb.buf + trailer_block_end(trailer_block), 1,
- sb.len - trailer_block_end(trailer_block), outfile);
- trailer_block_release(trailer_block);
+ process_trailers(opts, new_trailer_head, &sb, &out);
if (opts->in_place)
- if (rename_tempfile(&trailers_tempfile, file))
- die_errno(_("could not rename temporary file to %s"), file);
+ write_file_buf(file, out.buf, out.len);
+ else
+ strbuf_write(&out, stdout);
strbuf_release(&sb);
+ strbuf_release(&out);
}
int cmd_interpret_trailers(int argc,
@@ -232,6 +165,8 @@ int cmd_interpret_trailers(int argc,
git_interpret_trailers_usage,
options);
+ trailer_config_init();
+
if (argc) {
int i;
for (i = 0; i < argc; i++)
diff --git a/builtin/last-modified.c b/builtin/last-modified.c
index ae8b36a2c3..b0ecbdc540 100644
--- a/builtin/last-modified.c
+++ b/builtin/last-modified.c
@@ -2,26 +2,32 @@
#include "bloom.h"
#include "builtin.h"
#include "commit-graph.h"
+#include "commit-slab.h"
#include "commit.h"
#include "config.h"
-#include "environment.h"
#include "diff.h"
#include "diffcore.h"
#include "environment.h"
+#include "ewah/ewok.h"
#include "hashmap.h"
#include "hex.h"
-#include "log-tree.h"
#include "object-name.h"
#include "object.h"
#include "parse-options.h"
+#include "prio-queue.h"
#include "quote.h"
#include "repository.h"
#include "revision.h"
+/* Remember to update object flag allocation in object.h */
+#define PARENT1 (1u<<16) /* used instead of SEEN */
+#define PARENT2 (1u<<17) /* used instead of BOTTOM, BOUNDARY */
+
struct last_modified_entry {
struct hashmap_entry hashent;
struct object_id oid;
struct bloom_key key;
+ size_t diff_idx;
const char path[FLEX_ARRAY];
};
@@ -37,13 +43,45 @@ static int last_modified_entry_hashcmp(const void *unused UNUSED,
return strcmp(ent1->path, path ? path : ent2->path);
}
+/*
+ * Hold a bitmap for each commit we're working with. In the bitmap, each bit
+ * represents a path in `lm->all_paths`. An active bit indicates the path still
+ * needs to be associated to a commit.
+ */
+define_commit_slab(active_paths_for_commit, struct bitmap *);
+
struct last_modified {
struct hashmap paths;
struct rev_info rev;
bool recursive;
bool show_trees;
+
+ const char **all_paths;
+ size_t all_paths_nr;
+ struct active_paths_for_commit active_paths;
+
+ /* 'scratch' to avoid allocating a bitmap every process_parent() */
+ struct bitmap *scratch;
};
+static struct bitmap *active_paths_for(struct last_modified *lm, struct commit *c)
+{
+ struct bitmap **bitmap = active_paths_for_commit_at(&lm->active_paths, c);
+ if (!*bitmap)
+ *bitmap = bitmap_word_alloc(lm->all_paths_nr / BITS_IN_EWORD + 1);
+
+ return *bitmap;
+}
+
+static void active_paths_free(struct last_modified *lm, struct commit *c)
+{
+ struct bitmap **bitmap = active_paths_for_commit_at(&lm->active_paths, c);
+ if (*bitmap) {
+ bitmap_free(*bitmap);
+ *bitmap = NULL;
+ }
+}
+
static void last_modified_release(struct last_modified *lm)
{
struct hashmap_iter iter;
@@ -54,6 +92,8 @@ static void last_modified_release(struct last_modified *lm)
hashmap_clear_and_free(&lm->paths, struct last_modified_entry, hashent);
release_revisions(&lm->rev);
+
+ free(lm->all_paths);
}
struct last_modified_callback_data {
@@ -146,7 +186,7 @@ static void mark_path(const char *path, const struct object_id *oid,
* Is it arriving at a version of interest, or is it from a side branch
* which did not contribute to the final state?
*/
- if (!oideq(oid, &ent->oid))
+ if (oid && !oideq(oid, &ent->oid))
return;
last_modified_emit(data->lm, path, data->commit);
@@ -196,7 +236,17 @@ static void last_modified_diff(struct diff_queue_struct *q,
}
}
-static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
+static void pass_to_parent(struct bitmap *c,
+ struct bitmap *p,
+ size_t pos)
+{
+ bitmap_unset(c, pos);
+ bitmap_set(p, pos);
+}
+
+static bool maybe_changed_path(struct last_modified *lm,
+ struct commit *origin,
+ struct bitmap *active)
{
struct bloom_filter *filter;
struct last_modified_entry *ent;
@@ -213,6 +263,9 @@ static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
return true;
hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) {
+ if (active && !bitmap_get(active, ent->diff_idx))
+ continue;
+
if (bloom_filter_contains(filter, &ent->key,
lm->rev.bloom_filter_settings))
return true;
@@ -220,42 +273,202 @@ static bool maybe_changed_path(struct last_modified *lm, struct commit *origin)
return false;
}
+static void process_parent(struct last_modified *lm,
+ struct prio_queue *queue,
+ struct commit *c, struct bitmap *active_c,
+ struct commit *parent, int parent_i)
+{
+ struct bitmap *active_p;
+
+ repo_parse_commit(lm->rev.repo, parent);
+ active_p = active_paths_for(lm, parent);
+
+ /*
+ * The first time entering this function for this commit (i.e. first parent)
+ * see if Bloom filters will tell us it's worth to do the diff.
+ */
+ if (parent_i || maybe_changed_path(lm, c, active_c)) {
+ diff_tree_oid(&parent->object.oid,
+ &c->object.oid, "", &lm->rev.diffopt);
+ diffcore_std(&lm->rev.diffopt);
+ }
+
+ /*
+ * Test each path for TREESAME-ness against the parent. If a path is
+ * TREESAME, pass it on to this parent.
+ *
+ * First, collect all paths that are *not* TREESAME in 'scratch'.
+ * Then, pass paths that *are* TREESAME and active to the parent.
+ */
+ for (int i = 0; i < diff_queued_diff.nr; i++) {
+ struct diff_filepair *fp = diff_queued_diff.queue[i];
+ const char *path = fp->two->path;
+ struct last_modified_entry *ent =
+ hashmap_get_entry_from_hash(&lm->paths, strhash(path), path,
+ struct last_modified_entry, hashent);
+ if (ent) {
+ size_t k = ent->diff_idx;
+ if (bitmap_get(active_c, k))
+ bitmap_set(lm->scratch, k);
+ }
+ }
+ for (size_t i = 0; i < lm->all_paths_nr; i++) {
+ if (bitmap_get(active_c, i) && !bitmap_get(lm->scratch, i))
+ pass_to_parent(active_c, active_p, i);
+ }
+
+ /*
+ * If parent has any active paths, put it on the queue (if not already).
+ */
+ if (!bitmap_is_empty(active_p) && !(parent->object.flags & PARENT1)) {
+ parent->object.flags |= PARENT1;
+ prio_queue_put(queue, parent);
+ }
+ if (!(parent->object.flags & PARENT1))
+ active_paths_free(lm, parent);
+
+ memset(lm->scratch->words, 0x0, lm->scratch->word_alloc);
+ diff_queue_clear(&diff_queued_diff);
+}
+
static int last_modified_run(struct last_modified *lm)
{
+ int max_count, queue_popped = 0;
+ struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+ struct prio_queue not_queue = { compare_commits_by_gen_then_commit_date };
+ struct commit_list *list;
struct last_modified_callback_data data = { .lm = lm };
lm->rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
lm->rev.diffopt.format_callback = last_modified_diff;
lm->rev.diffopt.format_callback_data = &data;
+ lm->rev.no_walk = 1;
prepare_revision_walk(&lm->rev);
- while (hashmap_get_size(&lm->paths)) {
- data.commit = get_revision(&lm->rev);
- if (!data.commit)
- BUG("paths remaining beyond boundary in last-modified");
+ max_count = lm->rev.max_count;
+
+ init_active_paths_for_commit(&lm->active_paths);
+ lm->scratch = bitmap_word_alloc(lm->all_paths_nr);
+
+ /*
+ * lm->rev.commits holds the set of boundary commits for our walk.
+ *
+ * Loop through each such commit, and place it in the appropriate queue.
+ */
+ for (list = lm->rev.commits; list; list = list->next) {
+ struct commit *c = list->item;
+
+ if (c->object.flags & BOTTOM) {
+ prio_queue_put(&not_queue, c);
+ c->object.flags |= PARENT2;
+ } else if (!(c->object.flags & PARENT1)) {
+ /*
+ * If the commit is a starting point (and hasn't been
+ * seen yet), then initialize the set of interesting
+ * paths, too.
+ */
+ struct bitmap *active;
+
+ prio_queue_put(&queue, c);
+ c->object.flags |= PARENT1;
+
+ active = active_paths_for(lm, c);
+ for (size_t i = 0; i < lm->all_paths_nr; i++)
+ bitmap_set(active, i);
+ }
+ }
- if (data.commit->object.flags & BOUNDARY) {
+ while (queue.nr) {
+ int parent_i;
+ struct commit_list *p;
+ struct commit *c = prio_queue_get(&queue);
+ struct bitmap *active_c = active_paths_for(lm, c);
+
+ if ((0 <= max_count && max_count < ++queue_popped) ||
+ (c->object.flags & PARENT2)) {
+ /*
+ * Either a boundary commit, or we have already seen too
+ * many others. Either way, stop here.
+ */
+ c->object.flags |= PARENT2 | BOUNDARY;
+ data.commit = c;
diff_tree_oid(lm->rev.repo->hash_algo->empty_tree,
- &data.commit->object.oid, "",
- &lm->rev.diffopt);
+ &c->object.oid,
+ "", &lm->rev.diffopt);
diff_flush(&lm->rev.diffopt);
+ goto cleanup;
+ }
- break;
+ /*
+ * Otherwise, make sure that 'c' isn't reachable from anything
+ * in the '--not' queue.
+ */
+ repo_parse_commit(lm->rev.repo, c);
+
+ while (not_queue.nr) {
+ struct commit_list *np;
+ struct commit *n = prio_queue_get(&not_queue);
+
+ repo_parse_commit(lm->rev.repo, n);
+
+ for (np = n->parents; np; np = np->next) {
+ if (!(np->item->object.flags & PARENT2)) {
+ prio_queue_put(&not_queue, np->item);
+ np->item->object.flags |= PARENT2;
+ }
+ }
+
+ if (commit_graph_generation(n) < commit_graph_generation(c))
+ break;
}
- if (!maybe_changed_path(lm, data.commit))
- continue;
+ /*
+ * Look at each parent and pass on each path that's TREESAME
+ * with that parent. Stop early when no active paths remain.
+ */
+ for (p = c->parents, parent_i = 0; p; p = p->next, parent_i++) {
+ process_parent(lm, &queue,
+ c, active_c,
+ p->item, parent_i);
+
+ if (bitmap_is_empty(active_c))
+ break;
+ }
+
+ /*
+ * Paths that remain active, or not TREESAME with any parent,
+ * were changed by 'c'.
+ */
+ if (!bitmap_is_empty(active_c)) {
+ data.commit = c;
+ for (size_t i = 0; i < lm->all_paths_nr; i++) {
+ if (bitmap_get(active_c, i))
+ mark_path(lm->all_paths[i], NULL, &data);
+ }
+ }
- log_tree_commit(&lm->rev, data.commit);
+cleanup:
+ active_paths_free(lm, c);
}
+ if (hashmap_get_size(&lm->paths))
+ BUG("paths remaining beyond boundary in last-modified");
+
+ clear_prio_queue(&not_queue);
+ clear_prio_queue(&queue);
+ clear_active_paths_for_commit(&lm->active_paths);
+ bitmap_free(lm->scratch);
+
return 0;
}
static int last_modified_init(struct last_modified *lm, struct repository *r,
const char *prefix, int argc, const char **argv)
{
+ struct hashmap_iter iter;
+ struct last_modified_entry *ent;
+
hashmap_init(&lm->paths, last_modified_entry_hashcmp, NULL, 0);
repo_init_revisions(r, &lm->rev, prefix);
@@ -280,6 +493,13 @@ static int last_modified_init(struct last_modified *lm, struct repository *r,
if (populate_paths_from_revs(lm) < 0)
return error(_("unable to setup last-modified"));
+ CALLOC_ARRAY(lm->all_paths, hashmap_get_size(&lm->paths));
+ lm->all_paths_nr = 0;
+ hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) {
+ ent->diff_idx = lm->all_paths_nr++;
+ lm->all_paths[ent->diff_idx] = ent->path;
+ }
+
return 0;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index df09000b30..fe77829557 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -156,7 +156,7 @@ int cmd_ls_remote(int argc,
continue;
if (!tail_match(&pattern, ref->name))
continue;
- item = ref_array_push(&ref_array, ref->name, &ref->old_oid);
+ item = ref_array_push(&ref_array, ref->name, &ref->old_oid, NULL);
item->symref = xstrdup_or_null(ref->symref);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 74512e54a3..615f7d1aae 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -339,10 +339,9 @@ static int cmp_by_tag_and_age(const void *a_, const void *b_)
return a->taggerdate != b->taggerdate;
}
-static int name_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
- int flags UNUSED, void *cb_data)
+static int name_ref(const struct reference *ref, void *cb_data)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(the_repository, ref->oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
@@ -350,14 +349,14 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
struct commit *commit = NULL;
timestamp_t taggerdate = TIME_MAX;
- if (data->tags_only && !starts_with(path, "refs/tags/"))
+ if (data->tags_only && !starts_with(ref->name, "refs/tags/"))
return 0;
if (data->exclude_filters.nr) {
struct string_list_item *item;
for_each_string_list_item(item, &data->exclude_filters) {
- if (subpath_matches(path, item->string) >= 0)
+ if (subpath_matches(ref->name, item->string) >= 0)
return 0;
}
}
@@ -378,7 +377,7 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
- switch (subpath_matches(path, item->string)) {
+ switch (subpath_matches(ref->name, item->string)) {
case -1: /* did not match */
break;
case 0: /* matched fully */
@@ -406,13 +405,13 @@ static int name_ref(const char *path, const char *referent UNUSED, const struct
}
if (o && o->type == OBJ_COMMIT) {
commit = (struct commit *)o;
- from_tag = starts_with(path, "refs/tags/");
+ from_tag = starts_with(ref->name, "refs/tags/");
if (taggerdate == TIME_MAX)
taggerdate = commit->date;
}
- add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
- from_tag, deref);
+ add_to_tip_table(ref->oid, ref->name, can_abbreviate_output,
+ commit, taggerdate, from_tag, deref);
return 0;
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5bdc44fb2d..7937106ec5 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -831,15 +831,14 @@ static enum write_one_status write_one(struct hashfile *f,
return WRITE_ONE_WRITTEN;
}
-static int mark_tagged(const char *path UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int mark_tagged(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid);
+ struct object_entry *entry = packlist_find(&to_pack, ref->oid);
if (entry)
entry->tagged = 1;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
@@ -1706,8 +1705,8 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
uint32_t found_mtime)
{
int want;
+ struct packfile_list_entry *e;
struct odb_source *source;
- struct list_head *pos;
if (!exclude && local) {
/*
@@ -1716,7 +1715,7 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
*/
struct odb_source *source = the_repository->objects->sources->next;
for (; source; source = source->next)
- if (has_loose_object(source, oid))
+ if (odb_source_loose_has_object(source, oid))
return 0;
}
@@ -1748,12 +1747,11 @@ static int want_object_in_pack_mtime(const struct object_id *oid,
}
}
- list_for_each(pos, packfile_store_get_packs_mru(the_repository->objects->packfiles)) {
- struct packed_git *p = list_entry(pos, struct packed_git, mru);
+ for (e = the_repository->objects->packfiles->packs.head; e; e = e->next) {
+ struct packed_git *p = e->pack;
want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime);
if (!exclude && want > 0)
- list_move(&p->mru,
- packfile_store_get_packs_mru(the_repository->objects->packfiles));
+ packfile_list_prepend(&the_repository->objects->packfiles->packs, p);
if (want != -1)
return want;
}
@@ -3306,13 +3304,13 @@ static void add_tag_chain(const struct object_id *oid)
}
}
-static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int add_ref_tag(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id peeled;
- if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
- add_tag_chain(oid);
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled) &&
+ obj_is_packed(&peeled))
+ add_tag_chain(ref->oid);
return 0;
}
@@ -3831,12 +3829,10 @@ static int pack_mtime_cmp(const void *_a, const void *_b)
static void read_packs_list_from_stdin(struct rev_info *revs)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct strbuf buf = STRBUF_INIT;
struct string_list include_packs = STRING_LIST_INIT_DUP;
struct string_list exclude_packs = STRING_LIST_INIT_DUP;
struct string_list_item *item = NULL;
-
struct packed_git *p;
while (strbuf_getline(&buf, stdin) != EOF) {
@@ -3856,7 +3852,7 @@ static void read_packs_list_from_stdin(struct rev_info *revs)
string_list_sort(&exclude_packs);
string_list_remove_duplicates(&exclude_packs, 0);
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
const char *pack_name = pack_basename(p);
if ((item = string_list_lookup(&include_packs, pack_name)))
@@ -3980,7 +3976,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
int found = 0;
for (; !found && source; source = source->next)
- if (has_loose_object(source, oid))
+ if (odb_source_loose_has_object(source, oid))
found = 1;
/*
@@ -4077,7 +4073,6 @@ static void enumerate_cruft_objects(void)
static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
struct rev_info revs;
int ret;
@@ -4107,7 +4102,7 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs
* Re-mark only the fresh packs as kept so that objects in
* unknown packs do not halt the reachability traversal early.
*/
- for (p = packfile_store_get_all_packs(packs); p; p = p->next)
+ repo_for_each_pack(the_repository, p)
p->pack_keep_in_core = 0;
mark_pack_kept_in_core(fresh_packs, 1);
@@ -4124,7 +4119,6 @@ static void enumerate_and_traverse_cruft_objects(struct string_list *fresh_packs
static void read_cruft_objects(void)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct strbuf buf = STRBUF_INIT;
struct string_list discard_packs = STRING_LIST_INIT_DUP;
struct string_list fresh_packs = STRING_LIST_INIT_DUP;
@@ -4145,7 +4139,7 @@ static void read_cruft_objects(void)
string_list_sort(&discard_packs);
string_list_sort(&fresh_packs);
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
const char *pack_name = pack_basename(p);
struct string_list_item *item;
@@ -4393,27 +4387,27 @@ static void add_unreachable_loose_objects(struct rev_info *revs)
static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
- static struct packed_git *last_found = (void *)1;
+ static struct packed_git *last_found = NULL;
struct packed_git *p;
- p = (last_found != (void *)1) ? last_found :
- packfile_store_get_all_packs(packs);
+ if (last_found && find_pack_entry_one(oid, last_found))
+ return 1;
- while (p) {
- if ((!p->pack_local || p->pack_keep ||
- p->pack_keep_in_core) &&
- find_pack_entry_one(oid, p)) {
+ repo_for_each_pack(the_repository, p) {
+ /*
+ * We have already checked `last_found`, so there is no need to
+ * re-check here.
+ */
+ if (p == last_found)
+ continue;
+
+ if ((!p->pack_local || p->pack_keep || p->pack_keep_in_core) &&
+ find_pack_entry_one(oid, p)) {
last_found = p;
return 1;
}
- if (p == last_found)
- p = packfile_store_get_all_packs(packs);
- else
- p = p->next;
- if (p == last_found)
- p = p->next;
}
+
return 0;
}
@@ -4440,13 +4434,12 @@ static int loosened_object_can_be_discarded(const struct object_id *oid,
static void loosen_unused_packed_objects(void)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
uint32_t i;
uint32_t loosened_objects_nr = 0;
struct object_id oid;
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
continue;
@@ -4533,19 +4526,16 @@ static void record_recent_commit(struct commit *commit, void *data UNUSED)
oid_array_append(&recent_objects, &commit->object.oid);
}
-static int mark_bitmap_preferred_tip(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *data UNUSED)
+static int mark_bitmap_preferred_tip(const struct reference *ref, void *data UNUSED)
{
+ const struct object_id *maybe_peeled = ref->oid;
struct object_id peeled;
struct object *object;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
+ maybe_peeled = &peeled;
- object = parse_object_or_die(the_repository, oid, refname);
+ object = parse_object_or_die(the_repository, maybe_peeled, ref->name);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
@@ -4747,13 +4737,12 @@ static void get_object_list(struct rev_info *revs, struct strvec *argv)
static void add_extra_kept_packs(const struct string_list *names)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
if (!names->nr)
return;
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
const char *name = basename(p->pack_name);
int i;
@@ -5191,10 +5180,9 @@ int cmd_pack_objects(int argc,
add_extra_kept_packs(&keep_pack_list);
if (ignore_packed_keep_on_disk) {
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
- for (p = packfile_store_get_all_packs(packs); p; p = p->next)
+ repo_for_each_pack(the_repository, p)
if (p->pack_local && p->pack_keep)
break;
if (!p) /* no keep-able packs found */
@@ -5206,10 +5194,9 @@ int cmd_pack_objects(int argc,
* want to unset "local" based on looking at packs, as
* it also covers non-local objects
*/
- struct packfile_store *packs = the_repository->objects->packfiles;
struct packed_git *p;
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
+ repo_for_each_pack(the_repository, p) {
if (!p->pack_local) {
have_non_local_packs = 1;
break;
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 80743d8806..e4ecf774ca 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -566,29 +566,23 @@ static struct pack_list * add_pack(struct packed_git *p)
static struct pack_list * add_pack_file(const char *filename)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
- struct packed_git *p = packfile_store_get_all_packs(packs);
+ struct packed_git *p;
if (strlen(filename) < 40)
die("Bad pack filename: %s", filename);
- while (p) {
+ repo_for_each_pack(the_repository, p)
if (strstr(p->pack_name, filename))
return add_pack(p);
- p = p->next;
- }
die("Filename %s not found in packed_git", filename);
}
static void load_all(void)
{
- struct packfile_store *packs = the_repository->objects->packfiles;
- struct packed_git *p = packfile_store_get_all_packs(packs);
+ struct packed_git *p;
- while (p) {
+ repo_for_each_pack(the_repository, p)
add_pack(p);
- p = p->next;
- }
}
int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, struct repository *repo UNUSED) {
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c468828189..a88abe08b4 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -36,6 +36,7 @@
#include "reset.h"
#include "trace2.h"
#include "hook.h"
+#include "trailer.h"
static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -113,6 +114,7 @@ struct rebase_options {
enum action action;
char *reflog_action;
int signoff;
+ struct strvec trailer_args;
int allow_rerere_autoupdate;
int keep_empty;
int autosquash;
@@ -143,6 +145,7 @@ struct rebase_options {
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
.exec = STRING_LIST_INIT_NODUP, \
+ .trailer_args = STRVEC_INIT, \
.git_format_patch_opt = STRBUF_INIT, \
.fork_point = -1, \
.reapply_cherry_picks = -1, \
@@ -166,6 +169,7 @@ static void rebase_options_release(struct rebase_options *opts)
free(opts->strategy);
string_list_clear(&opts->strategy_opts, 0);
strbuf_release(&opts->git_format_patch_opt);
+ strvec_clear(&opts->trailer_args);
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -177,6 +181,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
sequencer_init_config(&replay);
replay.signoff = opts->signoff;
+
+ for (size_t i = 0; i < opts->trailer_args.nr; i++)
+ strvec_push(&replay.trailer_args, opts->trailer_args.v[i]);
+
replay.allow_ff = !(opts->flags & REBASE_FORCE);
if (opts->allow_rerere_autoupdate)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
@@ -500,6 +508,23 @@ static int read_basic_state(struct rebase_options *opts)
opts->gpg_sign_opt = xstrdup(buf.buf);
}
+ strbuf_reset(&buf);
+
+ if (strbuf_read_file(&buf, state_dir_path("trailer", opts), 0) >= 0) {
+ const char *p = buf.buf, *end = buf.buf + buf.len;
+
+ while (p < end) {
+ char *nl = memchr(p, '\n', end - p);
+ if (!nl)
+ die("nl shouldn't be NULL");
+ *nl = '\0';
+
+ if (*p)
+ strvec_push(&opts->trailer_args, p);
+
+ p = nl + 1;
+ }
+ }
strbuf_release(&buf);
return 0;
@@ -528,6 +553,21 @@ static int rebase_write_basic_state(struct rebase_options *opts)
if (opts->signoff)
write_file(state_dir_path("signoff", opts), "--signoff");
+ /*
+ * save opts->trailer_args into state_dir/trailer
+ */
+ if (opts->trailer_args.nr) {
+ struct strbuf buf = STRBUF_INIT;
+
+ for (size_t i = 0; i < opts->trailer_args.nr; i++) {
+ strbuf_addstr(&buf, opts->trailer_args.v[i]);
+ strbuf_addch(&buf, '\n');
+ }
+ write_file(state_dir_path("trailer", opts),
+ "%s", buf.buf);
+ strbuf_release(&buf);
+ }
+
return 0;
}
@@ -1132,6 +1172,8 @@ int cmd_rebase(int argc,
.flags = PARSE_OPT_NOARG,
.defval = REBASE_DIFFSTAT,
},
+ OPT_STRVEC(0, "trailer", &options.trailer_args, N_("trailer"),
+ N_("add custom trailer(s)")),
OPT_BOOL(0, "signoff", &options.signoff,
N_("add a Signed-off-by trailer to each commit")),
OPT_BOOL(0, "committer-date-is-author-date",
@@ -1285,6 +1327,11 @@ int cmd_rebase(int argc,
builtin_rebase_options,
builtin_rebase_usage, 0);
+ if (options.trailer_args.nr) {
+ validate_trailer_args_after_config(&options.trailer_args);
+ options.flags |= REBASE_FORCE;
+ }
+
if (preserve_merges_selected)
die(_("--preserve-merges was replaced by --rebase-merges\n"
"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
@@ -1542,6 +1589,9 @@ int cmd_rebase(int argc,
if (options.root && !options.onto_name)
imply_merge(&options, "--root without --onto");
+ if (options.trailer_args.nr)
+ imply_merge(&options, "--trailer");
+
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c9288a9c7e..cfc1eeedd5 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -305,13 +305,12 @@ static void show_ref(const char *path, const struct object_id *oid)
}
}
-static int show_ref_cb(const char *path_full, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *data)
+static int show_ref_cb(const struct reference *ref, void *data)
{
struct oidset *seen = data;
- const char *path = strip_namespace(path_full);
+ const char *path = strip_namespace(ref->name);
- if (ref_is_hidden(path, path_full, &hidden_refs))
+ if (ref_is_hidden(path, ref->name, &hidden_refs))
return 0;
/*
@@ -320,13 +319,13 @@ static int show_ref_cb(const char *path_full, const char *referent UNUSED, const
* transfer but will otherwise ignore them.
*/
if (!path) {
- if (oidset_insert(seen, oid))
+ if (oidset_insert(seen, ref->oid))
return 0;
path = ".have";
} else {
- oidset_insert(seen, oid);
+ oidset_insert(seen, ref->oid);
}
- show_ref(path, oid);
+ show_ref(path, ref->oid);
return 0;
}
@@ -749,7 +748,7 @@ static int check_cert_push_options(const struct string_list *push_options)
return retval;
}
-static void prepare_push_cert_sha1(struct child_process *proc)
+static void prepare_push_cert_sha1(struct run_hooks_opt *opt)
{
static int already_done;
@@ -775,147 +774,132 @@ static void prepare_push_cert_sha1(struct child_process *proc)
nonce_status = check_nonce(sigcheck.payload);
}
if (!is_null_oid(&push_cert_oid)) {
- strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
oid_to_hex(&push_cert_oid));
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
sigcheck.signer ? sigcheck.signer : "");
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
sigcheck.key ? sigcheck.key : "");
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
sigcheck.result);
if (push_cert_nonce) {
- strvec_pushf(&proc->env,
+ strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE=%s",
push_cert_nonce);
- strvec_pushf(&proc->env,
+ strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE_STATUS=%s",
nonce_status);
if (nonce_status == NONCE_SLOP)
- strvec_pushf(&proc->env,
+ strvec_pushf(&opt->env,
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
nonce_stamp_slop);
}
}
}
+struct receive_hook_feed_context {
+ struct command *cmd;
+ int skip_broken;
+};
+
struct receive_hook_feed_state {
struct command *cmd;
struct ref_push_report *report;
int skip_broken;
struct strbuf buf;
- const struct string_list *push_options;
};
-typedef int (*feed_fn)(void *, const char **, size_t *);
-static int run_and_feed_hook(const char *hook_name, feed_fn feed,
- struct receive_hook_feed_state *feed_state)
+static int feed_receive_hook(int hook_stdin_fd, struct receive_hook_feed_state *state, int lines_batch_size)
{
- struct child_process proc = CHILD_PROCESS_INIT;
- struct async muxer;
- int code;
- const char *hook_path = find_hook(the_repository, hook_name);
+ struct command *cmd = state->cmd;
- if (!hook_path)
- return 0;
+ strbuf_reset(&state->buf);
- strvec_push(&proc.args, hook_path);
- proc.in = -1;
- proc.stdout_to_stderr = 1;
- proc.trace2_hook_name = hook_name;
-
- if (feed_state->push_options) {
- size_t i;
- for (i = 0; i < feed_state->push_options->nr; i++)
- strvec_pushf(&proc.env,
- "GIT_PUSH_OPTION_%"PRIuMAX"=%s",
- (uintmax_t)i,
- feed_state->push_options->items[i].string);
- strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
- (uintmax_t)feed_state->push_options->nr);
- } else
- strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT");
+ /* batch lines to avoid going through run-command's ppoll for each line */
+ for (int i = 0; i < lines_batch_size; i++) {
+ while (cmd &&
+ state->skip_broken && (cmd->error_string || cmd->did_not_exist))
+ cmd = cmd->next;
- if (tmp_objdir)
- strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
+ if (!cmd)
+ break; /* no more commands left */
- if (use_sideband) {
- memset(&muxer, 0, sizeof(muxer));
- muxer.proc = copy_to_sideband;
- muxer.in = -1;
- code = start_async(&muxer);
- if (code)
- return code;
- proc.err = muxer.in;
- }
+ if (!state->report)
+ state->report = cmd->report;
- prepare_push_cert_sha1(&proc);
+ if (state->report) {
+ struct object_id *old_oid;
+ struct object_id *new_oid;
+ const char *ref_name;
- code = start_command(&proc);
- if (code) {
- if (use_sideband)
- finish_async(&muxer);
- return code;
- }
+ old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
+ new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
+ ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
- sigchain_push(SIGPIPE, SIG_IGN);
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ oid_to_hex(old_oid), oid_to_hex(new_oid),
+ ref_name);
- while (1) {
- const char *buf;
- size_t n;
- if (feed(feed_state, &buf, &n))
- break;
- if (write_in_full(proc.in, buf, n) < 0)
- break;
+ state->report = state->report->next;
+ if (!state->report)
+ cmd = cmd->next;
+ } else {
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
+ cmd->ref_name);
+ cmd = cmd->next;
+ }
}
- close(proc.in);
- if (use_sideband)
- finish_async(&muxer);
- sigchain_pop(SIGPIPE);
+ state->cmd = cmd;
- return finish_command(&proc);
+ if (state->buf.len > 0) {
+ int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
+ if (ret < 0) {
+ if (errno == EPIPE)
+ return 1; /* child closed pipe */
+ return ret;
+ }
+ }
+
+ return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
}
-static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb, void *pp_task_cb UNUSED)
{
- struct receive_hook_feed_state *state = state_;
- struct command *cmd = state->cmd;
+ struct hook_cb_data *hook_cb = pp_cb;
+ struct receive_hook_feed_state *feed_state = hook_cb->options->feed_pipe_cb_data;
- while (cmd &&
- state->skip_broken && (cmd->error_string || cmd->did_not_exist))
- cmd = cmd->next;
- if (!cmd)
- return -1; /* EOF */
- if (!bufp)
- return 0; /* OK, can feed something. */
- strbuf_reset(&state->buf);
- if (!state->report)
- state->report = cmd->report;
- if (state->report) {
- struct object_id *old_oid;
- struct object_id *new_oid;
- const char *ref_name;
-
- old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
- new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
- ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
- strbuf_addf(&state->buf, "%s %s %s\n",
- oid_to_hex(old_oid), oid_to_hex(new_oid),
- ref_name);
- state->report = state->report->next;
- if (!state->report)
- state->cmd = cmd->next;
- } else {
- strbuf_addf(&state->buf, "%s %s %s\n",
- oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
- cmd->ref_name);
- state->cmd = cmd->next;
- }
- if (bufp) {
- *bufp = state->buf.buf;
- *sizep = state->buf.len;
+ /* first-time setup */
+ if (!hook_cb->options->feed_pipe_cb_data) {
+ struct receive_hook_feed_context *ctx = hook_cb->options->feed_pipe_ctx;
+ if (!ctx)
+ BUG("run_hooks_opt.feed_pipe_ctx required for receive hook");
+
+ hook_cb->options->feed_pipe_cb_data = xmalloc(sizeof(struct receive_hook_feed_state));
+ feed_state = hook_cb->options->feed_pipe_cb_data;
+ strbuf_init(&feed_state->buf, 0);
+ feed_state->cmd = ctx->cmd;
+ feed_state->skip_broken = ctx->skip_broken;
+ feed_state->report = NULL;
}
- return 0;
+
+ /* batch 500 lines at once to avoid going through the run-command ppoll loop too often */
+ if (feed_receive_hook(hook_stdin_fd, feed_state, 500) == 0)
+ return 0; /* still have more data to feed */
+
+ strbuf_release(&feed_state->buf);
+
+ if (hook_cb->options->feed_pipe_cb_data)
+ FREE_AND_NULL(hook_cb->options->feed_pipe_cb_data);
+
+ return 1; /* done feeding, run-command can close pipe */
+}
+
+static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
+{
+ if (output && output->len)
+ send_sideband(1, 2, output->buf, output->len, use_sideband);
}
static int run_receive_hook(struct command *commands,
@@ -923,47 +907,58 @@ static int run_receive_hook(struct command *commands,
int skip_broken,
const struct string_list *push_options)
{
- struct receive_hook_feed_state state;
- int status;
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+ struct receive_hook_feed_context ctx;
+ struct command *iter = commands;
- strbuf_init(&state.buf, 0);
- state.cmd = commands;
- state.skip_broken = skip_broken;
- state.report = NULL;
- if (feed_receive_hook(&state, NULL, NULL))
+ /* if there are no valid commands, don't invoke the hook at all. */
+ while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
+ iter = iter->next;
+ if (!iter)
return 0;
- state.cmd = commands;
- state.push_options = push_options;
- status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
- strbuf_release(&state.buf);
- return status;
+
+ if (push_options) {
+ int i;
+ for (i = 0; i < push_options->nr; i++)
+ strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i,
+ push_options->items[i].string);
+ strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
+ (uintmax_t)push_options->nr);
+ } else
+ strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
+
+ if (tmp_objdir)
+ strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
+
+ prepare_push_cert_sha1(&opt);
+
+ /* set up sideband printer */
+ if (use_sideband)
+ opt.consume_sideband = hook_output_to_sideband;
+
+ /* set up stdin callback */
+ ctx.cmd = commands;
+ ctx.skip_broken = skip_broken;
+ opt.feed_pipe = feed_receive_hook_cb;
+ opt.feed_pipe_ctx = &ctx;
+
+ return run_hooks_opt(the_repository, hook_name, &opt);
}
static int run_update_hook(struct command *cmd)
{
- struct child_process proc = CHILD_PROCESS_INIT;
- int code;
- const char *hook_path = find_hook(the_repository, "update");
-
- if (!hook_path)
- return 0;
-
- strvec_push(&proc.args, hook_path);
- strvec_push(&proc.args, cmd->ref_name);
- strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
- strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
- proc.no_stdin = 1;
- proc.stdout_to_stderr = 1;
- proc.err = use_sideband ? -1 : 0;
- proc.trace2_hook_name = "update";
+ strvec_pushl(&opt.args,
+ cmd->ref_name,
+ oid_to_hex(&cmd->old_oid),
+ oid_to_hex(&cmd->new_oid),
+ NULL);
- code = start_command(&proc);
- if (code)
- return code;
if (use_sideband)
- copy_to_sideband(proc.err, -1, NULL);
- return finish_command(&proc);
+ opt.consume_sideband = hook_output_to_sideband;
+
+ return run_hooks_opt(the_repository, "update", &opt);
}
static struct command *find_command_by_refname(struct command *list,
@@ -1640,33 +1635,20 @@ out:
static void run_update_post_hook(struct command *commands)
{
struct command *cmd;
- struct child_process proc = CHILD_PROCESS_INIT;
- const char *hook;
-
- hook = find_hook(the_repository, "post-update");
- if (!hook)
- return;
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
for (cmd = commands; cmd; cmd = cmd->next) {
if (cmd->error_string || cmd->did_not_exist)
continue;
- if (!proc.args.nr)
- strvec_push(&proc.args, hook);
- strvec_push(&proc.args, cmd->ref_name);
+ strvec_push(&opt.args, cmd->ref_name);
}
- if (!proc.args.nr)
+ if (!opt.args.nr)
return;
- proc.no_stdin = 1;
- proc.stdout_to_stderr = 1;
- proc.err = use_sideband ? -1 : 0;
- proc.trace2_hook_name = "post-update";
+ if (use_sideband)
+ opt.consume_sideband = hook_output_to_sideband;
- if (!start_command(&proc)) {
- if (use_sideband)
- copy_to_sideband(proc.err, -1, NULL);
- finish_command(&proc);
- }
+ run_hooks_opt(the_repository, "post-update", &opt);
}
static void check_aliased_update_internal(struct command *cmd,
diff --git a/builtin/remote.c b/builtin/remote.c
index 8a7ed4299a..7ffc14ba15 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -570,17 +570,14 @@ struct branches_for_remote {
struct known_remotes *keep;
};
-static int add_branch_for_removal(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int add_branch_for_removal(const struct reference *ref, void *cb_data)
{
struct branches_for_remote *branches = cb_data;
struct refspec_item refspec;
struct known_remote *kr;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (remote_find_tracking(branches->remote, &refspec))
return 0;
free(refspec.src);
@@ -588,7 +585,7 @@ static int add_branch_for_removal(const char *refname,
/* don't delete a branch if another remote also uses it */
for (kr = branches->keep->list; kr; kr = kr->next) {
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(kr->remote, &refspec)) {
free(refspec.src);
return 0;
@@ -596,16 +593,16 @@ static int add_branch_for_removal(const char *refname,
}
/* don't delete non-remote-tracking refs */
- if (!starts_with(refname, "refs/remotes/")) {
+ if (!starts_with(ref->name, "refs/remotes/")) {
/* advise user how to delete local branches */
- if (starts_with(refname, "refs/heads/"))
+ if (starts_with(ref->name, "refs/heads/"))
string_list_append(branches->skipped,
- abbrev_branch(refname));
+ abbrev_branch(ref->name));
/* silently skip over other non-remote refs */
return 0;
}
- string_list_append(branches->branches, refname);
+ string_list_append(branches->branches, ref->name);
return 0;
}
@@ -713,18 +710,18 @@ out:
return error;
}
-static int rename_one_ref(const char *old_refname, const char *referent,
- const struct object_id *oid,
- int flags, void *cb_data)
+static int rename_one_ref(const struct reference *ref, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
+ const struct object_id *oid = ref->oid;
+ const char *referent = ref->target;
int error;
- compute_renamed_ref(rename, old_refname, &new_refname);
+ compute_renamed_ref(rename, ref->name, &new_refname);
- if (flags & REF_ISSYMREF) {
+ if (ref->flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
@@ -732,25 +729,25 @@ static int rename_one_ref(const char *old_refname, const char *referent,
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- old_refname, RESOLVE_REF_NO_RECURSE,
+ ref->name, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}
- error = ref_transaction_delete(rename->transaction, old_refname,
+ error = ref_transaction_delete(rename->transaction, ref->name,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;
error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
- (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
+ (ref->flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;
- error = rename_one_reflog(old_refname, oid, rename);
+ error = rename_one_reflog(ref->name, oid, rename);
if (error < 0)
goto out;
@@ -1125,19 +1122,16 @@ static void free_remote_ref_states(struct ref_states *states)
string_list_clear_func(&states->push, clear_push_info);
}
-static int append_ref_to_tracked_list(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags, void *cb_data)
+static int append_ref_to_tracked_list(const struct reference *ref, void *cb_data)
{
struct ref_states *states = cb_data;
struct refspec_item refspec;
- if (flags & REF_ISSYMREF)
+ if (ref->flags & REF_ISSYMREF)
return 0;
memset(&refspec, 0, sizeof(refspec));
- refspec.dst = (char *)refname;
+ refspec.dst = (char *)ref->name;
if (!remote_find_tracking(states->remote, &refspec)) {
string_list_append(&states->tracked, abbrev_branch(refspec.src));
free(refspec.src);
diff --git a/builtin/repack.c b/builtin/repack.c
index e8730808c5..cfdb4c0920 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -3,27 +3,18 @@
#include "builtin.h"
#include "config.h"
-#include "dir.h"
#include "environment.h"
-#include "gettext.h"
-#include "hex.h"
#include "parse-options.h"
#include "path.h"
#include "run-command.h"
#include "server-info.h"
-#include "strbuf.h"
#include "string-list.h"
-#include "strvec.h"
#include "midx.h"
#include "packfile.h"
#include "prune-packed.h"
-#include "odb.h"
#include "promisor-remote.h"
+#include "repack.h"
#include "shallow.h"
-#include "pack.h"
-#include "pack-bitmap.h"
-#include "refs.h"
-#include "list-objects-filter-options.h"
#define ALL_INTO_ONE 1
#define LOOSEN_UNREACHABLE 2
@@ -33,8 +24,6 @@
#define RETAIN_PACK 2
static int pack_everything;
-static int delta_base_offset = 1;
-static int pack_kept_objects = -1;
static int write_bitmaps = -1;
static int use_delta_islands;
static int run_update_server_info = 1;
@@ -53,31 +42,23 @@ static const char incremental_bitmap_conflict_error[] = N_(
"--no-write-bitmap-index or disable the pack.writeBitmaps configuration."
);
-struct pack_objects_args {
- char *window;
- char *window_memory;
- char *depth;
- char *threads;
- unsigned long max_pack_size;
- int no_reuse_delta;
- int no_reuse_object;
- int quiet;
- int local;
- int name_hash_version;
- int path_walk;
- struct list_objects_filter_options filter_options;
+struct repack_config_ctx {
+ struct pack_objects_args *po_args;
+ struct pack_objects_args *cruft_po_args;
};
static int repack_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
- struct pack_objects_args *cruft_po_args = cb;
+ struct repack_config_ctx *repack_ctx = cb;
+ struct pack_objects_args *po_args = repack_ctx->po_args;
+ struct pack_objects_args *cruft_po_args = repack_ctx->cruft_po_args;
if (!strcmp(var, "repack.usedeltabaseoffset")) {
- delta_base_offset = git_config_bool(var, value);
+ po_args->delta_base_offset = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "repack.packkeptobjects")) {
- pack_kept_objects = git_config_bool(var, value);
+ po_args->pack_kept_objects = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "repack.writebitmaps") ||
@@ -116,1138 +97,10 @@ static int repack_config(const char *var, const char *value,
return git_default_config(var, value, ctx, cb);
}
-static void pack_objects_args_release(struct pack_objects_args *args)
-{
- free(args->window);
- free(args->window_memory);
- free(args->depth);
- free(args->threads);
- list_objects_filter_release(&args->filter_options);
-}
-
-struct existing_packs {
- struct string_list kept_packs;
- struct string_list non_kept_packs;
- struct string_list cruft_packs;
-};
-
-#define EXISTING_PACKS_INIT { \
- .kept_packs = STRING_LIST_INIT_DUP, \
- .non_kept_packs = STRING_LIST_INIT_DUP, \
- .cruft_packs = STRING_LIST_INIT_DUP, \
-}
-
-static int has_existing_non_kept_packs(const struct existing_packs *existing)
-{
- return existing->non_kept_packs.nr || existing->cruft_packs.nr;
-}
-
-static void pack_mark_for_deletion(struct string_list_item *item)
-{
- item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
-}
-
-static void pack_unmark_for_deletion(struct string_list_item *item)
-{
- item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
-}
-
-static int pack_is_marked_for_deletion(struct string_list_item *item)
-{
- return (uintptr_t)item->util & DELETE_PACK;
-}
-
-static void pack_mark_retained(struct string_list_item *item)
-{
- item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
-}
-
-static int pack_is_retained(struct string_list_item *item)
-{
- return (uintptr_t)item->util & RETAIN_PACK;
-}
-
-static void mark_packs_for_deletion_1(struct string_list *names,
- struct string_list *list)
-{
- struct string_list_item *item;
- const int hexsz = the_hash_algo->hexsz;
-
- for_each_string_list_item(item, list) {
- char *sha1;
- size_t len = strlen(item->string);
- if (len < hexsz)
- continue;
- sha1 = item->string + len - hexsz;
-
- if (pack_is_retained(item)) {
- pack_unmark_for_deletion(item);
- } else if (!string_list_has_string(names, sha1)) {
- /*
- * 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).
- */
- pack_mark_for_deletion(item);
- }
- }
-}
-
-static void retain_cruft_pack(struct existing_packs *existing,
- struct packed_git *cruft)
-{
- struct strbuf buf = STRBUF_INIT;
- struct string_list_item *item;
-
- strbuf_addstr(&buf, pack_basename(cruft));
- strbuf_strip_suffix(&buf, ".pack");
-
- item = string_list_lookup(&existing->cruft_packs, buf.buf);
- if (!item)
- BUG("could not find cruft pack '%s'", pack_basename(cruft));
-
- pack_mark_retained(item);
- strbuf_release(&buf);
-}
-
-static void mark_packs_for_deletion(struct existing_packs *existing,
- struct string_list *names)
-
-{
- mark_packs_for_deletion_1(names, &existing->non_kept_packs);
- mark_packs_for_deletion_1(names, &existing->cruft_packs);
-}
-
-static void remove_redundant_pack(const char *dir_name, const char *base_name)
-{
- struct strbuf buf = STRBUF_INIT;
- struct odb_source *source = the_repository->objects->sources;
- struct multi_pack_index *m = get_multi_pack_index(source);
- strbuf_addf(&buf, "%s.pack", base_name);
- if (m && source->local && midx_contains_pack(m, buf.buf))
- clear_midx_file(the_repository);
- strbuf_insertf(&buf, 0, "%s/", dir_name);
- unlink_pack_path(buf.buf, 1);
- strbuf_release(&buf);
-}
-
-static void remove_redundant_packs_1(struct string_list *packs)
-{
- struct string_list_item *item;
- for_each_string_list_item(item, packs) {
- if (!pack_is_marked_for_deletion(item))
- continue;
- remove_redundant_pack(packdir, item->string);
- }
-}
-
-static void remove_redundant_existing_packs(struct existing_packs *existing)
-{
- remove_redundant_packs_1(&existing->non_kept_packs);
- remove_redundant_packs_1(&existing->cruft_packs);
-}
-
-static void existing_packs_release(struct existing_packs *existing)
-{
- string_list_clear(&existing->kept_packs, 0);
- string_list_clear(&existing->non_kept_packs, 0);
- string_list_clear(&existing->cruft_packs, 0);
-}
-
-/*
- * Adds all packs hex strings (pack-$HASH) to either packs->non_kept
- * or packs->kept 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 collect_pack_filenames(struct existing_packs *existing,
- const struct string_list *extra_keep)
-{
- struct packfile_store *packs = the_repository->objects->packfiles;
- struct packed_git *p;
- struct strbuf buf = STRBUF_INIT;
-
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
- int i;
- const char *base;
-
- if (!p->pack_local)
- continue;
-
- base = pack_basename(p);
-
- for (i = 0; i < extra_keep->nr; i++)
- if (!fspathcmp(base, extra_keep->items[i].string))
- break;
-
- strbuf_reset(&buf);
- strbuf_addstr(&buf, base);
- strbuf_strip_suffix(&buf, ".pack");
-
- if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep)
- string_list_append(&existing->kept_packs, buf.buf);
- else if (p->is_cruft)
- string_list_append(&existing->cruft_packs, buf.buf);
- else
- string_list_append(&existing->non_kept_packs, buf.buf);
- }
-
- string_list_sort(&existing->kept_packs);
- string_list_sort(&existing->non_kept_packs);
- string_list_sort(&existing->cruft_packs);
- strbuf_release(&buf);
-}
-
-static void prepare_pack_objects(struct child_process *cmd,
- const struct pack_objects_args *args,
- const char *out)
-{
- strvec_push(&cmd->args, "pack-objects");
- if (args->window)
- strvec_pushf(&cmd->args, "--window=%s", args->window);
- if (args->window_memory)
- strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
- if (args->depth)
- strvec_pushf(&cmd->args, "--depth=%s", args->depth);
- if (args->threads)
- strvec_pushf(&cmd->args, "--threads=%s", args->threads);
- if (args->max_pack_size)
- strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
- if (args->no_reuse_delta)
- strvec_pushf(&cmd->args, "--no-reuse-delta");
- if (args->no_reuse_object)
- strvec_pushf(&cmd->args, "--no-reuse-object");
- if (args->name_hash_version)
- strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version);
- if (args->path_walk)
- strvec_pushf(&cmd->args, "--path-walk");
- if (args->local)
- strvec_push(&cmd->args, "--local");
- if (args->quiet)
- strvec_push(&cmd->args, "--quiet");
- if (delta_base_offset)
- strvec_push(&cmd->args, "--delta-base-offset");
- strvec_push(&cmd->args, out);
- cmd->git_cmd = 1;
- cmd->out = -1;
-}
-
-/*
- * Write oid to the given struct child_process's stdin, starting it first if
- * necessary.
- */
-static int write_oid(const struct object_id *oid,
- struct packed_git *pack UNUSED,
- uint32_t pos UNUSED, void *data)
-{
- struct child_process *cmd = data;
-
- if (cmd->in == -1) {
- if (start_command(cmd))
- die(_("could not start pack-objects to repack promisor objects"));
- }
-
- if (write_in_full(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
- write_in_full(cmd->in, "\n", 1) < 0)
- die(_("failed to feed promisor objects to pack-objects"));
- return 0;
-}
-
-static struct {
- const char *name;
- unsigned optional:1;
-} exts[] = {
- {".pack"},
- {".rev", 1},
- {".mtimes", 1},
- {".bitmap", 1},
- {".promisor", 1},
- {".idx"},
-};
-
-struct generated_pack_data {
- struct tempfile *tempfiles[ARRAY_SIZE(exts)];
-};
-
-static struct generated_pack_data *populate_pack_exts(const char *name)
-{
- struct stat statbuf;
- struct strbuf path = STRBUF_INIT;
- struct generated_pack_data *data = xcalloc(1, sizeof(*data));
- int i;
-
- for (i = 0; i < ARRAY_SIZE(exts); i++) {
- strbuf_reset(&path);
- strbuf_addf(&path, "%s-%s%s", packtmp, name, exts[i].name);
-
- if (stat(path.buf, &statbuf))
- continue;
-
- data->tempfiles[i] = register_tempfile(path.buf);
- }
-
- strbuf_release(&path);
- return data;
-}
-
-static int has_pack_ext(const struct generated_pack_data *data,
- const char *ext)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(exts); i++) {
- if (strcmp(exts[i].name, ext))
- continue;
- return !!data->tempfiles[i];
- }
- BUG("unknown pack extension: '%s'", ext);
-}
-
-static void repack_promisor_objects(const struct pack_objects_args *args,
- struct string_list *names)
-{
- struct child_process cmd = CHILD_PROCESS_INIT;
- FILE *out;
- struct strbuf line = STRBUF_INIT;
-
- prepare_pack_objects(&cmd, args, packtmp);
- cmd.in = -1;
-
- /*
- * NEEDSWORK: Giving pack-objects only the OIDs without any ordering
- * hints may result in suboptimal deltas in the resulting pack. See if
- * the OIDs can be sent with fake paths such that pack-objects can use a
- * {type -> existing pack order} ordering when computing deltas instead
- * of a {type -> size} ordering, which may produce better deltas.
- */
- for_each_packed_object(the_repository, write_oid, &cmd,
- FOR_EACH_OBJECT_PROMISOR_ONLY);
-
- if (cmd.in == -1) {
- /* No packed objects; cmd was never started */
- child_process_clear(&cmd);
- return;
- }
-
- close(cmd.in);
-
- out = xfdopen(cmd.out, "r");
- while (strbuf_getline_lf(&line, out) != EOF) {
- struct string_list_item *item;
- char *promisor_name;
-
- if (line.len != the_hash_algo->hexsz)
- die(_("repack: Expecting full hex object ID lines only from pack-objects."));
- item = string_list_append(names, line.buf);
-
- /*
- * pack-objects creates the .pack and .idx files, but not the
- * .promisor file. Create the .promisor file, which is empty.
- *
- * NEEDSWORK: fetch-pack sometimes generates non-empty
- * .promisor files containing the ref names and associated
- * hashes at the point of generation of the corresponding
- * packfile, but this would not preserve their contents. Maybe
- * concatenate the contents of all .promisor files instead of
- * just creating a new empty file.
- */
- promisor_name = mkpathdup("%s-%s.promisor", packtmp,
- line.buf);
- write_promisor_file(promisor_name, NULL, 0);
-
- item->util = populate_pack_exts(item->string);
-
- free(promisor_name);
- }
-
- fclose(out);
- if (finish_command(&cmd))
- die(_("could not finish pack-objects to repack promisor objects"));
- strbuf_release(&line);
-}
-
-struct pack_geometry {
- struct packed_git **pack;
- uint32_t pack_nr, pack_alloc;
- uint32_t split;
-
- int split_factor;
-};
-
-static uint32_t geometry_pack_weight(struct packed_git *p)
-{
- if (open_pack_index(p))
- die(_("cannot open index for %s"), p->pack_name);
- return p->num_objects;
-}
-
-static int geometry_cmp(const void *va, const void *vb)
-{
- uint32_t aw = geometry_pack_weight(*(struct packed_git **)va),
- bw = geometry_pack_weight(*(struct packed_git **)vb);
-
- if (aw < bw)
- return -1;
- if (aw > bw)
- return 1;
- return 0;
-}
-
-static void init_pack_geometry(struct pack_geometry *geometry,
- struct existing_packs *existing,
- const struct pack_objects_args *args)
-{
- struct packfile_store *packs = the_repository->objects->packfiles;
- struct packed_git *p;
- struct strbuf buf = STRBUF_INIT;
-
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
- if (args->local && !p->pack_local)
- /*
- * When asked to only repack local packfiles we skip
- * over any packfiles that are borrowed from alternate
- * object directories.
- */
- continue;
-
- if (!pack_kept_objects) {
- /*
- * Any pack that has its pack_keep bit set will
- * appear in existing->kept_packs below, but
- * this saves us from doing a more expensive
- * check.
- */
- if (p->pack_keep)
- continue;
-
- /*
- * The pack may be kept via the --keep-pack
- * option; check 'existing->kept_packs' to
- * determine whether to ignore it.
- */
- strbuf_reset(&buf);
- strbuf_addstr(&buf, pack_basename(p));
- strbuf_strip_suffix(&buf, ".pack");
-
- if (string_list_has_string(&existing->kept_packs, buf.buf))
- continue;
- }
- if (p->is_cruft)
- continue;
-
- ALLOC_GROW(geometry->pack,
- geometry->pack_nr + 1,
- geometry->pack_alloc);
-
- geometry->pack[geometry->pack_nr] = p;
- geometry->pack_nr++;
- }
-
- QSORT(geometry->pack, geometry->pack_nr, geometry_cmp);
- strbuf_release(&buf);
-}
-
-static void split_pack_geometry(struct pack_geometry *geometry)
-{
- uint32_t i;
- uint32_t split;
- off_t total_size = 0;
-
- if (!geometry->pack_nr) {
- geometry->split = geometry->pack_nr;
- return;
- }
-
- /*
- * First, count the number of packs (in descending order of size) which
- * already form a geometric progression.
- */
- for (i = geometry->pack_nr - 1; i > 0; i--) {
- struct packed_git *ours = geometry->pack[i];
- struct packed_git *prev = geometry->pack[i - 1];
-
- if (unsigned_mult_overflows(geometry->split_factor,
- geometry_pack_weight(prev)))
- die(_("pack %s too large to consider in geometric "
- "progression"),
- prev->pack_name);
-
- if (geometry_pack_weight(ours) <
- geometry->split_factor * geometry_pack_weight(prev))
- break;
- }
-
- split = i;
-
- if (split) {
- /*
- * Move the split one to the right, since the top element in the
- * last-compared pair can't be in the progression. Only do this
- * when we split in the middle of the array (otherwise if we got
- * to the end, then the split is in the right place).
- */
- split++;
- }
-
- /*
- * Then, anything to the left of 'split' must be in a new pack. But,
- * creating that new pack may cause packs in the heavy half to no longer
- * form a geometric progression.
- *
- * Compute an expected size of the new pack, and then determine how many
- * packs in the heavy half need to be joined into it (if any) to restore
- * the geometric progression.
- */
- for (i = 0; i < split; i++) {
- struct packed_git *p = geometry->pack[i];
-
- if (unsigned_add_overflows(total_size, geometry_pack_weight(p)))
- die(_("pack %s too large to roll up"), p->pack_name);
- total_size += geometry_pack_weight(p);
- }
- for (i = split; i < geometry->pack_nr; i++) {
- struct packed_git *ours = geometry->pack[i];
-
- if (unsigned_mult_overflows(geometry->split_factor,
- total_size))
- die(_("pack %s too large to roll up"), ours->pack_name);
-
- if (geometry_pack_weight(ours) <
- geometry->split_factor * total_size) {
- if (unsigned_add_overflows(total_size,
- geometry_pack_weight(ours)))
- die(_("pack %s too large to roll up"),
- ours->pack_name);
-
- split++;
- total_size += geometry_pack_weight(ours);
- } else
- break;
- }
-
- geometry->split = split;
-}
-
-static struct packed_git *get_preferred_pack(struct pack_geometry *geometry)
-{
- uint32_t i;
-
- 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;
-
- /*
- * The preferred pack is the largest pack above the split line. In
- * other words, it is the largest pack that does not get rolled up in
- * the geometric repack.
- */
- for (i = geometry->pack_nr; i > geometry->split; i--)
- /*
- * A pack that is not local would never be included in a
- * multi-pack index. We thus skip over any non-local packs.
- */
- if (geometry->pack[i - 1]->pack_local)
- return geometry->pack[i - 1];
-
- return NULL;
-}
-
-static void geometry_remove_redundant_packs(struct pack_geometry *geometry,
- struct string_list *names,
- struct existing_packs *existing)
-{
- struct strbuf buf = STRBUF_INIT;
- uint32_t i;
-
- for (i = 0; i < geometry->split; i++) {
- struct packed_git *p = geometry->pack[i];
- if (string_list_has_string(names, hash_to_hex(p->hash)))
- continue;
-
- strbuf_reset(&buf);
- strbuf_addstr(&buf, pack_basename(p));
- strbuf_strip_suffix(&buf, ".pack");
-
- if ((p->pack_keep) ||
- (string_list_has_string(&existing->kept_packs, buf.buf)))
- continue;
-
- remove_redundant_pack(packdir, buf.buf);
- }
-
- strbuf_release(&buf);
-}
-
-static void free_pack_geometry(struct pack_geometry *geometry)
-{
- if (!geometry)
- return;
-
- free(geometry->pack);
-}
-
-static int midx_has_unknown_packs(char **midx_pack_names,
- size_t midx_pack_names_nr,
- struct string_list *include,
- struct pack_geometry *geometry,
- struct existing_packs *existing)
-{
- size_t i;
-
- string_list_sort(include);
-
- for (i = 0; i < midx_pack_names_nr; i++) {
- const char *pack_name = midx_pack_names[i];
-
- /*
- * Determine whether or not each MIDX'd pack from the existing
- * MIDX (if any) is represented in the new MIDX. For each pack
- * in the MIDX, it must either be:
- *
- * - In the "include" list of packs to be included in the new
- * MIDX. Note this function is called before the include
- * list is populated with any cruft pack(s).
- *
- * - Below the geometric split line (if using pack geometry),
- * indicating that the pack won't be included in the new
- * MIDX, but its contents were rolled up as part of the
- * geometric repack.
- *
- * - In the existing non-kept packs list (if not using pack
- * geometry), and marked as non-deleted.
- */
- if (string_list_has_string(include, pack_name)) {
- continue;
- } else if (geometry) {
- struct strbuf buf = STRBUF_INIT;
- uint32_t j;
-
- for (j = 0; j < geometry->split; j++) {
- strbuf_reset(&buf);
- strbuf_addstr(&buf, pack_basename(geometry->pack[j]));
- strbuf_strip_suffix(&buf, ".pack");
- strbuf_addstr(&buf, ".idx");
-
- if (!strcmp(pack_name, buf.buf)) {
- strbuf_release(&buf);
- break;
- }
- }
-
- strbuf_release(&buf);
-
- if (j < geometry->split)
- continue;
- } else {
- struct string_list_item *item;
-
- item = string_list_lookup(&existing->non_kept_packs,
- pack_name);
- if (item && !pack_is_marked_for_deletion(item))
- continue;
- }
-
- /*
- * If we got to this point, the MIDX includes some pack that we
- * don't know about.
- */
- return 1;
- }
-
- return 0;
-}
-
-struct midx_snapshot_ref_data {
- struct tempfile *f;
- struct oidset seen;
- int preferred;
-};
-
-static int midx_snapshot_ref_one(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *_data)
-{
- struct midx_snapshot_ref_data *data = _data;
- struct object_id peeled;
-
- if (!peel_iterated_oid(the_repository, oid, &peeled))
- oid = &peeled;
-
- if (oidset_insert(&data->seen, oid))
- return 0; /* already seen */
-
- if (odb_read_object_info(the_repository->objects, 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)
- refs_for_each_ref_in(get_main_ref_store(the_repository),
- item->string,
- midx_snapshot_ref_one, &data);
- data.preferred = 0;
- }
-
- refs_for_each_ref(get_main_ref_store(the_repository),
- 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 existing_packs *existing,
- char **midx_pack_names,
- size_t midx_pack_names_nr,
- struct string_list *names,
- struct pack_geometry *geometry)
-{
- struct string_list_item *item;
- struct strbuf buf = STRBUF_INIT;
-
- for_each_string_list_item(item, &existing->kept_packs) {
- strbuf_reset(&buf);
- strbuf_addf(&buf, "%s.idx", item->string);
- string_list_insert(include, buf.buf);
- }
-
- for_each_string_list_item(item, names) {
- strbuf_reset(&buf);
- strbuf_addf(&buf, "pack-%s.idx", item->string);
- string_list_insert(include, buf.buf);
- }
-
- if (geometry->split_factor) {
- uint32_t i;
-
- for (i = geometry->split; i < geometry->pack_nr; i++) {
- struct packed_git *p = geometry->pack[i];
-
- /*
- * The multi-pack index never refers to packfiles part
- * of an alternate object database, so we skip these.
- * While git-multi-pack-index(1) would silently ignore
- * them anyway, this allows us to skip executing the
- * command completely when we have only non-local
- * packfiles.
- */
- if (!p->pack_local)
- continue;
-
- strbuf_reset(&buf);
- strbuf_addstr(&buf, pack_basename(p));
- strbuf_strip_suffix(&buf, ".pack");
- strbuf_addstr(&buf, ".idx");
-
- string_list_insert(include, buf.buf);
- }
- } else {
- for_each_string_list_item(item, &existing->non_kept_packs) {
- if (pack_is_marked_for_deletion(item))
- continue;
-
- strbuf_reset(&buf);
- strbuf_addf(&buf, "%s.idx", item->string);
- string_list_insert(include, buf.buf);
- }
- }
-
- if (midx_must_contain_cruft ||
- midx_has_unknown_packs(midx_pack_names, midx_pack_names_nr,
- include, geometry, existing)) {
- /*
- * If there are one or more unknown pack(s) present (see
- * midx_has_unknown_packs() for what makes a pack
- * "unknown") in the MIDX before the repack, keep them
- * as they may be required to form a reachability
- * closure if the MIDX is bitmapped.
- *
- * For example, a cruft pack can be required to form a
- * reachability closure if the MIDX is bitmapped and one
- * or more of the bitmap's selected commits reaches a
- * once-cruft object that was later made reachable.
- */
- for_each_string_list_item(item, &existing->cruft_packs) {
- /*
- * When doing a --geometric repack, there is no
- * need to check for deleted packs, since we're
- * by definition not doing an ALL_INTO_ONE
- * repack (hence no packs will be deleted).
- * Otherwise we must check for and exclude any
- * packs which are enqueued for deletion.
- *
- * So we could omit the conditional below in the
- * --geometric case, but doing so is unnecessary
- * since no packs are marked as pending
- * deletion (since we only call
- * `mark_packs_for_deletion()` when doing an
- * all-into-one repack).
- */
- if (pack_is_marked_for_deletion(item))
- continue;
-
- strbuf_reset(&buf);
- strbuf_addf(&buf, "%s.idx", item->string);
- string_list_insert(include, buf.buf);
- }
- } else {
- /*
- * Modern versions of Git (with the appropriate
- * configuration setting) will write new copies of
- * once-cruft objects when doing a --geometric repack.
- *
- * If the MIDX has no cruft pack, new packs written
- * during a --geometric repack will not rely on the
- * cruft pack to form a reachability closure, so we can
- * avoid including them in the MIDX in that case.
- */
- ;
- }
-
- strbuf_release(&buf);
-}
-
-static int write_midx_included_packs(struct string_list *include,
- struct pack_geometry *geometry,
- struct string_list *names,
- 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 *preferred = get_preferred_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 (preferred)
- strvec_pushf(&cmd.args, "--preferred-pack=%s",
- pack_basename(preferred));
- else if (names->nr) {
- /* The largest pack was repacked, meaning that either
- * one or two packs exist depending on whether the
- * repository has a cruft pack or not.
- *
- * Select the non-cruft one as preferred to encourage
- * pack-reuse among packs containing reachable objects
- * over unreachable ones.
- *
- * (Note we could write multiple packs here if
- * `--max-pack-size` was given, but any one of them
- * will suffice, so pick the first one.)
- */
- for_each_string_list_item(item, names) {
- struct generated_pack_data *data = item->util;
- if (has_pack_ext(data, ".mtimes"))
- continue;
-
- strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack",
- item->string);
- break;
- }
- } else {
- /*
- * No packs were kept, and no packs were written. The
- * only thing remaining are .keep packs (unless
- * --pack-kept-objects was given).
- *
- * Set the `--preferred-pack` arbitrarily here.
- */
- ;
- }
-
- 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);
-}
-
-static void remove_redundant_bitmaps(struct string_list *include,
- const char *packdir)
-{
- struct strbuf path = STRBUF_INIT;
- struct string_list_item *item;
- size_t packdir_len;
-
- strbuf_addstr(&path, packdir);
- strbuf_addch(&path, '/');
- packdir_len = path.len;
-
- /*
- * Remove any pack bitmaps corresponding to packs which are now
- * included in the MIDX.
- */
- for_each_string_list_item(item, include) {
- strbuf_addstr(&path, item->string);
- strbuf_strip_suffix(&path, ".idx");
- strbuf_addstr(&path, ".bitmap");
-
- if (unlink(path.buf) && errno != ENOENT)
- warning_errno(_("could not remove stale bitmap: %s"),
- path.buf);
-
- strbuf_setlen(&path, packdir_len);
- }
- strbuf_release(&path);
-}
-
-static int finish_pack_objects_cmd(struct child_process *cmd,
- struct string_list *names,
- int local)
-{
- FILE *out;
- struct strbuf line = STRBUF_INIT;
-
- out = xfdopen(cmd->out, "r");
- while (strbuf_getline_lf(&line, out) != EOF) {
- struct string_list_item *item;
-
- if (line.len != the_hash_algo->hexsz)
- die(_("repack: Expecting full hex object ID lines only "
- "from pack-objects."));
- /*
- * Avoid putting packs written outside of the repository in the
- * list of names.
- */
- if (local) {
- item = string_list_append(names, line.buf);
- item->util = populate_pack_exts(line.buf);
- }
- }
- fclose(out);
-
- strbuf_release(&line);
-
- return finish_command(cmd);
-}
-
-static int write_filtered_pack(const struct pack_objects_args *args,
- const char *destination,
- const char *pack_prefix,
- struct existing_packs *existing,
- struct string_list *names)
-{
- struct child_process cmd = CHILD_PROCESS_INIT;
- struct string_list_item *item;
- FILE *in;
- int ret;
- const char *caret;
- const char *scratch;
- int local = skip_prefix(destination, packdir, &scratch);
-
- prepare_pack_objects(&cmd, args, destination);
-
- strvec_push(&cmd.args, "--stdin-packs");
-
- if (!pack_kept_objects)
- strvec_push(&cmd.args, "--honor-pack-keep");
- for_each_string_list_item(item, &existing->kept_packs)
- strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);
-
- cmd.in = -1;
-
- ret = start_command(&cmd);
- if (ret)
- return ret;
-
- /*
- * Here 'names' contains only the pack(s) that were just
- * written, which is exactly the packs we want to keep. Also
- * 'existing_kept_packs' already contains the packs in
- * 'keep_pack_list'.
- */
- in = xfdopen(cmd.in, "w");
- for_each_string_list_item(item, names)
- fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
- for_each_string_list_item(item, &existing->non_kept_packs)
- fprintf(in, "%s.pack\n", item->string);
- for_each_string_list_item(item, &existing->cruft_packs)
- fprintf(in, "%s.pack\n", item->string);
- caret = pack_kept_objects ? "" : "^";
- for_each_string_list_item(item, &existing->kept_packs)
- fprintf(in, "%s%s.pack\n", caret, item->string);
- fclose(in);
-
- return finish_pack_objects_cmd(&cmd, names, local);
-}
-
-static void combine_small_cruft_packs(FILE *in, size_t combine_cruft_below_size,
- struct existing_packs *existing)
-{
- struct packfile_store *packs = the_repository->objects->packfiles;
- struct packed_git *p;
- struct strbuf buf = STRBUF_INIT;
- size_t i;
-
- for (p = packfile_store_get_all_packs(packs); p; p = p->next) {
- if (!(p->is_cruft && p->pack_local))
- continue;
-
- strbuf_reset(&buf);
- strbuf_addstr(&buf, pack_basename(p));
- strbuf_strip_suffix(&buf, ".pack");
-
- if (!string_list_has_string(&existing->cruft_packs, buf.buf))
- continue;
-
- if (p->pack_size < combine_cruft_below_size) {
- fprintf(in, "-%s\n", pack_basename(p));
- } else {
- retain_cruft_pack(existing, p);
- fprintf(in, "%s\n", pack_basename(p));
- }
- }
-
- for (i = 0; i < existing->non_kept_packs.nr; i++)
- fprintf(in, "-%s.pack\n",
- existing->non_kept_packs.items[i].string);
-
- strbuf_release(&buf);
-}
-
-static int write_cruft_pack(const struct pack_objects_args *args,
- const char *destination,
- const char *pack_prefix,
- const char *cruft_expiration,
- unsigned long combine_cruft_below_size,
- struct string_list *names,
- struct existing_packs *existing)
-{
- struct child_process cmd = CHILD_PROCESS_INIT;
- struct string_list_item *item;
- FILE *in;
- int ret;
- const char *scratch;
- int local = skip_prefix(destination, packdir, &scratch);
-
- prepare_pack_objects(&cmd, args, destination);
-
- strvec_push(&cmd.args, "--cruft");
- if (cruft_expiration)
- strvec_pushf(&cmd.args, "--cruft-expiration=%s",
- cruft_expiration);
-
- strvec_push(&cmd.args, "--honor-pack-keep");
- strvec_push(&cmd.args, "--non-empty");
-
- cmd.in = -1;
-
- ret = start_command(&cmd);
- if (ret)
- return ret;
-
- /*
- * names has a confusing double use: it both provides the list
- * of just-written new packs, and accepts the name of the cruft
- * pack we are writing.
- *
- * By the time it is read here, it contains only the pack(s)
- * that were just written, which is exactly the set of packs we
- * want to consider kept.
- *
- * If `--expire-to` is given, the double-use served by `names`
- * ensures that the pack written to `--expire-to` excludes any
- * objects contained in the cruft pack.
- */
- in = xfdopen(cmd.in, "w");
- for_each_string_list_item(item, names)
- fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
- if (combine_cruft_below_size && !cruft_expiration) {
- combine_small_cruft_packs(in, combine_cruft_below_size,
- existing);
- } else {
- for_each_string_list_item(item, &existing->non_kept_packs)
- fprintf(in, "-%s.pack\n", item->string);
- for_each_string_list_item(item, &existing->cruft_packs)
- fprintf(in, "-%s.pack\n", item->string);
- }
- for_each_string_list_item(item, &existing->kept_packs)
- fprintf(in, "%s.pack\n", item->string);
- fclose(in);
-
- return finish_pack_objects_cmd(&cmd, names, local);
-}
-
-static const char *find_pack_prefix(const char *packdir, const char *packtmp)
-{
- const char *pack_prefix;
- if (!skip_prefix(packtmp, packdir, &pack_prefix))
- die(_("pack prefix %s does not begin with objdir %s"),
- packtmp, packdir);
- if (*pack_prefix == '/')
- pack_prefix++;
- return pack_prefix;
-}
-
int cmd_repack(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
@@ -1255,18 +108,17 @@ int cmd_repack(int argc,
struct existing_packs existing = EXISTING_PACKS_INIT;
struct pack_geometry geometry = { 0 };
struct tempfile *refs_snapshot = NULL;
- int i, ext, ret;
+ int i, ret;
int show_progress;
- char **midx_pack_names = NULL;
- size_t midx_pack_names_nr = 0;
/* variables to be filled by option parsing */
+ struct repack_config_ctx config_ctx;
int delete_redundant = 0;
const char *unpack_unreachable = NULL;
int keep_unreachable = 0;
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
- struct pack_objects_args po_args = { 0 };
- struct pack_objects_args cruft_po_args = { 0 };
+ struct pack_objects_args po_args = PACK_OBJECTS_ARGS_INIT;
+ struct pack_objects_args cruft_po_args = PACK_OBJECTS_ARGS_INIT;
int write_midx = 0;
const char *cruft_expiration = NULL;
const char *expire_to = NULL;
@@ -1327,7 +179,7 @@ int cmd_repack(int argc,
OPT_UNSIGNED(0, "max-pack-size", &po_args.max_pack_size,
N_("maximum size of each packfile")),
OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options),
- OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
+ OPT_BOOL(0, "pack-kept-objects", &po_args.pack_kept_objects,
N_("repack objects in packs marked with .keep")),
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
N_("do not repack this pack")),
@@ -1344,7 +196,11 @@ int cmd_repack(int argc,
list_objects_filter_init(&po_args.filter_options);
- repo_config(the_repository, repack_config, &cruft_po_args);
+ memset(&config_ctx, 0, sizeof(config_ctx));
+ config_ctx.po_args = &po_args;
+ config_ctx.cruft_po_args = &cruft_po_args;
+
+ repo_config(repo, repack_config, &config_ctx);
argc = parse_options(argc, argv, prefix, builtin_repack_options,
git_repack_usage, 0);
@@ -1354,7 +210,7 @@ int cmd_repack(int argc,
po_args.depth = xstrdup_or_null(opt_depth);
po_args.threads = xstrdup_or_null(opt_threads);
- if (delete_redundant && the_repository->repository_format_precious_objects)
+ if (delete_redundant && repo->repository_format_precious_objects)
die(_("cannot delete packs in a precious-objects repo"));
die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A",
@@ -1369,14 +225,14 @@ int cmd_repack(int argc,
(!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
write_bitmaps = 0;
}
- if (pack_kept_objects < 0)
- pack_kept_objects = write_bitmaps > 0 && !write_midx;
+ if (po_args.pack_kept_objects < 0)
+ po_args.pack_kept_objects = write_bitmaps > 0 && !write_midx;
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
if (write_bitmaps && po_args.local &&
- odb_has_alternates(the_repository->objects)) {
+ odb_has_alternates(repo->objects)) {
/*
* When asked to do a local repack, but we have
* packfiles that are inherited from an alternate, then
@@ -1391,26 +247,28 @@ int cmd_repack(int argc,
if (write_midx && write_bitmaps) {
struct strbuf path = STRBUF_INIT;
- strbuf_addf(&path, "%s/%s_XXXXXX", repo_get_object_directory(the_repository),
+ strbuf_addf(&path, "%s/%s_XXXXXX",
+ repo_get_object_directory(repo),
"bitmap-ref-tips");
refs_snapshot = xmks_tempfile(path.buf);
- midx_snapshot_refs(refs_snapshot);
+ midx_snapshot_refs(repo, refs_snapshot);
strbuf_release(&path);
}
- packdir = mkpathdup("%s/pack", repo_get_object_directory(the_repository));
+ packdir = mkpathdup("%s/pack", repo_get_object_directory(repo));
packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid());
packtmp = mkpathdup("%s/%s", packdir, packtmp_name);
- collect_pack_filenames(&existing, &keep_pack_list);
+ existing.repo = repo;
+ existing_packs_collect(&existing, &keep_pack_list);
if (geometry.split_factor) {
if (pack_everything)
die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
- init_pack_geometry(&geometry, &existing, &po_args);
- split_pack_geometry(&geometry);
+ pack_geometry_init(&geometry, &existing, &po_args);
+ pack_geometry_split(&geometry);
}
prepare_pack_objects(&cmd, &po_args, packtmp);
@@ -1418,8 +276,6 @@ int cmd_repack(int argc,
show_progress = !po_args.quiet && isatty(2);
strvec_push(&cmd.args, "--keep-true-parents");
- if (!pack_kept_objects)
- strvec_push(&cmd.args, "--honor-pack-keep");
for (i = 0; i < keep_pack_list.nr; i++)
strvec_pushf(&cmd.args, "--keep-pack=%s",
keep_pack_list.items[i].string);
@@ -1439,7 +295,7 @@ int cmd_repack(int argc,
strvec_push(&cmd.args, "--reflog");
strvec_push(&cmd.args, "--indexed-objects");
}
- if (repo_has_promisor_remote(the_repository))
+ if (repo_has_promisor_remote(repo))
strvec_push(&cmd.args, "--exclude-promisor-objects");
if (!write_midx) {
if (write_bitmaps > 0)
@@ -1451,9 +307,9 @@ int cmd_repack(int argc,
strvec_push(&cmd.args, "--delta-islands");
if (pack_everything & ALL_INTO_ONE) {
- repack_promisor_objects(&po_args, &names);
+ repack_promisor_objects(repo, &po_args, &names, packtmp);
- if (has_existing_non_kept_packs(&existing) &&
+ if (existing_packs_has_non_kept(&existing) &&
delete_redundant &&
!(pack_everything & PACK_CRUFT)) {
for_each_string_list_item(item, &names) {
@@ -1515,9 +371,17 @@ int cmd_repack(int argc,
fclose(in);
}
- ret = finish_pack_objects_cmd(&cmd, &names, 1);
- if (ret)
- goto cleanup;
+ {
+ struct write_pack_opts opts = {
+ .packdir = packdir,
+ .destination = packdir,
+ .packtmp = packtmp,
+ };
+ ret = finish_pack_objects_cmd(repo->hash_algo, &opts, &cmd,
+ &names);
+ if (ret)
+ goto cleanup;
+ }
if (!names.nr) {
if (!po_args.quiet)
@@ -1535,12 +399,17 @@ int cmd_repack(int argc,
* midx_has_unknown_packs() will make the decision for
* us.
*/
- if (!get_multi_pack_index(the_repository->objects->sources))
+ if (!get_multi_pack_index(repo->objects->sources))
midx_must_contain_cruft = 1;
}
if (pack_everything & PACK_CRUFT) {
- const char *pack_prefix = find_pack_prefix(packdir, packtmp);
+ struct write_pack_opts opts = {
+ .po_args = &cruft_po_args,
+ .destination = packtmp,
+ .packtmp = packtmp,
+ .packdir = packdir,
+ };
if (!cruft_po_args.window)
cruft_po_args.window = xstrdup_or_null(po_args.window);
@@ -1555,9 +424,10 @@ int cmd_repack(int argc,
cruft_po_args.local = po_args.local;
cruft_po_args.quiet = po_args.quiet;
+ cruft_po_args.delta_base_offset = po_args.delta_base_offset;
+ cruft_po_args.pack_kept_objects = 0;
- ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
- cruft_expiration,
+ ret = write_cruft_pack(&opts, cruft_expiration,
combine_cruft_below_size, &names,
&existing);
if (ret)
@@ -1592,11 +462,8 @@ int cmd_repack(int argc,
* pack, but rather removing all cruft packs from the
* main repository regardless of size.
*/
- ret = write_cruft_pack(&cruft_po_args, expire_to,
- pack_prefix,
- NULL,
- 0ul,
- &names,
+ opts.destination = expire_to;
+ ret = write_cruft_pack(&opts, NULL, 0ul, &names,
&existing);
if (ret)
goto cleanup;
@@ -1604,99 +471,63 @@ int cmd_repack(int argc,
}
if (po_args.filter_options.choice) {
- if (!filter_to)
- filter_to = packtmp;
-
- ret = write_filtered_pack(&po_args,
- filter_to,
- find_pack_prefix(packdir, packtmp),
- &existing,
- &names);
+ struct write_pack_opts opts = {
+ .po_args = &po_args,
+ .destination = filter_to,
+ .packdir = packdir,
+ .packtmp = packtmp,
+ };
+
+ if (!opts.destination)
+ opts.destination = packtmp;
+
+ ret = write_filtered_pack(&opts, &existing, &names);
if (ret)
goto cleanup;
}
string_list_sort(&names);
- if (get_multi_pack_index(the_repository->objects->sources)) {
- struct multi_pack_index *m =
- get_multi_pack_index(the_repository->objects->sources);
-
- ALLOC_ARRAY(midx_pack_names,
- m->num_packs + m->num_packs_in_base);
-
- for (; m; m = m->base_midx)
- for (uint32_t i = 0; i < m->num_packs; i++)
- midx_pack_names[midx_pack_names_nr++] =
- xstrdup(m->pack_names[i]);
- }
-
- close_object_store(the_repository->objects);
+ close_object_store(repo->objects);
/*
* Ok we have prepared all new packfiles.
*/
- for_each_string_list_item(item, &names) {
- struct generated_pack_data *data = item->util;
-
- for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
- char *fname;
-
- fname = mkpathdup("%s/pack-%s%s",
- packdir, item->string, exts[ext].name);
-
- if (data->tempfiles[ext]) {
- const char *fname_old = get_tempfile_path(data->tempfiles[ext]);
- struct stat statbuffer;
-
- if (!stat(fname_old, &statbuffer)) {
- statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
- chmod(fname_old, statbuffer.st_mode);
- }
-
- if (rename_tempfile(&data->tempfiles[ext], fname))
- die_errno(_("renaming pack to '%s' failed"), fname);
- } else if (!exts[ext].optional)
- die(_("pack-objects did not write a '%s' file for pack %s-%s"),
- exts[ext].name, packtmp, item->string);
- else if (unlink(fname) < 0 && errno != ENOENT)
- die_errno(_("could not unlink: %s"), fname);
-
- free(fname);
- }
- }
+ for_each_string_list_item(item, &names)
+ generated_pack_install(item->util, item->string, packdir,
+ packtmp);
/* End of pack replacement. */
if (delete_redundant && pack_everything & ALL_INTO_ONE)
- mark_packs_for_deletion(&existing, &names);
+ existing_packs_mark_for_deletion(&existing, &names);
if (write_midx) {
- struct string_list include = STRING_LIST_INIT_DUP;
- midx_included_packs(&include, &existing, midx_pack_names,
- midx_pack_names_nr, &names, &geometry);
-
- ret = write_midx_included_packs(&include, &geometry, &names,
- refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
- show_progress, write_bitmaps > 0);
-
- if (!ret && write_bitmaps)
- remove_redundant_bitmaps(&include, packdir);
-
- string_list_clear(&include, 0);
+ struct repack_write_midx_opts opts = {
+ .existing = &existing,
+ .geometry = &geometry,
+ .names = &names,
+ .refs_snapshot = refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
+ .packdir = packdir,
+ .show_progress = show_progress,
+ .write_bitmaps = write_bitmaps > 0,
+ .midx_must_contain_cruft = midx_must_contain_cruft
+ };
+
+ ret = write_midx_included_packs(&opts);
if (ret)
goto cleanup;
}
- odb_reprepare(the_repository->objects);
+ odb_reprepare(repo->objects);
if (delete_redundant) {
int opts = 0;
- remove_redundant_existing_packs(&existing);
+ existing_packs_remove_redundant(&existing, packdir);
if (geometry.split_factor)
- geometry_remove_redundant_packs(&geometry, &names,
- &existing);
+ pack_geometry_remove_redundant(&geometry, &names,
+ &existing, packdir);
if (show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -1704,18 +535,18 @@ int cmd_repack(int argc,
if (!keep_unreachable &&
(!(pack_everything & LOOSEN_UNREACHABLE) ||
unpack_unreachable) &&
- is_repository_shallow(the_repository))
+ is_repository_shallow(repo))
prune_shallow(PRUNE_QUICK);
}
if (run_update_server_info)
- update_server_info(the_repository, 0);
+ update_server_info(repo, 0);
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) {
unsigned flags = 0;
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0))
flags |= MIDX_WRITE_INCREMENTAL;
- write_midx_file(the_repository->objects->sources,
+ write_midx_file(repo->objects->sources,
NULL, NULL, flags);
}
@@ -1723,10 +554,7 @@ cleanup:
string_list_clear(&keep_pack_list, 0);
string_list_clear(&names, 1);
existing_packs_release(&existing);
- free_pack_geometry(&geometry);
- for (size_t i = 0; i < midx_pack_names_nr; i++)
- free(midx_pack_names[i]);
- free(midx_pack_names);
+ pack_geometry_release(&geometry);
pack_objects_args_release(&po_args);
pack_objects_args_release(&cruft_po_args);
diff --git a/builtin/replace.c b/builtin/replace.c
index 900b560a77..4c62c5ab58 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -47,30 +47,27 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int show_reference(const struct reference *ref, void *cb_data)
{
struct show_data *data = cb_data;
- if (!wildmatch(data->pattern, refname, 0)) {
+ if (!wildmatch(data->pattern, ref->name, 0)) {
if (data->format == REPLACE_FORMAT_SHORT)
- printf("%s\n", refname);
+ printf("%s\n", ref->name);
else if (data->format == REPLACE_FORMAT_MEDIUM)
- printf("%s -> %s\n", refname, oid_to_hex(oid));
+ printf("%s -> %s\n", ref->name, oid_to_hex(ref->oid));
else { /* data->format == REPLACE_FORMAT_LONG */
struct object_id object;
enum object_type obj_type, repl_type;
- if (repo_get_oid(data->repo, refname, &object))
- return error(_("failed to resolve '%s' as a valid ref"), refname);
+ if (repo_get_oid(data->repo, ref->name, &object))
+ return error(_("failed to resolve '%s' as a valid ref"), ref->name);
obj_type = odb_read_object_info(data->repo->objects, &object, NULL);
- repl_type = odb_read_object_info(data->repo->objects, oid, NULL);
+ repl_type = odb_read_object_info(data->repo->objects, ref->oid, NULL);
- printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
- oid_to_hex(oid), type_name(repl_type));
+ printf("%s (%s) -> %s (%s)\n", ref->name, type_name(obj_type),
+ oid_to_hex(ref->oid), type_name(repl_type));
}
}
diff --git a/builtin/replay.c b/builtin/replay.c
index 6172c8aacc..f974a8c963 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,12 +2,12 @@
* "git replay" builtin command
*/
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "builtin.h"
+#include "config.h"
#include "environment.h"
#include "hex.h"
#include "lockfile.h"
@@ -15,17 +15,16 @@
#include "object-name.h"
#include "parse-options.h"
#include "refs.h"
+#include "replay.h"
#include "revision.h"
#include "strmap.h"
#include <oidset.h>
#include <tree.h>
-static const char *short_commit_name(struct repository *repo,
- struct commit *commit)
-{
- return repo_find_unique_abbrev(repo, &commit->object.oid,
- DEFAULT_ABBREV);
-}
+enum ref_action_mode {
+ REF_ACTION_UPDATE,
+ REF_ACTION_PRINT,
+};
static struct commit *peel_committish(struct repository *repo, const char *name)
{
@@ -39,59 +38,6 @@ static struct commit *peel_committish(struct repository *repo, const char *name)
OBJ_COMMIT);
}
-static char *get_author(const char *message)
-{
- size_t len;
- const char *a;
-
- a = find_commit_header(message, "author", &len);
- if (a)
- return xmemdupz(a, len);
-
- return NULL;
-}
-
-static struct commit *create_commit(struct repository *repo,
- struct tree *tree,
- struct commit *based_on,
- struct commit *parent)
-{
- struct object_id ret;
- struct object *obj = NULL;
- struct commit_list *parents = NULL;
- char *author;
- char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
- struct commit_extra_header *extra = NULL;
- struct strbuf msg = STRBUF_INIT;
- const char *out_enc = get_commit_output_encoding();
- const char *message = repo_logmsg_reencode(repo, based_on,
- NULL, out_enc);
- const char *orig_message = NULL;
- const char *exclude_gpgsig[] = { "gpgsig", NULL };
-
- commit_list_insert(parent, &parents);
- extra = read_commit_extra_headers(based_on, exclude_gpgsig);
- find_commit_subject(message, &orig_message);
- strbuf_addstr(&msg, orig_message);
- author = get_author(message);
- reset_ident_date();
- if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
- &ret, author, NULL, sign_commit, extra)) {
- error(_("failed to write commit object"));
- goto out;
- }
-
- obj = parse_object(repo, &ret);
-
-out:
- repo_unuse_commit_buffer(the_repository, based_on, message);
- free_commit_extra_headers(extra);
- free_commit_list(parents);
- strbuf_release(&msg);
- free(author);
- return (struct commit *)obj;
-}
-
struct ref_info {
struct commit *onto;
struct strset positive_refs;
@@ -240,48 +186,52 @@ static void determine_replay_mode(struct repository *repo,
strset_clear(&rinfo.positive_refs);
}
-static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
- struct commit *commit,
- struct commit *fallback)
+static enum ref_action_mode parse_ref_action_mode(const char *ref_action, const char *source)
{
- khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
- if (pos == kh_end(replayed_commits))
- return fallback;
- return kh_value(replayed_commits, pos);
+ if (!ref_action || !strcmp(ref_action, "update"))
+ return REF_ACTION_UPDATE;
+ if (!strcmp(ref_action, "print"))
+ return REF_ACTION_PRINT;
+ die(_("invalid %s value: '%s'"), source, ref_action);
}
-static struct commit *pick_regular_commit(struct repository *repo,
- struct commit *pickme,
- kh_oid_map_t *replayed_commits,
- struct commit *onto,
- struct merge_options *merge_opt,
- struct merge_result *result)
+static enum ref_action_mode get_ref_action_mode(struct repository *repo, const char *ref_action)
{
- struct commit *base, *replayed_base;
- struct tree *pickme_tree, *base_tree;
-
- base = pickme->parents->item;
- replayed_base = mapped_commit(replayed_commits, base, onto);
+ const char *config_value = NULL;
- result->tree = repo_get_commit_tree(repo, replayed_base);
- pickme_tree = repo_get_commit_tree(repo, pickme);
- base_tree = repo_get_commit_tree(repo, base);
+ /* Command line option takes precedence */
+ if (ref_action)
+ return parse_ref_action_mode(ref_action, "--ref-action");
- merge_opt->branch1 = short_commit_name(repo, replayed_base);
- merge_opt->branch2 = short_commit_name(repo, pickme);
- merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+ /* Check config value */
+ if (!repo_config_get_string_tmp(repo, "replay.refAction", &config_value))
+ return parse_ref_action_mode(config_value, "replay.refAction");
- merge_incore_nonrecursive(merge_opt,
- base_tree,
- result->tree,
- pickme_tree,
- result);
+ /* Default to update mode */
+ return REF_ACTION_UPDATE;
+}
- free((char*)merge_opt->ancestor);
- merge_opt->ancestor = NULL;
- if (!result->clean)
- return NULL;
- return create_commit(repo, result->tree, pickme, replayed_base);
+static int handle_ref_update(enum ref_action_mode mode,
+ struct ref_transaction *transaction,
+ const char *refname,
+ const struct object_id *new_oid,
+ const struct object_id *old_oid,
+ const char *reflog_msg,
+ struct strbuf *err)
+{
+ switch (mode) {
+ case REF_ACTION_PRINT:
+ printf("update %s %s %s\n",
+ refname,
+ oid_to_hex(new_oid),
+ oid_to_hex(old_oid));
+ return 0;
+ case REF_ACTION_UPDATE:
+ return ref_transaction_update(transaction, refname, new_oid, old_oid,
+ NULL, NULL, 0, reflog_msg, err);
+ default:
+ BUG("unknown ref_action_mode %d", mode);
+ }
}
int cmd_replay(int argc,
@@ -294,6 +244,8 @@ int cmd_replay(int argc,
struct commit *onto = NULL;
const char *onto_name = NULL;
int contained = 0;
+ const char *ref_action = NULL;
+ enum ref_action_mode ref_mode;
struct rev_info revs;
struct commit *last_commit = NULL;
@@ -302,12 +254,15 @@ int cmd_replay(int argc,
struct merge_result result;
struct strset *update_refs = NULL;
kh_oid_map_t *replayed_commits;
+ struct ref_transaction *transaction = NULL;
+ struct strbuf transaction_err = STRBUF_INIT;
+ struct strbuf reflog_msg = STRBUF_INIT;
int ret = 0;
- const char * const replay_usage[] = {
+ const char *const replay_usage[] = {
N_("(EXPERIMENTAL!) git replay "
"([--contained] --onto <newbase> | --advance <branch>) "
- "<revision-range>..."),
+ "[--ref-action[=<mode>]] <revision-range>..."),
NULL
};
struct option replay_options[] = {
@@ -319,6 +274,9 @@ int cmd_replay(int argc,
N_("replay onto given commit")),
OPT_BOOL(0, "contained", &contained,
N_("advance all branches contained in revision-range")),
+ OPT_STRING(0, "ref-action", &ref_action,
+ N_("mode"),
+ N_("control ref update behavior (update|print)")),
OPT_END()
};
@@ -330,9 +288,12 @@ int cmd_replay(int argc,
usage_with_options(replay_usage, replay_options);
}
- if (advance_name_opt && contained)
- die(_("options '%s' and '%s' cannot be used together"),
- "--advance", "--contained");
+ die_for_incompatible_opt2(!!advance_name_opt, "--advance",
+ contained, "--contained");
+
+ /* Parse ref action mode from command line or config */
+ ref_mode = get_ref_action_mode(repo, ref_action);
+
advance_name = xstrdup_or_null(advance_name_opt);
repo_init_revisions(repo, &revs, prefix);
@@ -389,6 +350,24 @@ int cmd_replay(int argc,
determine_replay_mode(repo, &revs.cmdline, onto_name, &advance_name,
&onto, &update_refs);
+ /* Build reflog message */
+ if (advance_name_opt)
+ strbuf_addf(&reflog_msg, "replay --advance %s", advance_name_opt);
+ else
+ strbuf_addf(&reflog_msg, "replay --onto %s",
+ oid_to_hex(&onto->object.oid));
+
+ /* Initialize ref transaction if using update mode */
+ if (ref_mode == REF_ACTION_UPDATE) {
+ transaction = ref_store_transaction_begin(get_main_ref_store(repo),
+ 0, &transaction_err);
+ if (!transaction) {
+ ret = error(_("failed to begin ref transaction: %s"),
+ transaction_err.buf);
+ goto cleanup;
+ }
+ }
+
if (!onto) /* FIXME: Should handle replaying down to root commit */
die("Replaying down to root commit is not supported yet!");
@@ -412,8 +391,8 @@ int cmd_replay(int argc,
if (commit->parents->next)
die(_("replaying merge commits is not supported yet!"));
- last_commit = pick_regular_commit(repo, commit, replayed_commits,
- onto, &merge_opt, &result);
+ last_commit = replay_pick_regular_commit(repo, commit, replayed_commits,
+ onto, &merge_opt, &result);
if (!last_commit)
break;
@@ -434,10 +413,16 @@ int cmd_replay(int argc,
if (decoration->type == DECORATION_REF_LOCAL &&
(contained || strset_contains(update_refs,
decoration->name))) {
- printf("update %s %s %s\n",
- decoration->name,
- oid_to_hex(&last_commit->object.oid),
- oid_to_hex(&commit->object.oid));
+ if (handle_ref_update(ref_mode, transaction,
+ decoration->name,
+ &last_commit->object.oid,
+ &commit->object.oid,
+ reflog_msg.buf,
+ &transaction_err) < 0) {
+ ret = error(_("failed to update ref '%s': %s"),
+ decoration->name, transaction_err.buf);
+ goto cleanup;
+ }
}
decoration = decoration->next;
}
@@ -445,10 +430,24 @@ int cmd_replay(int argc,
/* In --advance mode, advance the target ref */
if (result.clean == 1 && advance_name) {
- printf("update %s %s %s\n",
- advance_name,
- oid_to_hex(&last_commit->object.oid),
- oid_to_hex(&onto->object.oid));
+ if (handle_ref_update(ref_mode, transaction, advance_name,
+ &last_commit->object.oid,
+ &onto->object.oid,
+ reflog_msg.buf,
+ &transaction_err) < 0) {
+ ret = error(_("failed to update ref '%s': %s"),
+ advance_name, transaction_err.buf);
+ goto cleanup;
+ }
+ }
+
+ /* Commit the ref transaction if we have one */
+ if (transaction && result.clean == 1) {
+ if (ref_transaction_commit(transaction, &transaction_err)) {
+ ret = error(_("failed to commit ref transaction: %s"),
+ transaction_err.buf);
+ goto cleanup;
+ }
}
merge_finalize(&merge_opt, &result);
@@ -460,6 +459,10 @@ int cmd_replay(int argc,
ret = result.clean;
cleanup:
+ if (transaction)
+ ref_transaction_free(transaction);
+ strbuf_release(&transaction_err);
+ strbuf_release(&reflog_msg);
release_revisions(&revs);
free(advance_name);
diff --git a/builtin/repo.c b/builtin/repo.c
index bbb0966f2d..9c3da1a975 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -3,19 +3,27 @@
#include "builtin.h"
#include "environment.h"
#include "parse-options.h"
+#include "path-walk.h"
+#include "progress.h"
#include "quote.h"
+#include "ref-filter.h"
#include "refs.h"
+#include "revision.h"
#include "strbuf.h"
+#include "string-list.h"
#include "shallow.h"
+#include "utf8.h"
static const char *const repo_usage[] = {
- "git repo info [--format=(keyvalue|nul)] [-z] [<key>...]",
+ "git repo info [--format=(keyvalue|nul)] [-z] [--all | <key>...]",
+ "git repo structure [--format=(table|keyvalue|nul)]",
NULL
};
typedef int get_value_fn(struct repository *repo, struct strbuf *buf);
enum output_format {
+ FORMAT_TABLE,
FORMAT_KEYVALUE,
FORMAT_NUL_TERMINATED,
};
@@ -77,6 +85,24 @@ static get_value_fn *get_value_fn_for_key(const char *key)
return found ? found->get_value : NULL;
}
+static void print_field(enum output_format format, const char *key,
+ struct strbuf *valbuf, struct strbuf *quotbuf)
+{
+ strbuf_reset(quotbuf);
+
+ switch (format) {
+ case FORMAT_KEYVALUE:
+ quote_c_style(valbuf->buf, quotbuf, NULL, 0);
+ printf("%s=%s\n", key, quotbuf->buf);
+ break;
+ case FORMAT_NUL_TERMINATED:
+ printf("%s\n%s%c", key, valbuf->buf, '\0');
+ break;
+ default:
+ BUG("not a valid output format: %d", format);
+ }
+}
+
static int print_fields(int argc, const char **argv,
struct repository *repo,
enum output_format format)
@@ -97,21 +123,8 @@ static int print_fields(int argc, const char **argv,
}
strbuf_reset(&valbuf);
- strbuf_reset(&quotbuf);
-
get_value(repo, &valbuf);
-
- switch (format) {
- case FORMAT_KEYVALUE:
- quote_c_style(valbuf.buf, &quotbuf, NULL, 0);
- printf("%s=%s\n", key, quotbuf.buf);
- break;
- case FORMAT_NUL_TERMINATED:
- printf("%s\n%s%c", key, valbuf.buf, '\0');
- break;
- default:
- BUG("not a valid output format: %d", format);
- }
+ print_field(format, key, &valbuf, &quotbuf);
}
strbuf_release(&valbuf);
@@ -119,6 +132,24 @@ static int print_fields(int argc, const char **argv,
return ret;
}
+static void print_all_fields(struct repository *repo,
+ enum output_format format)
+{
+ struct strbuf valbuf = STRBUF_INIT;
+ struct strbuf quotbuf = STRBUF_INIT;
+
+ for (unsigned long i = 0; i < ARRAY_SIZE(repo_info_fields); i++) {
+ struct field field = repo_info_fields[i];
+
+ strbuf_reset(&valbuf);
+ field.get_value(repo, &valbuf);
+ print_field(format, field.key, &valbuf, &quotbuf);
+ }
+
+ strbuf_release(&valbuf);
+ strbuf_release(&quotbuf);
+}
+
static int parse_format_cb(const struct option *opt,
const char *arg, int unset UNUSED)
{
@@ -130,16 +161,19 @@ static int parse_format_cb(const struct option *opt,
*format = FORMAT_NUL_TERMINATED;
else if (!strcmp(arg, "keyvalue"))
*format = FORMAT_KEYVALUE;
+ else if (!strcmp(arg, "table"))
+ *format = FORMAT_TABLE;
else
die(_("invalid format '%s'"), arg);
return 0;
}
-static int repo_info(int argc, const char **argv, const char *prefix,
- struct repository *repo)
+static int cmd_repo_info(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
{
enum output_format format = FORMAT_KEYVALUE;
+ int all_keys = 0;
struct option options[] = {
OPT_CALLBACK_F(0, "format", &format, N_("format"),
N_("output format"),
@@ -148,20 +182,390 @@ static int repo_info(int argc, const char **argv, const char *prefix,
N_("synonym for --format=nul"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
parse_format_cb),
+ OPT_BOOL(0, "all", &all_keys, N_("return all keys")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
+ if (format != FORMAT_KEYVALUE && format != FORMAT_NUL_TERMINATED)
+ die(_("unsupported output format"));
+
+ if (all_keys) {
+ if (argc)
+ die(_("--all and <key> cannot be used together"));
+
+ print_all_fields(repo, format);
+ return 0;
+ }
return print_fields(argc, argv, repo, format);
}
+struct ref_stats {
+ size_t branches;
+ size_t remotes;
+ size_t tags;
+ size_t others;
+};
+
+struct object_stats {
+ size_t tags;
+ size_t commits;
+ size_t trees;
+ size_t blobs;
+};
+
+struct repo_structure {
+ struct ref_stats refs;
+ struct object_stats objects;
+};
+
+struct stats_table {
+ struct string_list rows;
+
+ int name_col_width;
+ int value_col_width;
+};
+
+/*
+ * Holds column data that gets stored for each row.
+ */
+struct stats_table_entry {
+ char *value;
+};
+
+static void stats_table_vaddf(struct stats_table *table,
+ struct stats_table_entry *entry,
+ const char *format, va_list ap)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list_item *item;
+ char *formatted_name;
+ int name_width;
+
+ strbuf_vaddf(&buf, format, ap);
+ formatted_name = strbuf_detach(&buf, NULL);
+ name_width = utf8_strwidth(formatted_name);
+
+ item = string_list_append_nodup(&table->rows, formatted_name);
+ item->util = entry;
+
+ if (name_width > table->name_col_width)
+ table->name_col_width = name_width;
+ if (entry) {
+ int value_width = utf8_strwidth(entry->value);
+ if (value_width > table->value_col_width)
+ table->value_col_width = value_width;
+ }
+}
+
+static void stats_table_addf(struct stats_table *table, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ stats_table_vaddf(table, NULL, format, ap);
+ va_end(ap);
+}
+
+static void stats_table_count_addf(struct stats_table *table, size_t value,
+ const char *format, ...)
+{
+ struct stats_table_entry *entry;
+ va_list ap;
+
+ CALLOC_ARRAY(entry, 1);
+ entry->value = xstrfmt("%" PRIuMAX, (uintmax_t)value);
+
+ va_start(ap, format);
+ stats_table_vaddf(table, entry, format, ap);
+ va_end(ap);
+}
+
+static inline size_t get_total_reference_count(struct ref_stats *stats)
+{
+ return stats->branches + stats->remotes + stats->tags + stats->others;
+}
+
+static inline size_t get_total_object_count(struct object_stats *stats)
+{
+ return stats->tags + stats->commits + stats->trees + stats->blobs;
+}
+
+static void stats_table_setup_structure(struct stats_table *table,
+ struct repo_structure *stats)
+{
+ struct object_stats *objects = &stats->objects;
+ struct ref_stats *refs = &stats->refs;
+ size_t object_total;
+ size_t ref_total;
+
+ ref_total = get_total_reference_count(refs);
+ stats_table_addf(table, "* %s", _("References"));
+ stats_table_count_addf(table, ref_total, " * %s", _("Count"));
+ stats_table_count_addf(table, refs->branches, " * %s", _("Branches"));
+ stats_table_count_addf(table, refs->tags, " * %s", _("Tags"));
+ stats_table_count_addf(table, refs->remotes, " * %s", _("Remotes"));
+ stats_table_count_addf(table, refs->others, " * %s", _("Others"));
+
+ object_total = get_total_object_count(objects);
+ stats_table_addf(table, "");
+ stats_table_addf(table, "* %s", _("Reachable objects"));
+ stats_table_count_addf(table, object_total, " * %s", _("Count"));
+ stats_table_count_addf(table, objects->commits, " * %s", _("Commits"));
+ stats_table_count_addf(table, objects->trees, " * %s", _("Trees"));
+ stats_table_count_addf(table, objects->blobs, " * %s", _("Blobs"));
+ stats_table_count_addf(table, objects->tags, " * %s", _("Tags"));
+}
+
+static void stats_table_print_structure(const struct stats_table *table)
+{
+ const char *name_col_title = _("Repository structure");
+ const char *value_col_title = _("Value");
+ int name_col_width = utf8_strwidth(name_col_title);
+ int value_col_width = utf8_strwidth(value_col_title);
+ struct string_list_item *item;
+
+ if (table->name_col_width > name_col_width)
+ name_col_width = table->name_col_width;
+ if (table->value_col_width > value_col_width)
+ value_col_width = table->value_col_width;
+
+ printf("| %-*s | %-*s |\n", name_col_width, name_col_title,
+ value_col_width, value_col_title);
+ printf("| ");
+ for (int i = 0; i < name_col_width; i++)
+ putchar('-');
+ printf(" | ");
+ for (int i = 0; i < value_col_width; i++)
+ putchar('-');
+ printf(" |\n");
+
+ for_each_string_list_item(item, &table->rows) {
+ struct stats_table_entry *entry = item->util;
+ const char *value = "";
+
+ if (entry) {
+ struct stats_table_entry *entry = item->util;
+ value = entry->value;
+ }
+
+ printf("| %-*s | %*s |\n", name_col_width, item->string,
+ value_col_width, value);
+ }
+}
+
+static void stats_table_clear(struct stats_table *table)
+{
+ struct stats_table_entry *entry;
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, &table->rows) {
+ entry = item->util;
+ if (entry)
+ free(entry->value);
+ }
+
+ string_list_clear(&table->rows, 1);
+}
+
+static void structure_keyvalue_print(struct repo_structure *stats,
+ char key_delim, char value_delim)
+{
+ printf("references.branches.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->refs.branches, value_delim);
+ printf("references.tags.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->refs.tags, value_delim);
+ printf("references.remotes.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->refs.remotes, value_delim);
+ printf("references.others.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->refs.others, value_delim);
+
+ printf("objects.commits.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->objects.commits, value_delim);
+ printf("objects.trees.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->objects.trees, value_delim);
+ printf("objects.blobs.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->objects.blobs, value_delim);
+ printf("objects.tags.count%c%" PRIuMAX "%c", key_delim,
+ (uintmax_t)stats->objects.tags, value_delim);
+
+ fflush(stdout);
+}
+
+struct count_references_data {
+ struct ref_stats *stats;
+ struct rev_info *revs;
+ struct progress *progress;
+};
+
+static int count_references(const struct reference *ref, void *cb_data)
+{
+ struct count_references_data *data = cb_data;
+ struct ref_stats *stats = data->stats;
+ size_t ref_count;
+
+ switch (ref_kind_from_refname(ref->name)) {
+ case FILTER_REFS_BRANCHES:
+ stats->branches++;
+ break;
+ case FILTER_REFS_REMOTES:
+ stats->remotes++;
+ break;
+ case FILTER_REFS_TAGS:
+ stats->tags++;
+ break;
+ case FILTER_REFS_OTHERS:
+ stats->others++;
+ break;
+ default:
+ BUG("unexpected reference type");
+ }
+
+ /*
+ * While iterating through references for counting, also add OIDs in
+ * preparation for the path walk.
+ */
+ add_pending_oid(data->revs, NULL, ref->oid, 0);
+
+ ref_count = get_total_reference_count(stats);
+ display_progress(data->progress, ref_count);
+
+ return 0;
+}
+
+static void structure_count_references(struct ref_stats *stats,
+ struct rev_info *revs,
+ struct repository *repo,
+ int show_progress)
+{
+ struct count_references_data data = {
+ .stats = stats,
+ .revs = revs,
+ };
+
+ if (show_progress)
+ data.progress = start_delayed_progress(repo,
+ _("Counting references"), 0);
+
+ refs_for_each_ref(get_main_ref_store(repo), count_references, &data);
+ stop_progress(&data.progress);
+}
+
+struct count_objects_data {
+ struct object_stats *stats;
+ struct progress *progress;
+};
+
+static int count_objects(const char *path UNUSED, struct oid_array *oids,
+ enum object_type type, void *cb_data)
+{
+ struct count_objects_data *data = cb_data;
+ struct object_stats *stats = data->stats;
+ size_t object_count;
+
+ switch (type) {
+ case OBJ_TAG:
+ stats->tags += oids->nr;
+ break;
+ case OBJ_COMMIT:
+ stats->commits += oids->nr;
+ break;
+ case OBJ_TREE:
+ stats->trees += oids->nr;
+ break;
+ case OBJ_BLOB:
+ stats->blobs += oids->nr;
+ break;
+ default:
+ BUG("invalid object type");
+ }
+
+ object_count = get_total_object_count(stats);
+ display_progress(data->progress, object_count);
+
+ return 0;
+}
+
+static void structure_count_objects(struct object_stats *stats,
+ struct rev_info *revs,
+ struct repository *repo, int show_progress)
+{
+ struct path_walk_info info = PATH_WALK_INFO_INIT;
+ struct count_objects_data data = {
+ .stats = stats,
+ };
+
+ info.revs = revs;
+ info.path_fn = count_objects;
+ info.path_fn_data = &data;
+
+ if (show_progress)
+ data.progress = start_delayed_progress(repo, _("Counting objects"), 0);
+
+ walk_objects_by_path(&info);
+ path_walk_info_clear(&info);
+ stop_progress(&data.progress);
+}
+
+static int cmd_repo_structure(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ struct stats_table table = {
+ .rows = STRING_LIST_INIT_DUP,
+ };
+ enum output_format format = FORMAT_TABLE;
+ struct repo_structure stats = { 0 };
+ struct rev_info revs;
+ int show_progress = -1;
+ struct option options[] = {
+ OPT_CALLBACK_F(0, "format", &format, N_("format"),
+ N_("output format"),
+ PARSE_OPT_NONEG, parse_format_cb),
+ OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, repo_usage, 0);
+ if (argc)
+ usage(_("too many arguments"));
+
+ repo_init_revisions(repo, &revs, prefix);
+
+ if (show_progress < 0)
+ show_progress = isatty(2);
+
+ structure_count_references(&stats.refs, &revs, repo, show_progress);
+ structure_count_objects(&stats.objects, &revs, repo, show_progress);
+
+ switch (format) {
+ case FORMAT_TABLE:
+ stats_table_setup_structure(&table, &stats);
+ stats_table_print_structure(&table);
+ break;
+ case FORMAT_KEYVALUE:
+ structure_keyvalue_print(&stats, '=', '\n');
+ break;
+ case FORMAT_NUL_TERMINATED:
+ structure_keyvalue_print(&stats, '\n', '\0');
+ break;
+ default:
+ BUG("invalid output format");
+ }
+
+ stats_table_clear(&table);
+ release_revisions(&revs);
+
+ return 0;
+}
+
int cmd_repo(int argc, const char **argv, const char *prefix,
struct repository *repo)
{
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
- OPT_SUBCOMMAND("info", &fn, repo_info),
+ OPT_SUBCOMMAND("info", &fn, cmd_repo_info),
+ OPT_SUBCOMMAND("structure", &fn, cmd_repo_structure),
OPT_END()
};
diff --git a/builtin/reset.c b/builtin/reset.c
index ed35802af1..088449e120 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -346,7 +346,7 @@ int cmd_reset(int argc,
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
- struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
+ struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
const struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "no-refresh", &no_refresh,
@@ -371,8 +371,8 @@ int cmd_reset(int argc,
PARSE_OPT_OPTARG,
option_parse_recurse_submodules_worktree_updater),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
- OPT_DIFF_UNIFIED(&add_p_opt.context),
- OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
+ OPT_DIFF_UNIFIED(&interactive_opts.context),
+ OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
@@ -423,9 +423,9 @@ int cmd_reset(int argc,
oidcpy(&oid, &tree->object.oid);
}
- if (add_p_opt.context < -1)
+ if (interactive_opts.context < -1)
die(_("'%s' cannot be negative"), "--unified");
- if (add_p_opt.interhunkcontext < -1)
+ if (interactive_opts.interhunkcontext < -1)
die(_("'%s' cannot be negative"), "--inter-hunk-context");
prepare_repo_settings(the_repository);
@@ -436,12 +436,12 @@ int cmd_reset(int argc,
die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive");
update_ref_status = !!run_add_p(the_repository, ADD_P_RESET,
- &add_p_opt, rev, &pathspec);
+ &interactive_opts, rev, &pathspec);
goto cleanup;
} else {
- if (add_p_opt.context != -1)
+ if (interactive_opts.context != -1)
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
- if (add_p_opt.interhunkcontext != -1)
+ if (interactive_opts.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
}
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9da92b990d..9032cc6327 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -217,19 +217,17 @@ static int show_default(void)
return 0;
}
-static int show_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int show_reference(const struct reference *ref, void *cb_data UNUSED)
{
- if (ref_excluded(&ref_excludes, refname))
+ if (ref_excluded(&ref_excludes, ref->name))
return 0;
- show_rev(NORMAL, oid, refname);
+ show_rev(NORMAL, ref->oid, ref->name);
return 0;
}
-static int anti_reference(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int anti_reference(const struct reference *ref, void *cb_data UNUSED)
{
- show_rev(REVERSED, oid, refname);
+ show_rev(REVERSED, ref->oid, ref->name);
return 0;
}
@@ -1107,11 +1105,20 @@ int cmd_rev_parse(int argc,
const char *val = arg ? arg : "storage";
if (strcmp(val, "storage") &&
+ strcmp(val, "compat") &&
strcmp(val, "input") &&
strcmp(val, "output"))
die(_("unknown mode for --show-object-format: %s"),
arg);
- puts(the_hash_algo->name);
+
+ if (!strcmp(val, "compat")) {
+ if (the_repository->compat_hash_algo)
+ puts(the_repository->compat_hash_algo->name);
+ else
+ putchar('\n');
+ } else {
+ puts(the_hash_algo->name);
+ }
continue;
}
if (!strcmp(arg, "--show-ref-format")) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 441babf2e3..10475a6b5e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -413,34 +413,32 @@ static int append_ref(const char *refname, const struct object_id *oid,
return 0;
}
-static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_head_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 11;
- if (!starts_with(refname, "refs/heads/"))
+ if (!starts_with(ref->name, "refs/heads/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
-static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data UNUSED)
+static int append_remote_ref(const struct reference *ref, void *cb_data UNUSED)
{
struct object_id tmp;
int ofs = 13;
- if (!starts_with(refname, "refs/remotes/"))
+ if (!starts_with(ref->name, "refs/remotes/"))
return 0;
/* If both heads/foo and tags/foo exists, get_sha1 would
* get confused.
*/
- if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid))
+ if (repo_get_oid(the_repository, ref->name + ofs, &tmp) || !oideq(&tmp, ref->oid))
ofs = 5;
- return append_ref(refname + ofs, oid, 0);
+ return append_ref(ref->name + ofs, ref->oid, 0);
}
static int append_tag_ref(const char *refname, const struct object_id *oid,
@@ -454,27 +452,26 @@ static int append_tag_ref(const char *refname, const struct object_id *oid,
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
-static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data)
+static int append_matching_ref(const struct reference *ref, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
- int slash = count_slashes(refname);
- for (tail = refname; *tail && match_ref_slash < slash; )
+ int slash = count_slashes(ref->name);
+ for (tail = ref->name; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (wildmatch(match_ref_pattern, tail, 0))
return 0;
- if (starts_with(refname, "refs/heads/"))
- return append_head_ref(refname, NULL, oid, flag, cb_data);
- if (starts_with(refname, "refs/tags/"))
- return append_tag_ref(refname, oid, flag, cb_data);
- return append_ref(refname, oid, 0);
+ if (starts_with(ref->name, "refs/heads/"))
+ return append_head_ref(ref, cb_data);
+ if (starts_with(ref->name, "refs/tags/"))
+ return append_tag_ref(ref->name, ref->oid, ref->flags, cb_data);
+ return append_ref(ref->name, ref->oid, 0);
}
static void snarf_refs(int head, int remotes)
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 0b6f9edf86..4d4984e4e0 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,31 +31,31 @@ struct show_one_options {
};
static void show_one(const struct show_one_options *opts,
- const char *refname, const struct object_id *oid)
+ const struct reference *ref)
{
const char *hex;
struct object_id peeled;
- if (!odb_has_object(the_repository->objects, oid,
+ if (!odb_has_object(the_repository->objects, ref->oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
- die("git show-ref: bad ref %s (%s)", refname,
- oid_to_hex(oid));
+ die("git show-ref: bad ref %s (%s)", ref->name,
+ oid_to_hex(ref->oid));
if (opts->quiet)
return;
- hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev);
+ hex = repo_find_unique_abbrev(the_repository, ref->oid, opts->abbrev);
if (opts->hash_only)
printf("%s\n", hex);
else
- printf("%s %s\n", hex, refname);
+ printf("%s %s\n", hex, ref->name);
if (!opts->deref_tags)
return;
- if (!peel_iterated_oid(the_repository, oid, &peeled)) {
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled)) {
hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
- printf("%s %s^{}\n", hex, refname);
+ printf("%s %s^{}\n", hex, ref->name);
}
}
@@ -66,26 +66,25 @@ struct show_ref_data {
int show_head;
};
-static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cbdata)
+static int show_ref(const struct reference *ref, void *cbdata)
{
struct show_ref_data *data = cbdata;
- if (data->show_head && !strcmp(refname, "HEAD"))
+ if (data->show_head && !strcmp(ref->name, "HEAD"))
goto match;
if (data->patterns) {
- int reflen = strlen(refname);
+ int reflen = strlen(ref->name);
const char **p = data->patterns, *m;
while ((m = *p++) != NULL) {
int len = strlen(m);
if (len > reflen)
continue;
- if (memcmp(m, refname + reflen - len, len))
+ if (memcmp(m, ref->name + reflen - len, len))
continue;
if (len == reflen)
goto match;
- if (refname[reflen - len - 1] == '/')
+ if (ref->name[reflen - len - 1] == '/')
goto match;
}
return 0;
@@ -94,18 +93,15 @@ static int show_ref(const char *refname, const char *referent UNUSED, const stru
match:
data->found_match++;
- show_one(data->show_one_opts, refname, oid);
+ show_one(data->show_one_opts, ref);
return 0;
}
-static int add_existing(const char *refname,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cbdata)
+static int add_existing(const struct reference *ref, void *cbdata)
{
struct string_list *list = (struct string_list *)cbdata;
- string_list_insert(list, refname);
+ string_list_insert(list, ref->name);
return 0;
}
@@ -179,12 +175,18 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
- show_one(show_one_opts, *refs, &oid);
- }
- else if (!show_one_opts->quiet)
+ struct reference ref = {
+ .name = *refs,
+ .oid = &oid,
+ };
+
+ show_one(show_one_opts, &ref);
+ } else if (!show_one_opts->quiet) {
die("'%s' - not a valid ref", *refs);
- else
+ } else {
return 1;
+ }
+
refs++;
}
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 8c333b3e2e..15d51e60a8 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -2,6 +2,7 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "builtin.h"
+#include "abspath.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
@@ -23,7 +24,7 @@
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
- N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"),
+ N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules | clean) [<options>]"),
NULL
};
@@ -204,12 +205,12 @@ static void clean_tracked_sparse_directories(struct repository *r)
ensure_full_index(r->index);
}
-static int update_working_directory(struct pattern_list *pl)
+static int update_working_directory(struct repository *r,
+ struct pattern_list *pl)
{
enum update_sparsity_result result;
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
- struct repository *r = the_repository;
struct pattern_list *old_pl;
/* If no branch has been checked out, there are no updates to make. */
@@ -327,7 +328,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
string_list_clear(&sl, 0);
}
-static int write_patterns_and_update(struct pattern_list *pl)
+static int write_patterns_and_update(struct repository *repo,
+ struct pattern_list *pl)
{
char *sparse_filename;
FILE *fp;
@@ -336,15 +338,15 @@ static int write_patterns_and_update(struct pattern_list *pl)
sparse_filename = get_sparse_checkout_filename();
- if (safe_create_leading_directories(the_repository, sparse_filename))
+ if (safe_create_leading_directories(repo, sparse_filename))
die(_("failed to create directory for sparse-checkout file"));
hold_lock_file_for_update(&lk, sparse_filename, LOCK_DIE_ON_ERROR);
- result = update_working_directory(pl);
+ result = update_working_directory(repo, pl);
if (result) {
rollback_lock_file(&lk);
- update_working_directory(NULL);
+ update_working_directory(repo, NULL);
goto out;
}
@@ -372,25 +374,26 @@ enum sparse_checkout_mode {
MODE_CONE_PATTERNS = 2,
};
-static int set_config(enum sparse_checkout_mode mode)
+static int set_config(struct repository *repo,
+ enum sparse_checkout_mode mode)
{
/* Update to use worktree config, if not already. */
- if (init_worktree_config(the_repository)) {
+ if (init_worktree_config(repo)) {
error(_("failed to initialize worktree config"));
return 1;
}
- if (repo_config_set_worktree_gently(the_repository,
+ if (repo_config_set_worktree_gently(repo,
"core.sparseCheckout",
mode ? "true" : "false") ||
- repo_config_set_worktree_gently(the_repository,
+ repo_config_set_worktree_gently(repo,
"core.sparseCheckoutCone",
mode == MODE_CONE_PATTERNS ?
"true" : "false"))
return 1;
if (mode == MODE_NO_PATTERNS)
- return set_sparse_index_config(the_repository, 0);
+ return set_sparse_index_config(repo, 0);
return 0;
}
@@ -410,7 +413,7 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
return MODE_ALL_PATTERNS;
}
-static int update_modes(int *cone_mode, int *sparse_index)
+static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index)
{
int mode, record_mode;
@@ -418,20 +421,20 @@ static int update_modes(int *cone_mode, int *sparse_index)
record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
mode = update_cone_mode(cone_mode);
- if (record_mode && set_config(mode))
+ if (record_mode && set_config(repo, mode))
return 1;
/* Set sparse-index/non-sparse-index mode if specified */
if (*sparse_index >= 0) {
- if (set_sparse_index_config(the_repository, *sparse_index) < 0)
+ if (set_sparse_index_config(repo, *sparse_index) < 0)
die(_("failed to modify sparse-index config"));
/* force an index rewrite */
- repo_read_index(the_repository);
- the_repository->index->updated_workdir = 1;
+ repo_read_index(repo);
+ repo->index->updated_workdir = 1;
if (!*sparse_index)
- ensure_full_index(the_repository->index);
+ ensure_full_index(repo->index);
}
return 0;
@@ -448,7 +451,7 @@ static struct sparse_checkout_init_opts {
} init_opts;
static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct pattern_list pl;
char *sparse_filename;
@@ -464,7 +467,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
};
setup_work_tree();
- repo_read_index(the_repository);
+ repo_read_index(repo);
init_opts.cone_mode = -1;
init_opts.sparse_index = -1;
@@ -473,7 +476,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
- if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index))
+ if (update_modes(repo, &init_opts.cone_mode, &init_opts.sparse_index))
return 1;
memset(&pl, 0, sizeof(pl));
@@ -485,14 +488,14 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
if (res >= 0) {
free(sparse_filename);
clear_pattern_list(&pl);
- return update_working_directory(NULL);
+ return update_working_directory(repo, NULL);
}
- if (repo_get_oid(the_repository, "HEAD", &oid)) {
+ if (repo_get_oid(repo, "HEAD", &oid)) {
FILE *fp;
/* assume we are in a fresh repo, but update the sparse-checkout file */
- if (safe_create_leading_directories(the_repository, sparse_filename))
+ if (safe_create_leading_directories(repo, sparse_filename))
die(_("unable to create leading directories of %s"),
sparse_filename);
fp = xfopen(sparse_filename, "w");
@@ -511,7 +514,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
add_pattern("!/*/", empty_base, 0, &pl, 0);
pl.use_cone_patterns = init_opts.cone_mode;
- return write_patterns_and_update(&pl);
+ return write_patterns_and_update(repo, &pl);
}
static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
@@ -674,7 +677,8 @@ static void add_patterns_literal(int argc, const char **argv,
add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
}
-static int modify_pattern_list(struct strvec *args, int use_stdin,
+static int modify_pattern_list(struct repository *repo,
+ struct strvec *args, int use_stdin,
enum modify_type m)
{
int result;
@@ -696,22 +700,23 @@ static int modify_pattern_list(struct strvec *args, int use_stdin,
}
if (!core_apply_sparse_checkout) {
- set_config(MODE_ALL_PATTERNS);
+ set_config(repo, MODE_ALL_PATTERNS);
core_apply_sparse_checkout = 1;
changed_config = 1;
}
- result = write_patterns_and_update(pl);
+ result = write_patterns_and_update(repo, pl);
if (result && changed_config)
- set_config(MODE_NO_PATTERNS);
+ set_config(repo, MODE_NO_PATTERNS);
clear_pattern_list(pl);
free(pl);
return result;
}
-static void sanitize_paths(struct strvec *args,
+static void sanitize_paths(struct repository *repo,
+ struct strvec *args,
const char *prefix, int skip_checks)
{
int i;
@@ -752,7 +757,7 @@ static void sanitize_paths(struct strvec *args,
for (i = 0; i < args->nr; i++) {
struct cache_entry *ce;
- struct index_state *index = the_repository->index;
+ struct index_state *index = repo->index;
int pos = index_name_pos(index, args->v[i], strlen(args->v[i]));
if (pos < 0)
@@ -779,7 +784,7 @@ static struct sparse_checkout_add_opts {
} add_opts;
static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
static struct option builtin_sparse_checkout_add_options[] = {
OPT_BOOL_F(0, "skip-checks", &add_opts.skip_checks,
@@ -796,7 +801,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
if (!core_apply_sparse_checkout)
die(_("no sparse-checkout to add to"));
- repo_read_index(the_repository);
+ repo_read_index(repo);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_add_options,
@@ -804,9 +809,9 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix,
for (int i = 0; i < argc; i++)
strvec_push(&patterns, argv[i]);
- sanitize_paths(&patterns, prefix, add_opts.skip_checks);
+ sanitize_paths(repo, &patterns, prefix, add_opts.skip_checks);
- ret = modify_pattern_list(&patterns, add_opts.use_stdin, ADD);
+ ret = modify_pattern_list(repo, &patterns, add_opts.use_stdin, ADD);
strvec_clear(&patterns);
return ret;
@@ -825,7 +830,7 @@ static struct sparse_checkout_set_opts {
} set_opts;
static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int default_patterns_nr = 2;
const char *default_patterns[] = {"/*", "!/*/", NULL};
@@ -847,7 +852,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
int ret;
setup_work_tree();
- repo_read_index(the_repository);
+ repo_read_index(repo);
set_opts.cone_mode = -1;
set_opts.sparse_index = -1;
@@ -856,7 +861,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage, 0);
- if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
+ if (update_modes(repo, &set_opts.cone_mode, &set_opts.sparse_index))
return 1;
/*
@@ -870,10 +875,10 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
} else {
for (int i = 0; i < argc; i++)
strvec_push(&patterns, argv[i]);
- sanitize_paths(&patterns, prefix, set_opts.skip_checks);
+ sanitize_paths(repo, &patterns, prefix, set_opts.skip_checks);
}
- ret = modify_pattern_list(&patterns, set_opts.use_stdin, REPLACE);
+ ret = modify_pattern_list(repo, &patterns, set_opts.use_stdin, REPLACE);
strvec_clear(&patterns);
return ret;
@@ -891,7 +896,7 @@ static struct sparse_checkout_reapply_opts {
static int sparse_checkout_reapply(int argc, const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
static struct option builtin_sparse_checkout_reapply_options[] = {
OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
@@ -912,12 +917,107 @@ static int sparse_checkout_reapply(int argc, const char **argv,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
- repo_read_index(the_repository);
+ repo_read_index(repo);
- if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
+ if (update_modes(repo, &reapply_opts.cone_mode, &reapply_opts.sparse_index))
return 1;
- return update_working_directory(NULL);
+ return update_working_directory(repo, NULL);
+}
+
+static char const * const builtin_sparse_checkout_clean_usage[] = {
+ "git sparse-checkout clean [-n|--dry-run]",
+ NULL
+};
+
+static int list_file_iterator(const char *path, const void *data)
+{
+ const char *msg = data;
+
+ printf(msg, path);
+ return 0;
+}
+
+static void list_every_file_in_dir(const char *msg,
+ const char *directory)
+{
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addstr(&path, directory);
+ for_each_file_in_dir(&path, list_file_iterator, msg);
+ strbuf_release(&path);
+}
+
+static const char *msg_remove = N_("Removing %s\n");
+static const char *msg_would_remove = N_("Would remove %s\n");
+
+static int sparse_checkout_clean(int argc, const char **argv,
+ const char *prefix,
+ struct repository *repo)
+{
+ struct strbuf full_path = STRBUF_INIT;
+ const char *msg = msg_remove;
+ size_t worktree_len;
+ int force = 0, dry_run = 0, verbose = 0;
+ int require_force = 1;
+
+ struct option builtin_sparse_checkout_clean_options[] = {
+ OPT__DRY_RUN(&dry_run, N_("dry run")),
+ OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE),
+ OPT__VERBOSE(&verbose, N_("report each affected file, not just directories")),
+ OPT_END(),
+ };
+
+ setup_work_tree();
+ if (!core_apply_sparse_checkout)
+ die(_("must be in a sparse-checkout to clean directories"));
+ if (!core_sparse_checkout_cone)
+ die(_("must be in a cone-mode sparse-checkout to clean directories"));
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_clean_options,
+ builtin_sparse_checkout_clean_usage, 0);
+
+ repo_config_get_bool(repo, "clean.requireforce", &require_force);
+ if (require_force && !force && !dry_run)
+ die(_("for safety, refusing to clean without one of --force or --dry-run"));
+
+ if (dry_run)
+ msg = msg_would_remove;
+
+ if (repo_read_index(repo) < 0)
+ die(_("failed to read index"));
+
+ if (convert_to_sparse(repo->index, SPARSE_INDEX_MEMORY_ONLY) ||
+ repo->index->sparse_index == INDEX_EXPANDED)
+ die(_("failed to convert index to a sparse index; resolve merge conflicts and try again"));
+
+ strbuf_addstr(&full_path, repo->worktree);
+ strbuf_addch(&full_path, '/');
+ worktree_len = full_path.len;
+
+ for (size_t i = 0; i < repo->index->cache_nr; i++) {
+ struct cache_entry *ce = repo->index->cache[i];
+ if (!S_ISSPARSEDIR(ce->ce_mode))
+ continue;
+ strbuf_setlen(&full_path, worktree_len);
+ strbuf_add(&full_path, ce->name, ce->ce_namelen);
+
+ if (!is_directory(full_path.buf))
+ continue;
+
+ if (verbose)
+ list_every_file_in_dir(msg, ce->name);
+ else
+ printf(msg, ce->name);
+
+ if (dry_run <= 0 &&
+ remove_dir_recursively(&full_path, 0))
+ warning_errno(_("failed to remove '%s'"), ce->name);
+ }
+
+ strbuf_release(&full_path);
+ return 0;
}
static char const * const builtin_sparse_checkout_disable_usage[] = {
@@ -927,7 +1027,7 @@ static char const * const builtin_sparse_checkout_disable_usage[] = {
static int sparse_checkout_disable(int argc, const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
static struct option builtin_sparse_checkout_disable_options[] = {
OPT_END(),
@@ -955,7 +1055,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
* are expecting to do that when disabling sparse-checkout.
*/
give_advice_on_expansion = 0;
- repo_read_index(the_repository);
+ repo_read_index(repo);
memset(&pl, 0, sizeof(pl));
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
@@ -966,13 +1066,13 @@ static int sparse_checkout_disable(int argc, const char **argv,
add_pattern("/*", empty_base, 0, &pl, 0);
prepare_repo_settings(the_repository);
- the_repository->settings.sparse_index = 0;
+ repo->settings.sparse_index = 0;
- if (update_working_directory(&pl))
+ if (update_working_directory(repo, &pl))
die(_("error while refreshing working directory"));
clear_pattern_list(&pl);
- return set_config(MODE_NO_PATTERNS);
+ return set_config(repo, MODE_NO_PATTERNS);
}
static char const * const builtin_sparse_checkout_check_rules_usage[] = {
@@ -987,14 +1087,17 @@ static struct sparse_checkout_check_rules_opts {
char *rules_file;
} check_rules_opts;
-static int check_rules(struct pattern_list *pl, int null_terminated) {
+static int check_rules(struct repository *repo,
+ struct pattern_list *pl,
+ int null_terminated)
+{
struct strbuf line = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
char *path;
int line_terminator = null_terminated ? 0 : '\n';
strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul
: strbuf_getline;
- the_repository->index->sparse_checkout_patterns = pl;
+ repo->index->sparse_checkout_patterns = pl;
while (!getline_fn(&line, stdin)) {
path = line.buf;
if (!null_terminated && line.buf[0] == '"') {
@@ -1006,7 +1109,7 @@ static int check_rules(struct pattern_list *pl, int null_terminated) {
path = unquoted.buf;
}
- if (path_in_sparse_checkout(path, the_repository->index))
+ if (path_in_sparse_checkout(path, repo->index))
write_name_quoted(path, stdout, line_terminator);
}
strbuf_release(&line);
@@ -1016,7 +1119,7 @@ static int check_rules(struct pattern_list *pl, int null_terminated) {
}
static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
static struct option builtin_sparse_checkout_check_rules_options[] = {
OPT_BOOL('z', NULL, &check_rules_opts.null_termination,
@@ -1055,7 +1158,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
free(sparse_filename);
}
- ret = check_rules(&pl, check_rules_opts.null_termination);
+ ret = check_rules(repo, &pl, check_rules_opts.null_termination);
clear_pattern_list(&pl);
free(check_rules_opts.rules_file);
return ret;
@@ -1073,6 +1176,7 @@ int cmd_sparse_checkout(int argc,
OPT_SUBCOMMAND("set", &fn, sparse_checkout_set),
OPT_SUBCOMMAND("add", &fn, sparse_checkout_add),
OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply),
+ OPT_SUBCOMMAND("clean", &fn, sparse_checkout_clean),
OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable),
OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules),
OPT_END(),
@@ -1084,8 +1188,8 @@ int cmd_sparse_checkout(int argc,
repo_config(the_repository, git_default_config, NULL);
- prepare_repo_settings(the_repository);
- the_repository->settings.command_requires_full_index = 0;
+ prepare_repo_settings(repo);
+ repo->settings.command_requires_full_index = 0;
return fn(argc, argv, prefix, repo);
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 948eba06fb..3b50905233 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1306,7 +1306,7 @@ done:
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
struct strbuf *out_patch, int quiet,
- struct add_p_opt *add_p_opt)
+ struct interactive_options *interactive_opts)
{
int ret = 0;
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
@@ -1331,7 +1331,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
- ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps);
+ ret = !!run_add_p(the_repository, ADD_P_STASH, interactive_opts, NULL, ps);
the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env)
@@ -1427,7 +1427,8 @@ done:
}
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
- int include_untracked, int patch_mode, struct add_p_opt *add_p_opt,
+ int include_untracked, int patch_mode,
+ struct interactive_options *interactive_opts,
int only_staged, struct stash_info *info, struct strbuf *patch,
int quiet)
{
@@ -1509,7 +1510,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
untracked_commit_option = 1;
}
if (patch_mode) {
- ret = stash_patch(info, ps, patch, quiet, add_p_opt);
+ ret = stash_patch(info, ps, patch, quiet, interactive_opts);
if (ret < 0) {
if (!quiet)
fprintf_ln(stderr, _("Cannot save the current "
@@ -1595,7 +1596,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
}
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
- int keep_index, int patch_mode, struct add_p_opt *add_p_opt,
+ int keep_index, int patch_mode,
+ struct interactive_options *interactive_opts,
int include_untracked, int only_staged)
{
int ret = 0;
@@ -1667,7 +1669,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
if (stash_msg)
strbuf_addstr(&stash_msg_buf, stash_msg);
if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
- add_p_opt, only_staged, &info, &patch, quiet)) {
+ interactive_opts, only_staged, &info, &patch, quiet)) {
ret = -1;
goto done;
}
@@ -1841,7 +1843,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
const char *stash_msg = NULL;
char *pathspec_from_file = NULL;
struct pathspec ps;
- struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
+ struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
@@ -1849,8 +1851,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
- OPT_DIFF_UNIFIED(&add_p_opt.context),
- OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
+ OPT_DIFF_UNIFIED(&interactive_opts.context),
+ OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext),
OPT__QUIET(&quiet, N_("quiet mode")),
OPT_BOOL('u', "include-untracked", &include_untracked,
N_("include untracked files in stash")),
@@ -1907,19 +1909,19 @@ static int push_stash(int argc, const char **argv, const char *prefix,
}
if (!patch_mode) {
- if (add_p_opt.context != -1)
+ if (interactive_opts.context != -1)
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
- if (add_p_opt.interhunkcontext != -1)
+ if (interactive_opts.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
}
- if (add_p_opt.context < -1)
+ if (interactive_opts.context < -1)
die(_("'%s' cannot be negative"), "--unified");
- if (add_p_opt.interhunkcontext < -1)
+ if (interactive_opts.interhunkcontext < -1)
die(_("'%s' cannot be negative"), "--inter-hunk-context");
ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- &add_p_opt, include_untracked, only_staged);
+ &interactive_opts, include_untracked, only_staged);
clear_pathspec(&ps);
free(pathspec_from_file);
@@ -1944,7 +1946,7 @@ static int save_stash(int argc, const char **argv, const char *prefix,
const char *stash_msg = NULL;
struct pathspec ps;
struct strbuf stash_msg_buf = STRBUF_INIT;
- struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
+ struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
@@ -1952,8 +1954,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
- OPT_DIFF_UNIFIED(&add_p_opt.context),
- OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
+ OPT_DIFF_UNIFIED(&interactive_opts.context),
+ OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext),
OPT__QUIET(&quiet, N_("quiet mode")),
OPT_BOOL('u', "include-untracked", &include_untracked,
N_("include untracked files in stash")),
@@ -1973,20 +1975,20 @@ static int save_stash(int argc, const char **argv, const char *prefix,
memset(&ps, 0, sizeof(ps));
- if (add_p_opt.context < -1)
+ if (interactive_opts.context < -1)
die(_("'%s' cannot be negative"), "--unified");
- if (add_p_opt.interhunkcontext < -1)
+ if (interactive_opts.interhunkcontext < -1)
die(_("'%s' cannot be negative"), "--inter-hunk-context");
if (!patch_mode) {
- if (add_p_opt.context != -1)
+ if (interactive_opts.context != -1)
die(_("the option '%s' requires '%s'"), "--unified", "--patch");
- if (add_p_opt.interhunkcontext != -1)
+ if (interactive_opts.interhunkcontext != -1)
die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
}
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
- patch_mode, &add_p_opt, include_untracked,
+ patch_mode, &interactive_opts, include_untracked,
only_staged);
strbuf_release(&stash_msg_buf);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index fcd73abe53..96d5301510 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -593,16 +593,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("\n");
}
-static int handle_submodule_head_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid,
- int flags UNUSED,
- void *cb_data)
+static int handle_submodule_head_ref(const struct reference *ref, void *cb_data)
{
struct object_id *output = cb_data;
- if (oid)
- oidcpy(output, oid);
+ if (ref->oid)
+ oidcpy(output, ref->oid);
return 0;
}
@@ -1208,6 +1204,22 @@ static int module_summary(int argc, const char **argv, const char *prefix,
return ret;
}
+static int module_gitdir(int argc, const char **argv, const char *prefix UNUSED,
+ struct repository *repo)
+{
+ struct strbuf gitdir = STRBUF_INIT;
+
+ if (argc != 2)
+ usage(_("git submodule--helper gitdir <name>"));
+
+ submodule_name_to_gitdir(&gitdir, repo, argv[1]);
+
+ printf("%s\n", gitdir.buf);
+
+ strbuf_release(&gitdir);
+ return 0;
+}
+
struct sync_cb {
const char *prefix;
const char *super_prefix;
@@ -3187,13 +3199,13 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
static int add_submodule(const struct add_data *add_data)
{
- char *submod_gitdir_path;
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
struct string_list reference = STRING_LIST_INIT_NODUP;
int ret = -1;
/* perhaps the path already exists and is already a git repo, else clone it */
if (is_directory(add_data->sm_path)) {
+ char *submod_gitdir_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);
@@ -3207,10 +3219,11 @@ static int add_submodule(const struct add_data *add_data)
free(submod_gitdir_path);
} else {
struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf submod_gitdir = STRBUF_INIT;
- submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);
+ submodule_name_to_gitdir(&submod_gitdir, the_repository, add_data->sm_name);
- if (is_directory(submod_gitdir_path)) {
+ if (is_directory(submod_gitdir.buf)) {
if (!add_data->force) {
struct strbuf msg = STRBUF_INIT;
char *die_msg;
@@ -3219,8 +3232,8 @@ static int add_submodule(const struct add_data *add_data)
"locally with remote(s):\n"),
add_data->sm_name);
- append_fetch_remotes(&msg, submod_gitdir_path);
- free(submod_gitdir_path);
+ append_fetch_remotes(&msg, submod_gitdir.buf);
+ strbuf_release(&submod_gitdir);
strbuf_addf(&msg, _("If you want to reuse this local git "
"directory instead of cloning again from\n"
@@ -3238,7 +3251,7 @@ static int add_submodule(const struct add_data *add_data)
"submodule '%s'\n"), add_data->sm_name);
}
}
- free(submod_gitdir_path);
+ strbuf_release(&submod_gitdir);
clone_data.prefix = add_data->prefix;
clone_data.path = add_data->sm_path;
@@ -3590,6 +3603,7 @@ int cmd_submodule__helper(int argc,
NULL
};
struct option options[] = {
+ OPT_SUBCOMMAND("gitdir", &fn, module_gitdir),
OPT_SUBCOMMAND("clone", &fn, module_clone),
OPT_SUBCOMMAND("add", &fn, module_add),
OPT_SUBCOMMAND("update", &fn, module_update),
diff --git a/builtin/tag.c b/builtin/tag.c
index f0665af3ac..81dedb9a4c 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return -1;
if (format->format)
- pretty_print_ref(name, oid, format);
+ pretty_print_ref(name, oid, NULL, format);
return 0;
}
@@ -499,8 +499,7 @@ int cmd_tag(int argc,
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
- OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
- N_("add custom trailer(s)"), PARSE_OPT_NONEG),
+ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, parse_opt_strvec),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_CLEANUP(&cleanup_arg),
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index ef79e43715..6fc64e9e4b 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -363,7 +363,7 @@ struct input_zstream_data {
int status;
};
-static const void *feed_input_zstream(struct input_stream *in_stream,
+static const void *feed_input_zstream(struct odb_write_stream *in_stream,
unsigned long *readlen)
{
struct input_zstream_data *data = in_stream->data;
@@ -393,7 +393,7 @@ static void stream_blob(unsigned long size, unsigned nr)
{
git_zstream zstream = { 0 };
struct input_zstream_data data = { 0 };
- struct input_stream in_stream = {
+ struct odb_write_stream in_stream = {
.read = feed_input_zstream,
.data = &data,
};
@@ -402,8 +402,7 @@ static void stream_blob(unsigned long size, unsigned nr)
data.zstream = &zstream;
git_inflate_init(&zstream);
- if (stream_loose_object(the_repository->objects->sources,
- &in_stream, size, &info->oid))
+ if (odb_write_object_stream(the_repository->objects, &in_stream, size, &info->oid))
die(_("failed to write object in stream"));
if (data.status != Z_STREAM_END)
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index cd6bc11095..558121eaa1 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -67,7 +67,7 @@ int cmd_verify_tag(int argc,
}
if (format.format)
- pretty_print_ref(name, &oid, &format);
+ pretty_print_ref(name, &oid, NULL, &format);
}
return had_error;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 812774a5ca..b7f323b5e4 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -635,11 +635,7 @@ static void print_preparing_worktree_line(int detach,
*
* Returns 0 on failure and non-zero on success.
*/
-static int first_valid_ref(const char *refname UNUSED,
- const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data UNUSED)
+static int first_valid_ref(const struct reference *ref UNUSED, void *cb_data UNUSED)
{
return 1;
}