summaryrefslogtreecommitdiffstats
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/backfill.c22
-rw-r--r--builtin/hook.c61
-rw-r--r--builtin/log.c89
-rw-r--r--builtin/receive-pack.c64
-rw-r--r--builtin/replay.c36
-rw-r--r--builtin/worktree.c21
6 files changed, 206 insertions, 87 deletions
diff --git a/builtin/backfill.c b/builtin/backfill.c
index e9a33e81be..2c5ce56fb7 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -35,6 +35,7 @@ struct backfill_context {
struct oid_array current_batch;
size_t min_batch_size;
int sparse;
+ struct rev_info revs;
};
static void backfill_context_clear(struct backfill_context *ctx)
@@ -79,7 +80,6 @@ static int fill_missing_blobs(const char *path UNUSED,
static int do_backfill(struct backfill_context *ctx)
{
- struct rev_info revs;
struct path_walk_info info = PATH_WALK_INFO_INIT;
int ret;
@@ -91,13 +91,14 @@ static int do_backfill(struct backfill_context *ctx)
}
}
- repo_init_revisions(ctx->repo, &revs, "");
- handle_revision_arg("HEAD", &revs, 0, 0);
+ /* Walk from HEAD if otherwise unspecified. */
+ if (!ctx->revs.pending.nr)
+ add_head_to_pending(&ctx->revs);
info.blobs = 1;
info.tags = info.commits = info.trees = 0;
- info.revs = &revs;
+ info.revs = &ctx->revs;
info.path_fn = fill_missing_blobs;
info.path_fn_data = ctx;
@@ -108,7 +109,6 @@ static int do_backfill(struct backfill_context *ctx)
download_batch(ctx);
path_walk_info_clear(&info);
- release_revisions(&revs);
return ret;
}
@@ -120,6 +120,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
.current_batch = OID_ARRAY_INIT,
.min_batch_size = 50000,
.sparse = 0,
+ .revs = REV_INFO_INIT,
};
struct option options[] = {
OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size,
@@ -134,7 +135,15 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
builtin_backfill_usage, options);
argc = parse_options(argc, argv, prefix, options, builtin_backfill_usage,
- 0);
+ PARSE_OPT_KEEP_UNKNOWN_OPT |
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_DASHDASH);
+
+ repo_init_revisions(repo, &ctx.revs, prefix);
+ argc = setup_revisions(argc, argv, &ctx.revs, NULL);
+
+ if (argc > 1)
+ die(_("unrecognized argument: %s"), argv[1]);
repo_config(repo, git_default_config, NULL);
@@ -143,5 +152,6 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
result = do_backfill(&ctx);
backfill_context_clear(&ctx);
+ release_revisions(&ctx.revs);
return result;
}
diff --git a/builtin/hook.c b/builtin/hook.c
index 83020dfb4f..c0585587e5 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -4,14 +4,22 @@
#include "environment.h"
#include "gettext.h"
#include "hook.h"
+#include "hook-list.h"
#include "parse-options.h"
-#include "strvec.h"
-#include "abspath.h"
#define BUILTIN_HOOK_RUN_USAGE \
- N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
+ N_("git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
#define BUILTIN_HOOK_LIST_USAGE \
- N_("git hook list [-z] <hook-name>")
+ N_("git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>")
+
+static int is_known_hook(const char *name)
+{
+ const char **p;
+ for (p = hook_name_list; *p; p++)
+ if (!strcmp(*p, name))
+ return 1;
+ return 0;
+}
static const char * const builtin_hook_usage[] = {
BUILTIN_HOOK_RUN_USAGE,
@@ -35,11 +43,17 @@ static int list(int argc, const char **argv, const char *prefix,
struct string_list_item *item;
const char *hookname = NULL;
int line_terminator = '\n';
+ int show_scope = 0;
+ int allow_unknown = 0;
int ret = 0;
struct option list_options[] = {
OPT_SET_INT('z', NULL, &line_terminator,
N_("use NUL as line terminator"), '\0'),
+ OPT_BOOL(0, "show-scope", &show_scope,
+ N_("show the config scope that defined each hook")),
+ OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown,
+ N_("allow running a hook with a non-native hook name")),
OPT_END(),
};
@@ -51,15 +65,22 @@ static int list(int argc, const char **argv, const char *prefix,
* arguments later they probably should be caught by parse_options.
*/
if (argc != 1)
- usage_msg_opt(_("You must specify a hook event name to list."),
+ usage_msg_opt(_("you must specify a hook event name to list"),
builtin_hook_list_usage, list_options);
hookname = argv[0];
+ if (!allow_unknown && !is_known_hook(hookname)) {
+ error(_("unknown hook event '%s';\n"
+ "use --allow-unknown-hook-name to allow non-native hook names"),
+ hookname);
+ return 1;
+ }
+
head = list_hooks(repo, hookname, NULL);
if (!head->nr) {
- warning(_("No hooks found for event '%s'"), hookname);
+ warning(_("no hooks found for event '%s'"), hookname);
ret = 1; /* no hooks found */
goto cleanup;
}
@@ -71,16 +92,27 @@ static int list(int argc, const char **argv, const char *prefix,
case HOOK_TRADITIONAL:
printf("%s%c", _("hook from hookdir"), line_terminator);
break;
- case HOOK_CONFIGURED:
- printf("%s%c", h->u.configured.friendly_name, line_terminator);
+ case HOOK_CONFIGURED: {
+ const char *name = h->u.configured.friendly_name;
+ const char *scope = show_scope ?
+ config_scope_name(h->u.configured.scope) : NULL;
+ if (scope)
+ printf("%s\t%s%s%c", scope,
+ h->u.configured.disabled ? "disabled\t" : "",
+ name, line_terminator);
+ else
+ printf("%s%s%c",
+ h->u.configured.disabled ? "disabled\t" : "",
+ name, line_terminator);
break;
+ }
default:
BUG("unknown hook kind");
}
}
cleanup:
- hook_list_clear(head, NULL);
+ string_list_clear_func(head, hook_free);
free(head);
return ret;
}
@@ -91,8 +123,11 @@ static int run(int argc, const char **argv, const char *prefix,
int i;
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
int ignore_missing = 0;
+ int allow_unknown = 0;
const char *hook_name;
struct option run_options[] = {
+ OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown,
+ N_("allow running a hook with a non-native hook name")),
OPT_BOOL(0, "ignore-missing", &ignore_missing,
N_("silently ignore missing requested <hook-name>")),
OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
@@ -124,6 +159,14 @@ static int run(int argc, const char **argv, const char *prefix,
repo_config(the_repository, git_default_config, NULL);
hook_name = argv[0];
+
+ if (!allow_unknown && !is_known_hook(hook_name)) {
+ error(_("unknown hook event '%s';\n"
+ "use --allow-unknown-hook-name to allow non-native hook names"),
+ hook_name);
+ return 1;
+ }
+
if (!ignore_missing)
opt.error_if_missing = 1;
ret = run_hooks_opt(the_repository, hook_name, &opt);
diff --git a/builtin/log.c b/builtin/log.c
index 89e8b8f011..8c0939dd42 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -40,6 +40,7 @@
#include "progress.h"
#include "commit-slab.h"
#include "advice.h"
+#include "utf8.h"
#include "commit-reach.h"
#include "range-diff.h"
@@ -886,6 +887,7 @@ struct format_config {
char *signature;
char *signature_file;
enum cover_setting config_cover_letter;
+ char *fmt_cover_letter_commit_list;
char *config_output_directory;
enum cover_from_description cover_from_description_mode;
int show_notes;
@@ -930,6 +932,7 @@ static void format_config_release(struct format_config *cfg)
string_list_clear(&cfg->extra_cc, 0);
strbuf_release(&cfg->sprefix);
free(cfg->fmt_patch_suffix);
+ free(cfg->fmt_cover_letter_commit_list);
}
static enum cover_from_description parse_cover_from_description(const char *arg)
@@ -1052,6 +1055,10 @@ static int git_format_config(const char *var, const char *value,
cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
return 0;
}
+ if (!strcmp(var, "format.commitlistformat")) {
+ FREE_AND_NULL(cfg->fmt_cover_letter_commit_list);
+ return git_config_string(&cfg->fmt_cover_letter_commit_list, var, value);
+ }
if (!strcmp(var, "format.outputdirectory")) {
FREE_AND_NULL(cfg->config_output_directory);
return git_config_string(&cfg->config_output_directory, var, value);
@@ -1335,13 +1342,54 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev)
}
}
+static void generate_shortlog_cover_letter(struct shortlog *log,
+ struct rev_info *rev,
+ struct commit **list,
+ int nr)
+{
+ shortlog_init(log);
+ log->wrap_lines = 1;
+ log->wrap = MAIL_DEFAULT_WRAP;
+ log->in1 = 2;
+ log->in2 = 4;
+ log->file = rev->diffopt.file;
+ log->groups = SHORTLOG_GROUP_AUTHOR;
+ shortlog_finish_setup(log);
+ for (int i = 0; i < nr; i++)
+ shortlog_add_commit(log, list[i]);
+
+ shortlog_output(log);
+}
+
+static void generate_commit_list_cover(FILE *cover_file, const char *format,
+ struct commit **list, int n)
+{
+ struct strbuf commit_line = STRBUF_INIT;
+ struct pretty_print_context ctx = {0};
+ struct rev_info rev = REV_INFO_INIT;
+
+ rev.total = n;
+ ctx.rev = &rev;
+ for (int i = 1; i <= n; i++) {
+ rev.nr = i;
+ repo_format_commit_message(the_repository, list[n - i], format,
+ &commit_line, &ctx);
+ fprintf(cover_file, "%s\n", commit_line.buf);
+ strbuf_reset(&commit_line);
+ }
+ fprintf(cover_file, "\n");
+
+ strbuf_release(&commit_line);
+}
+
static void make_cover_letter(struct rev_info *rev, int use_separate_file,
struct commit *origin,
int nr, struct commit **list,
const char *description_file,
const char *branch_name,
int quiet,
- const struct format_config *cfg)
+ const struct format_config *cfg,
+ const char *format)
{
const char *from;
struct shortlog log;
@@ -1388,18 +1436,17 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
free(pp.after_subject);
strbuf_release(&sb);
- shortlog_init(&log);
- log.wrap_lines = 1;
- log.wrap = MAIL_DEFAULT_WRAP;
- log.in1 = 2;
- log.in2 = 4;
- log.file = rev->diffopt.file;
- log.groups = SHORTLOG_GROUP_AUTHOR;
- shortlog_finish_setup(&log);
- for (i = 0; i < nr; i++)
- shortlog_add_commit(&log, list[i]);
-
- shortlog_output(&log);
+ if (skip_prefix(format, "log:", &format))
+ generate_commit_list_cover(rev->diffopt.file, format, list, nr);
+ else if (!strcmp(format, "shortlog"))
+ generate_shortlog_cover_letter(&log, rev, list, nr);
+ else if (!strcmp(format, "modern"))
+ generate_commit_list_cover(rev->diffopt.file, "%w(72)[%(count)/%(total)] %s",
+ list, nr);
+ else if (strchr(format, '%'))
+ generate_commit_list_cover(rev->diffopt.file, format, list, nr);
+ else
+ die(_("'%s' is not a valid format string"), format);
/* We can only do diffstat with a unique reference point */
if (origin)
@@ -1917,6 +1964,7 @@ int cmd_format_patch(int argc,
int just_numbers = 0;
int ignore_if_in_upstream = 0;
int cover_letter = -1;
+ const char *cover_letter_fmt = NULL;
int boundary_count = 0;
int no_binary_diff = 0;
int zero_commit = 0;
@@ -1963,6 +2011,8 @@ int cmd_format_patch(int argc,
N_("print patches to standard out")),
OPT_BOOL(0, "cover-letter", &cover_letter,
N_("generate a cover letter")),
+ OPT_STRING(0, "commit-list-format", &cover_letter_fmt, N_("format-spec"),
+ N_("format spec used for the commit list in the cover letter")),
OPT_BOOL(0, "numbered-files", &just_numbers,
N_("use simple number sequence for output file names")),
OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
@@ -2300,6 +2350,15 @@ int cmd_format_patch(int argc,
/* nothing to do */
goto done;
total = list.nr;
+
+ if (!cover_letter_fmt) {
+ cover_letter_fmt = cfg.fmt_cover_letter_commit_list;
+ if (!cover_letter_fmt)
+ cover_letter_fmt = "shortlog";
+ } else if (cover_letter == -1) {
+ cover_letter = 1;
+ }
+
if (cover_letter == -1) {
if (cfg.config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
@@ -2386,12 +2445,14 @@ int cmd_format_patch(int argc,
}
rev.numbered_files = just_numbers;
rev.patch_suffix = fmt_patch_suffix;
+
if (cover_letter) {
if (cfg.thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, !!output_directory,
origin, list.nr, list.items,
- description_file, branch_name, quiet, &cfg);
+ description_file, branch_name, quiet, &cfg,
+ cover_letter_fmt);
print_bases(&bases, rev.diffopt.file);
print_signature(signature, rev.diffopt.file);
total++;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e34edff406..cb3656a034 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -3,46 +3,45 @@
#include "builtin.h"
#include "abspath.h"
-
+#include "commit.h"
+#include "commit-reach.h"
#include "config.h"
+#include "connect.h"
+#include "connected.h"
#include "environment.h"
+#include "exec-cmd.h"
+#include "fsck.h"
#include "gettext.h"
+#include "gpg-interface.h"
#include "hex.h"
-#include "lockfile.h"
-#include "pack.h"
-#include "refs.h"
-#include "pkt-line.h"
-#include "sideband.h"
-#include "run-command.h"
#include "hook.h"
-#include "exec-cmd.h"
-#include "commit.h"
+#include "lockfile.h"
#include "object.h"
-#include "remote.h"
-#include "connect.h"
-#include "string-list.h"
-#include "oid-array.h"
-#include "connected.h"
-#include "strvec.h"
-#include "version.h"
-#include "gpg-interface.h"
-#include "sigchain.h"
-#include "fsck.h"
-#include "tmp-objdir.h"
-#include "oidset.h"
-#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
+#include "oid-array.h"
+#include "oidset.h"
+#include "pack.h"
+#include "packfile.h"
+#include "parse-options.h"
+#include "pkt-line.h"
#include "protocol.h"
-#include "commit-reach.h"
+#include "refs.h"
+#include "remote.h"
+#include "run-command.h"
#include "server-info.h"
+#include "setup.h"
+#include "shallow.h"
+#include "sideband.h"
+#include "sigchain.h"
+#include "string-list.h"
+#include "strvec.h"
+#include "tmp-objdir.h"
#include "trace.h"
#include "trace2.h"
+#include "version.h"
#include "worktree.h"
-#include "shallow.h"
-#include "setup.h"
-#include "parse-options.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@@ -904,11 +903,14 @@ static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_
static void *receive_hook_feed_state_alloc(void *feed_pipe_ctx)
{
struct receive_hook_feed_state *init_state = feed_pipe_ctx;
- struct receive_hook_feed_state *data = xcalloc(1, sizeof(*data));
+ struct receive_hook_feed_state *data;
+
+ CALLOC_ARRAY(data, 1);
data->report = init_state->report;
data->cmd = init_state->cmd;
data->skip_broken = init_state->skip_broken;
strbuf_init(&data->buf, 0);
+
return data;
}
@@ -928,7 +930,11 @@ static int run_receive_hook(struct command *commands,
{
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
struct command *iter = commands;
- struct receive_hook_feed_state feed_init_state = { 0 };
+ struct receive_hook_feed_state feed_init_state = {
+ .cmd = commands,
+ .skip_broken = skip_broken,
+ .buf = STRBUF_INIT,
+ };
struct async sideband_async;
int sideband_async_started = 0;
int saved_stderr = -1;
@@ -961,8 +967,6 @@ static int run_receive_hook(struct command *commands,
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
/* set up stdin callback */
- feed_init_state.cmd = commands;
- feed_init_state.skip_broken = skip_broken;
opt.feed_pipe_ctx = &feed_init_state;
opt.feed_pipe = feed_receive_hook_cb;
opt.feed_pipe_cb_data_alloc = receive_hook_feed_state_alloc;
diff --git a/builtin/replay.c b/builtin/replay.c
index 2cdde830a8..a0879b020f 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -79,11 +79,12 @@ int cmd_replay(int argc,
struct ref_transaction *transaction = NULL;
struct strbuf transaction_err = STRBUF_INIT;
struct strbuf reflog_msg = STRBUF_INIT;
+ int desired_reverse;
int ret = 0;
const char *const replay_usage[] = {
N_("(EXPERIMENTAL!) git replay "
- "([--contained] --onto <newbase> | --advance <branch>) "
+ "([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) "
"[--ref-action[=<mode>]] <revision-range>"),
NULL
};
@@ -96,6 +97,9 @@ int cmd_replay(int argc,
N_("replay onto given commit")),
OPT_BOOL(0, "contained", &opts.contained,
N_("update all branches that point at commits in <revision-range>")),
+ OPT_STRING(0, "revert", &opts.revert,
+ N_("branch"),
+ N_("revert commits onto given branch")),
OPT_STRING(0, "ref-action", &ref_action,
N_("mode"),
N_("control ref update behavior (update|print)")),
@@ -105,19 +109,31 @@ int cmd_replay(int argc,
argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
- if (!opts.onto && !opts.advance) {
- error(_("option --onto or --advance is mandatory"));
+ /* Exactly one mode must be specified */
+ if (!opts.onto && !opts.advance && !opts.revert) {
+ error(_("exactly one of --onto, --advance, or --revert is required"));
usage_with_options(replay_usage, replay_options);
}
+ die_for_incompatible_opt3(!!opts.onto, "--onto",
+ !!opts.advance, "--advance",
+ !!opts.revert, "--revert");
die_for_incompatible_opt2(!!opts.advance, "--advance",
opts.contained, "--contained");
- die_for_incompatible_opt2(!!opts.advance, "--advance",
- !!opts.onto, "--onto");
+ die_for_incompatible_opt2(!!opts.revert, "--revert",
+ opts.contained, "--contained");
/* Parse ref action mode from command line or config */
ref_mode = get_ref_action_mode(repo, ref_action);
+ /*
+ * Cherry-pick/rebase need oldest-first ordering so that each
+ * replayed commit can build on its already-replayed parent.
+ * Revert needs newest-first ordering (like git revert) to
+ * reduce conflicts by peeling off changes from the top.
+ */
+ desired_reverse = !opts.revert;
+
repo_init_revisions(repo, &revs, prefix);
/*
@@ -129,7 +145,7 @@ int cmd_replay(int argc,
* some options changing these values if we think they could
* be useful.
*/
- revs.reverse = 1;
+ revs.reverse = desired_reverse;
revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
revs.topo_order = 1;
revs.simplify_history = 0;
@@ -144,11 +160,11 @@ int cmd_replay(int argc,
* Detect and warn if we override some user specified rev
* walking options.
*/
- if (revs.reverse != 1) {
+ if (revs.reverse != desired_reverse) {
warning(_("some rev walking options will be overridden as "
"'%s' bit in 'struct rev_info' will be forced"),
"reverse");
- revs.reverse = 1;
+ revs.reverse = desired_reverse;
}
if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
warning(_("some rev walking options will be overridden as "
@@ -174,7 +190,9 @@ int cmd_replay(int argc,
goto cleanup;
/* Build reflog message */
- if (opts.advance) {
+ if (opts.revert) {
+ strbuf_addf(&reflog_msg, "replay --revert %s", opts.revert);
+ } else if (opts.advance) {
strbuf_addf(&reflog_msg, "replay --advance %s", opts.advance);
} else {
struct object_id oid;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 4035b1cb06..4fd6f7575f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts)
if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
return 1;
} else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
- if (!opts->quiet) {
- struct strbuf path = STRBUF_INIT;
- struct strbuf contents = STRBUF_INIT;
- char *wt_gitdir = get_worktree_git_dir(NULL);
-
- strbuf_add_real_path(&path, wt_gitdir);
- strbuf_addstr(&path, "/HEAD");
- strbuf_read_file(&contents, path.buf, 64);
- strbuf_stripspace(&contents, NULL);
- strbuf_strip_suffix(&contents, "\n");
-
- warning(_("HEAD points to an invalid (or orphaned) reference.\n"
- "HEAD path: '%s'\n"
- "HEAD contents: '%s'"),
- path.buf, contents.buf);
- strbuf_release(&path);
- strbuf_release(&contents);
- free(wt_gitdir);
- }
+ if (!opts->quiet)
+ warning(_("HEAD points to an invalid (or orphaned) reference.\n"));
return 1;
}
return 0;