Merge branch 'ss/history-sign-rewrites' into seen
The git history command and its subcommands have been updated to honor the commit.gpgSign configuration variable and accept the --gpg-sign and --no-gpg-sign command-line options, ensuring both rewritten commits and their replayed descendants are cryptographically signed by the committer. * ss/history-sign-rewrites: history: sign rewritten commits replay: allow callers to sign commitsseen
commit
f09e2f0cc4
|
|
@ -8,11 +8,11 @@ git-history - EXPERIMENTAL: Rewrite history
|
|||
SYNOPSIS
|
||||
--------
|
||||
[synopsis]
|
||||
git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)]
|
||||
git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]
|
||||
git history reword <commit> [--dry-run] [--update-refs=(branches|head)]
|
||||
git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]
|
||||
git history squash [--dry-run] [--update-refs=(branches|head)] [--[no-]edit] <revision-range>
|
||||
git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)] [--[no-]gpg-sign[=<key-id>]]
|
||||
git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)] [--[no-]gpg-sign[=<key-id>]]
|
||||
git history reword <commit> [--dry-run] [--update-refs=(branches|head)] [--[no-]gpg-sign[=<key-id>]]
|
||||
git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--[no-]gpg-sign[=<key-id>]] [--] [<pathspec>...]
|
||||
git history squash [--dry-run] [--update-refs=(branches|head)] [--[no-]edit] [--[no-]gpg-sign[=<key-id>]] <revision-range>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
|
@ -180,6 +180,14 @@ OPTIONS
|
|||
`--reedit-message`::
|
||||
Open an editor to modify the target commit's message.
|
||||
|
||||
`-S[<key-id>]`::
|
||||
`--gpg-sign[=<key-id>]`::
|
||||
`--no-gpg-sign`::
|
||||
GPG-sign rewritten commits. The _<key-id>_ argument is optional and
|
||||
defaults to the committer identity; if specified, it must be stuck to
|
||||
the option without a space. `--no-gpg-sign` is useful to countermand
|
||||
both `commit.gpgSign` configuration and earlier `--gpg-sign`.
|
||||
|
||||
`--empty=(drop|keep|abort)`::
|
||||
Control what happens when a commit becomes empty as a result of the
|
||||
fixup. This can happen in two situations:
|
||||
|
|
|
|||
|
|
@ -30,15 +30,15 @@
|
|||
#include "wt-status.h"
|
||||
|
||||
#define GIT_HISTORY_DROP_USAGE \
|
||||
N_("git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)]")
|
||||
N_("git history drop <commit> [--dry-run] [--update-refs=(branches|head)] [--empty=(drop|keep|abort)] [--[no-]gpg-sign[=<key-id>]]")
|
||||
#define GIT_HISTORY_FIXUP_USAGE \
|
||||
N_("git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]")
|
||||
N_("git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)] [--[no-]gpg-sign[=<key-id>]]")
|
||||
#define GIT_HISTORY_REWORD_USAGE \
|
||||
N_("git history reword <commit> [--dry-run] [--update-refs=(branches|head)]")
|
||||
N_("git history reword <commit> [--dry-run] [--update-refs=(branches|head)] [--[no-]gpg-sign[=<key-id>]]")
|
||||
#define GIT_HISTORY_SPLIT_USAGE \
|
||||
N_("git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]")
|
||||
N_("git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--[no-]gpg-sign[=<key-id>]] [--] [<pathspec>...]")
|
||||
#define GIT_HISTORY_SQUASH_USAGE \
|
||||
N_("git history squash [--dry-run] [--update-refs=(branches|head)] [--[no-]edit] <revision-range>")
|
||||
N_("git history squash [--dry-run] [--update-refs=(branches|head)] [--[no-]edit] [--[no-]gpg-sign[=<key-id>]] <revision-range>")
|
||||
|
||||
static void change_data_free(void *util, const char *str UNUSED)
|
||||
{
|
||||
|
|
@ -110,6 +110,30 @@ enum commit_tree_flags {
|
|||
COMMIT_TREE_EDIT_MESSAGE = (1 << 0),
|
||||
};
|
||||
|
||||
static int history_config(const char *var, const char *value,
|
||||
const struct config_context *ctx, void *data)
|
||||
{
|
||||
const char **sign_commit = data;
|
||||
|
||||
if (!strcmp(var, "commit.gpgsign")) {
|
||||
*sign_commit = git_config_bool(var, value) ? "" : NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_default_config(var, value, ctx, NULL);
|
||||
}
|
||||
|
||||
#define OPT_HISTORY_GPG_SIGN(v) { \
|
||||
.type = OPTION_STRING, \
|
||||
.short_name = 'S', \
|
||||
.long_name = "gpg-sign", \
|
||||
.value = (v), \
|
||||
.argh = N_("key-id"), \
|
||||
.help = N_("GPG-sign rewritten commits"), \
|
||||
.flags = PARSE_OPT_OPTARG, \
|
||||
.defval = (intptr_t)"", \
|
||||
}
|
||||
|
||||
static int commit_tree_ext(struct repository *repo,
|
||||
const char *action,
|
||||
struct commit *commit_with_message,
|
||||
|
|
@ -117,6 +141,7 @@ static int commit_tree_ext(struct repository *repo,
|
|||
const struct commit_list *parents,
|
||||
const struct object_id *old_tree,
|
||||
const struct object_id *new_tree,
|
||||
const char *sign_commit,
|
||||
struct commit **out,
|
||||
enum commit_tree_flags flags)
|
||||
{
|
||||
|
|
@ -160,7 +185,7 @@ static int commit_tree_ext(struct repository *repo,
|
|||
|
||||
ret = commit_tree_extended(commit_message.buf, commit_message.len, new_tree,
|
||||
parents, &rewritten_commit_oid, original_author,
|
||||
NULL, NULL, original_extra_headers);
|
||||
NULL, sign_commit, original_extra_headers);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
|
|
@ -196,6 +221,7 @@ static int first_parent_tree_oid(struct repository *repo,
|
|||
static int commit_tree_with_edited_message(struct repository *repo,
|
||||
const char *action,
|
||||
struct commit *original,
|
||||
const char *sign_commit,
|
||||
struct commit **out)
|
||||
{
|
||||
struct object_id parent_tree_oid;
|
||||
|
|
@ -207,7 +233,8 @@ static int commit_tree_with_edited_message(struct repository *repo,
|
|||
return -1;
|
||||
|
||||
return commit_tree_ext(repo, action, original, NULL, original->parents,
|
||||
&parent_tree_oid, tree_oid, out, COMMIT_TREE_EDIT_MESSAGE);
|
||||
&parent_tree_oid, tree_oid, sign_commit, out,
|
||||
COMMIT_TREE_EDIT_MESSAGE);
|
||||
}
|
||||
|
||||
enum ref_action {
|
||||
|
|
@ -363,12 +390,14 @@ static int compute_pending_ref_updates(struct rev_info *revs,
|
|||
enum ref_action action,
|
||||
struct commit *original,
|
||||
struct commit *rewritten,
|
||||
const char *sign_commit,
|
||||
enum replay_empty_commit_action empty,
|
||||
struct replay_result *result)
|
||||
{
|
||||
const struct name_decoration *decoration;
|
||||
struct replay_revisions_options opts = {
|
||||
.empty = empty,
|
||||
.sign_commit = sign_commit,
|
||||
};
|
||||
char hex[GIT_MAX_HEXSZ + 1];
|
||||
bool detached_head;
|
||||
|
|
@ -473,13 +502,14 @@ static int handle_reference_updates(struct rev_info *revs,
|
|||
struct commit *rewritten,
|
||||
const char *reflog_msg,
|
||||
int dry_run,
|
||||
const char *sign_commit,
|
||||
enum replay_empty_commit_action empty)
|
||||
{
|
||||
struct replay_result result = { 0 };
|
||||
int ret;
|
||||
|
||||
ret = compute_pending_ref_updates(revs, action, original, rewritten,
|
||||
empty, &result);
|
||||
sign_commit, empty, &result);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
@ -533,6 +563,7 @@ static int cmd_history_fixup(int argc,
|
|||
enum replay_empty_commit_action empty = REPLAY_EMPTY_COMMIT_DROP;
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
enum commit_tree_flags flags = 0;
|
||||
const char *sign_commit = NULL;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
|
|
@ -546,6 +577,7 @@ static int cmd_history_fixup(int argc,
|
|||
OPT_CALLBACK_F(0, "empty", &empty, "(drop|keep|abort)",
|
||||
N_("how to handle commits that become empty"),
|
||||
PARSE_OPT_NONEG, parse_opt_empty),
|
||||
OPT_HISTORY_GPG_SIGN(&sign_commit),
|
||||
OPT_END(),
|
||||
};
|
||||
struct merge_result merge_result = { 0 };
|
||||
|
|
@ -557,12 +589,12 @@ static int cmd_history_fixup(int argc,
|
|||
bool skip_commit = false;
|
||||
int ret;
|
||||
|
||||
repo_config(repo, history_config, &sign_commit);
|
||||
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);
|
||||
|
||||
if (action == REF_ACTION_DEFAULT)
|
||||
action = REF_ACTION_BRANCHES;
|
||||
|
|
@ -687,7 +719,7 @@ static int cmd_history_fixup(int argc,
|
|||
if (!skip_commit) {
|
||||
ret = commit_tree_ext(repo, "fixup", original, NULL, original->parents,
|
||||
&original_tree->object.oid, &merge_result.tree->object.oid,
|
||||
&rewritten, flags);
|
||||
sign_commit, &rewritten, flags);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing fixed-up commit"));
|
||||
goto out;
|
||||
|
|
@ -697,7 +729,7 @@ static int cmd_history_fixup(int argc,
|
|||
strbuf_addf(&reflog_msg, "fixup: updating %s", argv[0]);
|
||||
|
||||
ret = handle_reference_updates(&revs, action, original, rewritten,
|
||||
reflog_msg.buf, dry_run, empty);
|
||||
reflog_msg.buf, dry_run, sign_commit, empty);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed replaying descendants"));
|
||||
goto out;
|
||||
|
|
@ -722,6 +754,7 @@ static int cmd_history_reword(int argc,
|
|||
NULL,
|
||||
};
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
const char *sign_commit = NULL;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
|
|
@ -729,6 +762,7 @@ static int cmd_history_reword(int argc,
|
|||
PARSE_OPT_NONEG, parse_ref_action),
|
||||
OPT_BOOL('n', "dry-run", &dry_run,
|
||||
N_("perform a dry-run without updating any refs")),
|
||||
OPT_HISTORY_GPG_SIGN(&sign_commit),
|
||||
OPT_END(),
|
||||
};
|
||||
struct strbuf reflog_msg = STRBUF_INIT;
|
||||
|
|
@ -736,12 +770,12 @@ static int cmd_history_reword(int argc,
|
|||
struct rev_info revs = { 0 };
|
||||
int ret;
|
||||
|
||||
repo_config(repo, history_config, &sign_commit);
|
||||
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);
|
||||
|
||||
if (action == REF_ACTION_DEFAULT)
|
||||
action = REF_ACTION_BRANCHES;
|
||||
|
|
@ -756,7 +790,8 @@ static int cmd_history_reword(int argc,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = commit_tree_with_edited_message(repo, "reworded", original, &rewritten);
|
||||
ret = commit_tree_with_edited_message(repo, "reworded", original,
|
||||
sign_commit, &rewritten);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing reworded commit"));
|
||||
goto out;
|
||||
|
|
@ -765,7 +800,8 @@ static int cmd_history_reword(int argc,
|
|||
strbuf_addf(&reflog_msg, "reword: updating %s", argv[0]);
|
||||
|
||||
ret = handle_reference_updates(&revs, action, original, rewritten,
|
||||
reflog_msg.buf, dry_run, REPLAY_EMPTY_COMMIT_ABORT);
|
||||
reflog_msg.buf, dry_run, sign_commit,
|
||||
REPLAY_EMPTY_COMMIT_ABORT);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed replaying descendants"));
|
||||
goto out;
|
||||
|
|
@ -831,6 +867,7 @@ out:
|
|||
static int split_commit(struct repository *repo,
|
||||
struct commit *original,
|
||||
struct pathspec *pathspec,
|
||||
const char *sign_commit,
|
||||
struct commit **out)
|
||||
{
|
||||
struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT;
|
||||
|
|
@ -900,8 +937,10 @@ static int split_commit(struct repository *repo,
|
|||
* The first commit is constructed from the split-out tree. The base
|
||||
* that shall be diffed against is the parent of the original commit.
|
||||
*/
|
||||
ret = commit_tree_ext(repo, "split-out", original, NULL, original->parents, &parent_tree_oid,
|
||||
&split_tree->object.oid, &first_commit, COMMIT_TREE_EDIT_MESSAGE);
|
||||
ret = commit_tree_ext(repo, "split-out", original, NULL, original->parents,
|
||||
&parent_tree_oid, &split_tree->object.oid, sign_commit,
|
||||
&first_commit,
|
||||
COMMIT_TREE_EDIT_MESSAGE);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing first commit"));
|
||||
goto out;
|
||||
|
|
@ -918,7 +957,8 @@ static int split_commit(struct repository *repo,
|
|||
new_tree_oid = &repo_get_commit_tree(repo, original)->object.oid;
|
||||
|
||||
ret = commit_tree_ext(repo, "split-out", original, NULL, parents, old_tree_oid,
|
||||
new_tree_oid, &second_commit, COMMIT_TREE_EDIT_MESSAGE);
|
||||
new_tree_oid, sign_commit, &second_commit,
|
||||
COMMIT_TREE_EDIT_MESSAGE);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing second commit"));
|
||||
goto out;
|
||||
|
|
@ -946,6 +986,7 @@ static int cmd_history_split(int argc,
|
|||
NULL,
|
||||
};
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
const char *sign_commit = NULL;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
|
|
@ -953,6 +994,7 @@ static int cmd_history_split(int argc,
|
|||
PARSE_OPT_NONEG, parse_ref_action),
|
||||
OPT_BOOL('n', "dry-run", &dry_run,
|
||||
N_("perform a dry-run without updating any refs")),
|
||||
OPT_HISTORY_GPG_SIGN(&sign_commit),
|
||||
OPT_END(),
|
||||
};
|
||||
struct commit *original, *rewritten = NULL;
|
||||
|
|
@ -961,12 +1003,12 @@ static int cmd_history_split(int argc,
|
|||
struct rev_info revs = { 0 };
|
||||
int ret;
|
||||
|
||||
repo_config(repo, history_config, &sign_commit);
|
||||
argc = parse_options(argc, argv, prefix, options, usage, 0);
|
||||
if (argc < 1) {
|
||||
ret = error(_("command expects a committish"));
|
||||
goto out;
|
||||
}
|
||||
repo_config(repo, git_default_config, NULL);
|
||||
|
||||
if (action == REF_ACTION_DEFAULT)
|
||||
action = REF_ACTION_BRANCHES;
|
||||
|
|
@ -992,14 +1034,15 @@ static int cmd_history_split(int argc,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = split_commit(repo, original, &pathspec, &rewritten);
|
||||
ret = split_commit(repo, original, &pathspec, sign_commit, &rewritten);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
strbuf_addf(&reflog_msg, "split: updating %s", argv[0]);
|
||||
|
||||
ret = handle_reference_updates(&revs, action, original, rewritten,
|
||||
reflog_msg.buf, dry_run, REPLAY_EMPTY_COMMIT_ABORT);
|
||||
reflog_msg.buf, dry_run, sign_commit,
|
||||
REPLAY_EMPTY_COMMIT_ABORT);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed replaying descendants"));
|
||||
goto out;
|
||||
|
|
@ -1579,6 +1622,7 @@ static int cmd_history_squash(int argc,
|
|||
NULL,
|
||||
};
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
const char *sign_commit = NULL;
|
||||
int dry_run = 0;
|
||||
int edit = 1;
|
||||
struct option options[] = {
|
||||
|
|
@ -1589,6 +1633,7 @@ static int cmd_history_squash(int argc,
|
|||
N_("perform a dry-run without updating any refs")),
|
||||
OPT_BOOL('e', "edit", &edit,
|
||||
N_("edit the commit message")),
|
||||
OPT_HISTORY_GPG_SIGN(&sign_commit),
|
||||
OPT_END(),
|
||||
};
|
||||
struct strbuf reflog_msg = STRBUF_INIT;
|
||||
|
|
@ -1598,13 +1643,13 @@ static int cmd_history_squash(int argc,
|
|||
struct rev_info revs = { 0 };
|
||||
int ret;
|
||||
|
||||
repo_config(repo, history_config, &sign_commit);
|
||||
argc = parse_options(argc, argv, prefix, options, usage,
|
||||
PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_KEEP_ARGV0);
|
||||
if (argc < 2) {
|
||||
ret = error(_("command expects a revision range"));
|
||||
goto out;
|
||||
}
|
||||
repo_config(repo, git_default_config, NULL);
|
||||
|
||||
if (action == REF_ACTION_DEFAULT)
|
||||
action = REF_ACTION_BRANCHES;
|
||||
|
|
@ -1629,7 +1674,7 @@ static int cmd_history_squash(int argc,
|
|||
|
||||
ret = commit_tree_ext(repo, "squash", oldest, message_template,
|
||||
oldest->parents, base_tree_oid, tip_tree_oid,
|
||||
&rewritten,
|
||||
sign_commit, &rewritten,
|
||||
edit ? COMMIT_TREE_EDIT_MESSAGE : 0);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing squashed commit"));
|
||||
|
|
@ -1638,6 +1683,7 @@ static int cmd_history_squash(int argc,
|
|||
|
||||
ret = handle_reference_updates(&revs, action, tip, rewritten,
|
||||
reflog_msg.buf, dry_run,
|
||||
sign_commit,
|
||||
REPLAY_EMPTY_COMMIT_ABORT);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed replaying descendants"));
|
||||
|
|
@ -1728,6 +1774,7 @@ static int cmd_history_drop(int argc,
|
|||
};
|
||||
enum replay_empty_commit_action empty = REPLAY_EMPTY_COMMIT_DROP;
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
const char *sign_commit = NULL;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
|
|
@ -1738,6 +1785,7 @@ static int cmd_history_drop(int argc,
|
|||
OPT_CALLBACK_F(0, "empty", &empty, "(drop|keep|abort)",
|
||||
N_("how to handle descendants that become empty"),
|
||||
PARSE_OPT_NONEG, parse_opt_empty),
|
||||
OPT_HISTORY_GPG_SIGN(&sign_commit),
|
||||
OPT_END(),
|
||||
};
|
||||
struct strbuf reflog_msg = STRBUF_INIT;
|
||||
|
|
@ -1748,12 +1796,12 @@ static int cmd_history_drop(int argc,
|
|||
bool head_moves = false;
|
||||
int ret;
|
||||
|
||||
repo_config(repo, history_config, &sign_commit);
|
||||
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);
|
||||
|
||||
if (action == REF_ACTION_DEFAULT)
|
||||
action = REF_ACTION_BRANCHES;
|
||||
|
|
@ -1781,7 +1829,7 @@ static int cmd_history_drop(int argc,
|
|||
rewritten = original->parents->item;
|
||||
|
||||
ret = compute_pending_ref_updates(&revs, action, original, rewritten,
|
||||
empty, &result);
|
||||
sign_commit, empty, &result);
|
||||
if (ret) {
|
||||
ret = error(_("failed replaying descendants"));
|
||||
goto out;
|
||||
|
|
|
|||
13
replay.c
13
replay.c
|
|
@ -85,13 +85,13 @@ static struct commit *create_commit(struct repository *repo,
|
|||
struct tree *tree,
|
||||
struct commit *based_on,
|
||||
struct commit *parent,
|
||||
enum replay_mode mode)
|
||||
enum replay_mode mode,
|
||||
const char *sign_commit)
|
||||
{
|
||||
struct object_id ret;
|
||||
struct object *obj = NULL;
|
||||
struct commit_list *parents = NULL;
|
||||
char *author = NULL;
|
||||
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();
|
||||
|
|
@ -288,7 +288,8 @@ static struct commit *pick_regular_commit(struct repository *repo,
|
|||
struct merge_options *merge_opt,
|
||||
struct merge_result *result,
|
||||
enum replay_mode mode,
|
||||
enum replay_empty_commit_action empty)
|
||||
enum replay_empty_commit_action empty,
|
||||
const char *sign_commit)
|
||||
{
|
||||
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
|
||||
|
||||
|
|
@ -361,7 +362,8 @@ static struct commit *pick_regular_commit(struct repository *repo,
|
|||
}
|
||||
}
|
||||
|
||||
return create_commit(repo, result->tree, pickme, replayed_base, mode);
|
||||
return create_commit(repo, result->tree, pickme, replayed_base, mode,
|
||||
sign_commit);
|
||||
}
|
||||
|
||||
void replay_result_release(struct replay_result *result)
|
||||
|
|
@ -481,7 +483,8 @@ int replay_revisions(struct rev_info *revs,
|
|||
|
||||
last_commit = pick_regular_commit(revs->repo, commit, base,
|
||||
&merge_opt, &result,
|
||||
mode, opts->empty);
|
||||
mode, opts->empty,
|
||||
opts->sign_commit);
|
||||
}
|
||||
|
||||
if (!last_commit)
|
||||
|
|
|
|||
6
replay.h
6
replay.h
|
|
@ -57,6 +57,12 @@ struct replay_revisions_options {
|
|||
*/
|
||||
int contained;
|
||||
|
||||
/*
|
||||
* Key used to sign newly-created commits. An empty string requests the
|
||||
* default configured signing key, and NULL disables signing.
|
||||
*/
|
||||
const char *sign_commit;
|
||||
|
||||
/*
|
||||
* Controls what to do when a replayed commit becomes empty.
|
||||
* Defaults to REPLAY_EMPTY_COMMIT_DROP.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ test_description='tests for git-history reword subcommand'
|
|||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-log-graph.sh"
|
||||
. "$TEST_DIRECTORY/lib-gpg.sh"
|
||||
|
||||
reword_with_message () {
|
||||
cat >message &&
|
||||
|
|
@ -26,6 +27,37 @@ expect_log () {
|
|||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_reword_gpg_sign () {
|
||||
must_fail= will=will
|
||||
if test "x$1" = "x!"
|
||||
then
|
||||
must_fail=test_must_fail
|
||||
will="will not"
|
||||
shift
|
||||
fi
|
||||
conf=$1
|
||||
shift
|
||||
|
||||
test_expect_success GPG "reword $* with commit.gpgsign=$conf $will sign rewritten history" "
|
||||
test_when_finished 'rm -rf repo' &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
|
||||
git config commit.gpgsign $conf &&
|
||||
reword_with_message $* HEAD~ <<-EOF &&
|
||||
second reworded
|
||||
EOF
|
||||
|
||||
$must_fail git verify-commit HEAD~ &&
|
||||
$must_fail git verify-commit HEAD
|
||||
)
|
||||
"
|
||||
}
|
||||
|
||||
test_expect_success 'can reword tip of a branch' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
@ -77,6 +109,37 @@ test_expect_success 'can reword commit in the middle' '
|
|||
)
|
||||
'
|
||||
|
||||
test_reword_gpg_sign ! false
|
||||
test_reword_gpg_sign true
|
||||
test_reword_gpg_sign false --gpg-sign
|
||||
test_reword_gpg_sign ! true --no-gpg-sign
|
||||
test_reword_gpg_sign ! true --gpg-sign --no-gpg-sign
|
||||
test_reword_gpg_sign false --no-gpg-sign --gpg-sign
|
||||
|
||||
test_expect_success GPG 'reword uses an explicit signing key for rewritten history' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
|
||||
reword_with_message -SB7227189 HEAD~ <<-EOF &&
|
||||
second reworded
|
||||
EOF
|
||||
|
||||
git verify-commit HEAD~ &&
|
||||
git verify-commit HEAD &&
|
||||
git log -2 --format=%GK >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
65A0EEA02E30CAD7
|
||||
65A0EEA02E30CAD7
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'can reword commit in the middle even on detached head' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ test_description='tests for git-history split subcommand'
|
|||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-log-graph.sh"
|
||||
. "$TEST_DIRECTORY/lib-gpg.sh"
|
||||
|
||||
# The fake editor takes multiple arguments, each of which represents a commit
|
||||
# message. Subsequent invocations of the editor will then yield those messages
|
||||
|
|
@ -36,6 +37,42 @@ expect_tree_entries () {
|
|||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_split_gpg_sign () {
|
||||
must_fail= will=will
|
||||
if test "x$1" = "x!"
|
||||
then
|
||||
must_fail=test_must_fail
|
||||
will="will not"
|
||||
shift
|
||||
fi
|
||||
conf=$1
|
||||
shift
|
||||
|
||||
test_expect_success GPG "split $* with commit.gpgsign=$conf $will sign rewritten history" "
|
||||
test_when_finished 'rm -rf repo' &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit initial &&
|
||||
touch bar foo &&
|
||||
git add . &&
|
||||
git commit -m split-me &&
|
||||
test_commit tip &&
|
||||
|
||||
git config commit.gpgsign $conf &&
|
||||
set_fake_editor 'first' 'second' &&
|
||||
git history split $* HEAD~ <<-EOF &&
|
||||
y
|
||||
n
|
||||
EOF
|
||||
|
||||
$must_fail git verify-commit HEAD~2 &&
|
||||
$must_fail git verify-commit HEAD~ &&
|
||||
$must_fail git verify-commit HEAD
|
||||
)
|
||||
"
|
||||
}
|
||||
|
||||
test_expect_success 'refuses to work with merge commits' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
@ -141,6 +178,13 @@ test_expect_success 'can split up tip commit' '
|
|||
)
|
||||
'
|
||||
|
||||
test_split_gpg_sign ! false
|
||||
test_split_gpg_sign true
|
||||
test_split_gpg_sign false --gpg-sign
|
||||
test_split_gpg_sign ! true --no-gpg-sign
|
||||
test_split_gpg_sign ! true --gpg-sign --no-gpg-sign
|
||||
test_split_gpg_sign false --no-gpg-sign --gpg-sign
|
||||
|
||||
test_expect_success 'can split up root commit' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
test_description='tests for git-history fixup subcommand'
|
||||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-gpg.sh"
|
||||
|
||||
fixup_with_message () {
|
||||
cat >message &&
|
||||
|
|
@ -21,6 +22,37 @@ expect_changes () {
|
|||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_fixup_gpg_sign () {
|
||||
must_fail= will=will
|
||||
if test "x$1" = "x!"
|
||||
then
|
||||
must_fail=test_must_fail
|
||||
will="will not"
|
||||
shift
|
||||
fi
|
||||
conf=$1
|
||||
shift
|
||||
|
||||
test_expect_success GPG "fixup $* with commit.gpgsign=$conf $will sign rewritten history" "
|
||||
test_when_finished 'rm -rf repo' &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
|
||||
git config commit.gpgsign $conf &&
|
||||
echo fix >>second.t &&
|
||||
git add second.t &&
|
||||
git history fixup $* HEAD~ &&
|
||||
|
||||
$must_fail git verify-commit HEAD~ &&
|
||||
$must_fail git verify-commit HEAD
|
||||
)
|
||||
"
|
||||
}
|
||||
|
||||
test_expect_success 'errors on missing commit argument' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
@ -229,6 +261,13 @@ test_expect_success 'preserves commit message and authorship' '
|
|||
)
|
||||
'
|
||||
|
||||
test_fixup_gpg_sign ! false
|
||||
test_fixup_gpg_sign true
|
||||
test_fixup_gpg_sign false --gpg-sign
|
||||
test_fixup_gpg_sign ! true --no-gpg-sign
|
||||
test_fixup_gpg_sign ! true --gpg-sign --no-gpg-sign
|
||||
test_fixup_gpg_sign false --no-gpg-sign --gpg-sign
|
||||
|
||||
test_expect_success 'updates all descendant branches by default' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo --initial-branch=main &&
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ test_description='tests for git-history drop subcommand'
|
|||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-log-graph.sh"
|
||||
. "$TEST_DIRECTORY/lib-gpg.sh"
|
||||
|
||||
expect_graph () {
|
||||
cat >expect &&
|
||||
|
|
@ -16,6 +17,34 @@ expect_log () {
|
|||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_drop_gpg_sign () {
|
||||
must_fail= will=will
|
||||
if test "x$1" = "x!"
|
||||
then
|
||||
must_fail=test_must_fail
|
||||
will="will not"
|
||||
shift
|
||||
fi
|
||||
conf=$1
|
||||
shift
|
||||
|
||||
test_expect_success GPG "drop $* with commit.gpgsign=$conf $will sign replayed descendants" "
|
||||
test_when_finished 'rm -rf repo' &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
|
||||
git config commit.gpgsign $conf &&
|
||||
git history drop $* HEAD~ &&
|
||||
|
||||
$must_fail git verify-commit HEAD
|
||||
)
|
||||
"
|
||||
}
|
||||
|
||||
test_expect_success 'errors on missing commit argument' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
@ -88,6 +117,27 @@ test_expect_success 'drops a commit in the middle and replays descendants' '
|
|||
)
|
||||
'
|
||||
|
||||
test_drop_gpg_sign ! false
|
||||
test_drop_gpg_sign true
|
||||
test_drop_gpg_sign false --gpg-sign
|
||||
test_drop_gpg_sign ! true --no-gpg-sign
|
||||
test_drop_gpg_sign ! true --gpg-sign --no-gpg-sign
|
||||
test_drop_gpg_sign false --no-gpg-sign --gpg-sign
|
||||
|
||||
test_expect_success GPG 'drop has no commit to sign when dropping the tip' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
|
||||
git history drop --gpg-sign HEAD &&
|
||||
|
||||
test_must_fail git verify-commit HEAD
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'drops the HEAD commit' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
test_description='tests for git-history squash subcommand'
|
||||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY/lib-gpg.sh"
|
||||
|
||||
stage_file () {
|
||||
printf "%s\n" "$1" >file &&
|
||||
|
|
@ -40,6 +41,66 @@ check_commit_author () {
|
|||
test_cmp expect actual
|
||||
}
|
||||
|
||||
test_squash_gpg_sign () {
|
||||
must_fail= will=will
|
||||
if test "x$1" = "x!"
|
||||
then
|
||||
must_fail=test_must_fail
|
||||
will="will not"
|
||||
shift
|
||||
fi
|
||||
conf=$1
|
||||
shift
|
||||
|
||||
test_expect_success GPG "squash $* with commit.gpgsign=$conf $will sign rewritten history" "
|
||||
test_when_finished 'rm -rf repo' &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
test_commit fourth &&
|
||||
|
||||
git config commit.gpgsign $conf &&
|
||||
git history squash --no-edit $* HEAD~3..HEAD~1 &&
|
||||
|
||||
$must_fail git verify-commit HEAD~ &&
|
||||
$must_fail git verify-commit HEAD
|
||||
)
|
||||
"
|
||||
}
|
||||
|
||||
test_squash_gpg_sign ! false
|
||||
test_squash_gpg_sign true
|
||||
test_squash_gpg_sign false --gpg-sign
|
||||
test_squash_gpg_sign ! true --no-gpg-sign
|
||||
test_squash_gpg_sign ! true --gpg-sign --no-gpg-sign
|
||||
test_squash_gpg_sign false --no-gpg-sign --gpg-sign
|
||||
|
||||
test_expect_success GPG 'squash uses an explicit signing key for rewritten history' '
|
||||
test_when_finished "rm -rf repo" &&
|
||||
git init repo &&
|
||||
(
|
||||
cd repo &&
|
||||
test_commit first &&
|
||||
test_commit second &&
|
||||
test_commit third &&
|
||||
test_commit fourth &&
|
||||
|
||||
git history squash --no-edit -SB7227189 HEAD~3..HEAD~1 &&
|
||||
|
||||
git verify-commit HEAD~ &&
|
||||
git verify-commit HEAD &&
|
||||
git log -2 --format=%GK >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
65A0EEA02E30CAD7
|
||||
65A0EEA02E30CAD7
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'setup linear history touching two files' '
|
||||
test_commit base file a start &&
|
||||
GIT_AUTHOR_NAME=One GIT_AUTHOR_EMAIL=one@example.com \
|
||||
|
|
|
|||
|
|
@ -3236,6 +3236,8 @@ test_expect_success 'git history subcommand options' '
|
|||
test_completion "git history split main --" <<-\EOF &&
|
||||
--update-refs=Z
|
||||
--dry-run Z
|
||||
--gpg-sign Z
|
||||
--no-... Z
|
||||
--no-dry-run Z
|
||||
EOF
|
||||
test_completion "git history fixup --upd" "--update-refs=" &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue