From: Li Chen <chenl311@xxxxxxxxxxxxxxx> Introduce a new `--reviewby` flag to `git rebase` and `git am` that appends a `Reviewed-by: name <email>` trailer to each rebased or applied commit, analogous to the existing `--signoff` behavior. especially useful for backporting patches and reviewing new patches. Signed-off-by: Li Chen <chenl311@xxxxxxxxxxxxxxx> Reviewed-by: Li Chen <chenl311@xxxxxxxxxxxxxxx> --- Documentation/git-am.adoc | 6 +- Documentation/git-rebase.adoc | 4 + builtin/am.c | 31 ++++++++ builtin/rebase.c | 14 ++++ builtin/revert.c | 4 +- sequencer.c | 87 ++++++++++++++++++++- sequencer.h | 10 +++ t/meson.build | 1 + t/t3439-rebase-reviewby.sh | 138 ++++++++++++++++++++++++++++++++++ t/t4150-am.sh | 75 ++++++++++++++++++ 10 files changed, 366 insertions(+), 4 deletions(-) create mode 100755 t/t3439-rebase-reviewby.sh diff --git a/Documentation/git-am.adoc b/Documentation/git-am.adoc index 221070de48..1d408f2bab 100644 --- a/Documentation/git-am.adoc +++ b/Documentation/git-am.adoc @@ -9,7 +9,7 @@ git-am - Apply a series of patches from a mailbox SYNOPSIS -------- [verse] -'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--no-verify] +'git am' [--signoff] [--reviewby] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--no-verify] [--[no-]3way] [--interactive] [--committer-date-is-author-date] [--ignore-date] [--ignore-space-change | --ignore-whitespace] [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>] @@ -40,7 +40,9 @@ OPTIONS Add a `Signed-off-by` trailer to the commit message, using the committer identity of yourself. See the signoff option in linkgit:git-commit[1] for more information. - +--reviewby:: + Add a `Reviewed-by` trailer to the commit message, using + the committer identity of yourself. -k:: --keep:: Pass `-k` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]). diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc index 956d3048f5..1f3d152035 100644 --- a/Documentation/git-rebase.adoc +++ b/Documentation/git-rebase.adoc @@ -521,6 +521,10 @@ See also INCOMPATIBLE OPTIONS below. that if `--interactive` is given then only commits marked to be picked, edited or reworded will have the trailer added. + +--review-by:: + Append a `Reviewed-by:` trailer whose value is taken from the + current committer identity, exactly like `--signoff` appends a + Signed-off-by:` trailer. See also INCOMPATIBLE OPTIONS below. -i:: diff --git a/builtin/am.c b/builtin/am.c index 4afb519830..f8fe95e15d 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -89,6 +89,12 @@ enum signoff_type { SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ }; +enum reviewby_type { + REVIEWBY_FALSE = 0, + REVIEWBY_TRUE = 1, + REVIEWBY_EXPLICIT /* --reviewby was set on the command-line */ +}; + enum resume_type { RESUME_FALSE = 0, RESUME_APPLY, @@ -134,6 +140,7 @@ struct am_state { int threeway; int quiet; int signoff; /* enum signoff_type */ + int reviewby; /* enum reviewby_type */ int utf8; int keep; /* enum keep_type */ int message_id; @@ -422,6 +429,9 @@ static void am_load(struct am_state *state) read_state_file(&sb, state, "sign", 1); state->signoff = !strcmp(sb.buf, "t"); + read_state_file(&sb, state, "review", 1); + state->reviewby = !strcmp(sb.buf, "t"); + read_state_file(&sb, state, "utf8", 1); state->utf8 = !strcmp(sb.buf, "t"); @@ -1017,6 +1027,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, write_state_bool(state, "threeway", state->threeway); write_state_bool(state, "quiet", state->quiet); write_state_bool(state, "sign", state->signoff); + write_state_bool(state, "review", state->reviewby); write_state_bool(state, "utf8", state->utf8); if (state->allow_rerere_autoupdate) @@ -1191,6 +1202,18 @@ static void am_append_signoff(struct am_state *state) state->msg = strbuf_detach(&sb, &state->msg_len); } +/** + * Appends reviewby to the "msg" field of the am_state. + */ +static void am_append_reviewby(struct am_state *state) +{ + struct strbuf sb = STRBUF_INIT; + + strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len); + append_reviewby(&sb, 0, 0); + state->msg = strbuf_detach(&sb, &state->msg_len); +} + /** * Parses `mail` using git-mailinfo, extracting its patch and authorship info. * state->msg will be set to the patch message. state->author_name, @@ -1849,6 +1872,9 @@ static void am_run(struct am_state *state, int resume) if (state->signoff) am_append_signoff(state); + if (state->reviewby) + am_append_reviewby(state); + write_author_script(state); write_commit_msg(state); } @@ -2337,6 +2363,9 @@ int cmd_am(int argc, OPT_SET_INT('s', "signoff", &state.signoff, N_("add a Signed-off-by trailer to the commit message"), SIGNOFF_EXPLICIT), + OPT_SET_INT(0, "reviewby", &state.reviewby, + N_("add a Reviewed-by trailer to the commit message"), + REVIEWBY_EXPLICIT), OPT_BOOL('u', "utf8", &state.utf8, N_("recode into utf8 (default)")), OPT_SET_INT('k', "keep", &state.keep, @@ -2483,6 +2512,8 @@ int cmd_am(int argc, if (state.signoff == SIGNOFF_EXPLICIT) am_append_signoff(&state); + if (state.reviewby == REVIEWBY_EXPLICIT) + am_append_reviewby(&state); } else { struct strvec paths = STRVEC_INIT; int i; diff --git a/builtin/rebase.c b/builtin/rebase.c index 2e8c4ee678..b288aedfb1 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -113,6 +113,7 @@ struct rebase_options { enum action action; char *reflog_action; int signoff; + int reviewby; int allow_rerere_autoupdate; int keep_empty; int autosquash; @@ -177,6 +178,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) sequencer_init_config(&replay); replay.signoff = opts->signoff; + replay.reviewby = opts->reviewby; replay.allow_ff = !(opts->flags & REBASE_FORCE); if (opts->allow_rerere_autoupdate) replay.allow_rerere_auto = opts->allow_rerere_autoupdate; @@ -477,6 +479,10 @@ static int read_basic_state(struct rebase_options *opts) opts->signoff = 1; opts->flags |= REBASE_FORCE; } + if (file_exists(state_dir_path("reviewby", opts))) { + opts->reviewby = 1; + opts->flags |= REBASE_FORCE; + } if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) { strbuf_reset(&buf); @@ -528,6 +534,8 @@ static int rebase_write_basic_state(struct rebase_options *opts) opts->gpg_sign_opt); if (opts->signoff) write_file(state_dir_path("signoff", opts), "--signoff"); + if (opts->reviewby) + write_file(state_dir_path("reviewby", opts), "--reviewby"); return 0; } @@ -1134,6 +1142,8 @@ int cmd_rebase(int argc, }, OPT_BOOL(0, "signoff", &options.signoff, N_("add a Signed-off-by trailer to each commit")), + OPT_BOOL(0, "reviewby", &options.reviewby, + N_("add a Reviewed-by trailer to each commit")), OPT_BOOL(0, "committer-date-is-author-date", &options.committer_date_is_author_date, N_("make committer date match author date")), @@ -1618,6 +1628,10 @@ int cmd_rebase(int argc, strvec_push(&options.git_am_opts, "--signoff"); options.flags |= REBASE_FORCE; } + if (options.reviewby) { + strvec_push(&options.git_am_opts, "--reviewby"); + options.flags |= REBASE_FORCE; + } if (!options.root) { if (argc < 1) { diff --git a/builtin/revert.c b/builtin/revert.c index e07c2217fe..c73a773c9d 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -124,7 +124,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, OPT_CLEANUP(&cleanup_arg), OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")), OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")), - OPT_NOOP_NOARG('r', NULL), + OPT_BOOL('r', "reviewby", &opts->reviewby, N_("add a Reviewed-by trailer")), OPT_BOOL('s', "signoff", &opts->signoff, N_("add a Signed-off-by trailer")), OPT_CALLBACK('m', "mainline", opts, N_("parent-number"), N_("select mainline parent"), option_parse_m), @@ -206,6 +206,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, verify_opt_compatible(me, this_operation, "--no-commit", opts->no_commit, "--signoff", opts->signoff, + "--reviewby", opts->reviewby, "--mainline", opts->mainline, "--strategy", opts->strategy ? 1 : 0, "--strategy-option", opts->xopts.nr ? 1 : 0, @@ -226,6 +227,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, if (opts->allow_ff) verify_opt_compatible(me, "--ff", "--signoff", opts->signoff, + "--reviewby", opts->reviewby, "--no-commit", opts->no_commit, "-x", opts->record_origin, "--edit", opts->edit > 0, diff --git a/sequencer.c b/sequencer.c index b5c4043757..01933882ed 100644 --- a/sequencer.c +++ b/sequencer.c @@ -61,6 +61,7 @@ #define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16) static const char sign_off_header[] = "Signed-off-by: "; +static const char review_by_header[] = "Reviewed-by: "; static const char cherry_picked_prefix[] = "(cherry picked from commit "; GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG") @@ -199,6 +200,7 @@ static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head") static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose") static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet") static GIT_PATH_FUNC(rebase_path_signoff, "rebase-merge/signoff") +static GIT_PATH_FUNC(rebase_path_reviewby, "rebase-merge/reviewby") static GIT_PATH_FUNC(rebase_path_head_name, "rebase-merge/head-name") static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto") static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash") @@ -1179,6 +1181,10 @@ static int run_git_commit(const char *defmsg, !opts->signoff && !opts->record_origin && !opts->explicit_cleanup) strvec_push(&cmd.args, "--cleanup=verbatim"); + else if (!(flags & CLEANUP_MSG) && + !opts->reviewby && !opts->record_origin && + !opts->explicit_cleanup) + strvec_push(&cmd.args, "--cleanup=verbatim"); if ((flags & ALLOW_EMPTY)) strvec_push(&cmd.args, "--allow-empty"); @@ -1628,6 +1634,9 @@ static int try_to_commit(struct repository *r, else if ((opts->signoff || opts->record_origin) && !opts->explicit_cleanup) cleanup = COMMIT_MSG_CLEANUP_SPACE; + else if ((opts->reviewby || opts->record_origin) && + !opts->explicit_cleanup) + cleanup = COMMIT_MSG_CLEANUP_SPACE; else cleanup = opts->default_msg_cleanup; @@ -2039,6 +2048,8 @@ static int append_squash_message(struct strbuf *buf, const char *body, */ if (opts->signoff) append_signoff(buf, 0, 0); + if (opts->reviewby) + append_reviewby(buf, 0, 0); if ((command == TODO_FIXUP) && (flag & TODO_REPLACE_FIXUP_MSG) && @@ -2418,6 +2429,9 @@ static int do_pick_commit(struct repository *r, if (opts->signoff && !is_fixup(command)) append_signoff(&ctx->message, 0, 0); + if (opts->reviewby && !is_fixup(command)) + append_reviewby(&ctx->message, 0, 0); + if (is_rebase_i(opts) && write_author_script(msg.message) < 0) res = -1; else if (!opts->strategy || @@ -3079,6 +3093,8 @@ static int populate_opts_cb(const char *key, const char *value, git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.signoff")) opts->signoff = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); + else if (!strcmp(key, "options.reviewby")) + opts->reviewby = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.record-origin")) opts->record_origin = git_config_bool_or_int(key, value, ctx->kvi, &error_flag); else if (!strcmp(key, "options.allow-ff")) @@ -3180,6 +3196,10 @@ static int read_populate_opts(struct replay_opts *opts) opts->allow_ff = 0; opts->signoff = 1; } + if (file_exists(rebase_path_reviewby())) { + opts->allow_ff = 0; + opts->reviewby = 1; + } if (file_exists(rebase_path_cdate_is_adate())) { opts->allow_ff = 0; @@ -3286,6 +3306,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign); if (opts->signoff) write_file(rebase_path_signoff(), "--signoff\n"); + if (opts->reviewby) + write_file(rebase_path_reviewby(), "--reviewby\n"); if (opts->drop_redundant_commits) write_file(rebase_path_drop_redundant_commits(), "%s", ""); if (opts->keep_redundant_commits) @@ -3629,6 +3651,9 @@ static int save_opts(struct replay_opts *opts) if (opts->signoff) res |= git_config_set_in_file_gently(opts_file, "options.signoff", NULL, "true"); + if (opts->reviewby) + res |= git_config_set_in_file_gently(opts_file, + "options.reviewby", NULL, "true"); if (opts->record_origin) res |= git_config_set_in_file_gently(opts_file, "options.record-origin", NULL, "true"); @@ -4949,7 +4974,7 @@ static int pick_commits(struct repository *r, ctx->reflog_message = sequencer_reflog_action(opts); if (opts->allow_ff) - ASSERT(!(opts->signoff || opts->no_commit || + ASSERT(!(opts->signoff || opts->reviewby || opts->no_commit || opts->record_origin || should_edit(opts) || opts->committer_date_is_author_date || opts->ignore_date)); @@ -5581,6 +5606,66 @@ int sequencer_pick_revisions(struct repository *r, return res; } +void append_reviewby(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag) +{ + unsigned no_dup_rb = flag & APPEND_REVIEWBY_DEDUP; + struct strbuf rb = STRBUF_INIT; + int has_footer; + + strbuf_addstr(&rb, review_by_header); + strbuf_addstr(&rb, fmt_name(WANT_COMMITTER_IDENT)); + strbuf_addch(&rb, '\n'); + + if (!ignore_footer) + strbuf_complete_line(msgbuf); + + /* + * If the whole message buffer is equal to the rb, pretend that we + * found a conforming footer with a matching rb + */ + if (msgbuf->len - ignore_footer == rb.len && + !strncmp(msgbuf->buf, rb.buf, rb.len)) + has_footer = 3; + else + has_footer = has_conforming_footer(msgbuf, &rb, ignore_footer); + + if (!has_footer) { + const char *append_newlines = NULL; + size_t len = msgbuf->len - ignore_footer; + + if (!len) { + /* + * The buffer is completely empty. Leave foom for + * the title and body to be filled in by the user. + */ + append_newlines = "\n\n"; + } else if (len == 1) { + /* + * Buffer contains a single newline. Add another + * so that we leave room for the title and body. + */ + append_newlines = "\n"; + } else if (msgbuf->buf[len - 2] != '\n') { + /* + * Buffer ends with a single newline. Add another + * so that there is an empty line between the message + * body and the rb. + */ + append_newlines = "\n"; + } /* else, the buffer already ends with two newlines. */ + + if (append_newlines) + strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, + append_newlines, strlen(append_newlines)); + } + + if (has_footer != 3 && (!no_dup_rb || has_footer != 2)) + strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, + rb.buf, rb.len); + + strbuf_release(&rb); +} + void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag) { unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP; diff --git a/sequencer.h b/sequencer.h index 304ba4b4d3..82b79fd1e8 100644 --- a/sequencer.h +++ b/sequencer.h @@ -17,6 +17,7 @@ const char *rebase_path_dropped(void); extern const char *rebase_resolvemsg; #define APPEND_SIGNOFF_DEDUP (1u << 0) +#define APPEND_REVIEWBY_DEDUP (1u << 1) enum replay_action { REPLAY_REVERT, @@ -44,6 +45,7 @@ struct replay_opts { int record_origin; int no_commit; int signoff; + int reviewby; int allow_ff; int allow_rerere_auto; int allow_empty; @@ -205,6 +207,14 @@ int todo_list_rearrange_squash(struct todo_list *todo_list); */ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag); +/* + * Append a reviewby to the commit message in "msgbuf". The ignore_footer + * parameter specifies the number of bytes at the end of msgbuf that should + * not be considered at all. I.e., they are not checked for existing trailers, + * and the new reviewby will be spliced into the buffer before those bytes. + */ +void append_reviewby(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag); + void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode); enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg, diff --git a/t/meson.build b/t/meson.build index bfb744e886..327fa461fd 100644 --- a/t/meson.build +++ b/t/meson.build @@ -392,6 +392,7 @@ integration_tests = [ 't3436-rebase-more-options.sh', 't3437-rebase-fixup-options.sh', 't3438-rebase-broken-files.sh', + 't3439-rebase-reviewby.sh', 't3500-cherry.sh', 't3501-revert-cherry-pick.sh', 't3502-cherry-pick-merge.sh', diff --git a/t/t3439-rebase-reviewby.sh b/t/t3439-rebase-reviewby.sh new file mode 100755 index 0000000000..ca7299fbdb --- /dev/null +++ b/t/t3439-rebase-reviewby.sh @@ -0,0 +1,138 @@ +#!/bin/sh + +test_description='git rebase --reviewby + +This test runs git rebase --reviewby and make sure that it works. +' + +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + +test_expect_success 'setup' ' + git commit --allow-empty -m "Initial empty commit" && + test_commit first file a && + test_commit second file && + git checkout -b conflict-branch first && + test_commit file-2 file-2 && + test_commit conflict file && + test_commit third file && + + ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" && + + # Expected commit message for initial commit after rebase --reviewby + cat >expected-initial-reviewed <<-EOF && + Initial empty commit + + Reviewed-by: $ident + EOF + + # Expected commit message after rebase --reviewby + cat >expected-reviewed <<-EOF && + first + + Reviewed-by: $ident + EOF + + # Expected commit message after conflict resolution for rebase --reviewby + cat >expected-reviewed-conflict <<-EOF && + third + + Reviewed-by: $ident + + conflict + + Reviewed-by: $ident + + file-2 + + Reviewed-by: $ident + + EOF + + # Expected commit message after rebase without --reviewby (or with --no-reviewby) + cat >expected-unreviewed <<-EOF && + first + EOF + + git config alias.rbr "rebase --reviewby" +' + +test_expect_success 'rebase --apply --reviewby adds a Reviewed-by line' ' + test_must_fail git rbr --apply second third && + git checkout --theirs file && + git add file && + git rebase --continue && + git log --format=%B -n3 >actual && + test_cmp expected-reviewed-conflict actual +' + +test_expect_success 'rebase --no-reviewby does not add a Reviewed-by line' ' + git commit --amend -m "first" && + git rbr --no-reviewby HEAD^ && + test_commit_message HEAD expected-unreviewed +' + +test_expect_success 'rebase --exec --reviewby adds a Reviewed-by line' ' + test_when_finished "rm exec" && + git rebase --exec "touch exec" --reviewby first^ first && + test_path_is_file exec && + test_commit_message HEAD expected-reviewed +' + +test_expect_success 'rebase --root --reviewby adds a Reviewed-by line' ' + git checkout first && + git rebase --root --keep-empty --reviewby && + test_commit_message HEAD^ expected-initial-reviewed && + test_commit_message HEAD expected-reviewed +' + +test_expect_success 'rebase -m --reviewby adds a Reviewed-by line' ' + test_must_fail git rebase -m --reviewby second third && + git checkout --theirs file && + git add file && + GIT_EDITOR="sed -n /Conflicts:/,/^\\\$/p >actual" \ + git rebase --continue && + cat >expect <<-\EOF && + # Conflicts: + # file + + EOF + test_cmp expect actual && + git log --format=%B -n3 >actual && + test_cmp expected-reviewed-conflict actual +' + +test_expect_success 'rebase -i --reviewby adds a Reviewed-by line when editing commit' ' + ( + set_fake_editor && + FAKE_LINES="edit 1 edit 3 edit 2" \ + git rebase -i --reviewby first third + ) && + echo a >a && + git add a && + test_must_fail git rebase --continue && + git checkout --ours file && + echo b >a && + git add a file && + git rebase --continue && + echo c >a && + git add a && + git log --format=%B -n3 >actual && + cat >expect <<-EOF && + conflict + + Reviewed-by: $ident + + third + + Reviewed-by: $ident + + file-2 + + Reviewed-by: $ident + + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 2ae93d3c96..40d6a2f43a 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -491,6 +491,7 @@ test_expect_success 'am changes committer and keeps author' ' "$(git log -1 --pretty=format:"%cn <%ce>" HEAD)" ' +########################## signoff begin ########################## test_expect_success 'am --signoff adds Signed-off-by: line' ' rm -fr .git/rebase-apply && git reset --hard && @@ -572,6 +573,80 @@ test_expect_success 'am without --keep removes Re: and [PATCH] stuff' ' git rev-parse topic_2 >actual && test_cmp expected actual ' +########################## signoff end ########################## + +########################## review begin ########################## +test_expect_success 'am --review adds Reviewed-by: line' ' + rm -fr .git/rebase-apply && + git reset --hard && + git checkout -b topic_3 first && + git am --reviewby <patch2 && + { + printf "third\n\nReviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" && + cat msg && + printf "Reviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" + } >expected-log && + git log --pretty=%B -2 HEAD >actual && + test_cmp expected-log actual +' +test_expect_success 'am stays in branch' ' + echo refs/heads/topic_3 >expected && + git symbolic-ref HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'am --reviewby does not add Reviewed-by: line if already there' ' + git format-patch --stdout first >patch3 && + git reset --hard first && + git am --reviewby <patch3 && + git log --pretty=%B -2 HEAD >actual && + test_cmp expected-log actual +' + +test_expect_success 'am --reviewby adds Reviewed-by: if another author is preset' ' + NAME="A N Other" && + EMAIL="a.n.other@xxxxxxxxxxx" && + { + printf "third\n\nReviewed-by: %s <%s>\nReviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \ + "$NAME" "$EMAIL" && + cat msg && + printf "Reviewed-by: %s <%s>\nReviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \ + "$NAME" "$EMAIL" + } >expected-log && + git reset --hard first && + GIT_COMMITTER_NAME="$NAME" GIT_COMMITTER_EMAIL="$EMAIL" \ + git am --reviewby <patch3 && + git log --pretty=%B -2 HEAD >actual && + test_cmp expected-log actual +' + +test_expect_success 'am --reviewby duplicates Reviewed-by: if it is not the last one' ' + NAME="A N Other" && + EMAIL="a.n.other@xxxxxxxxxxx" && + { + printf "third\n\nReviewed-by: %s <%s>\n\ +Reviewed-by: %s <%s>\nReviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \ + "$NAME" "$EMAIL" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" && + cat msg && + printf "Reviewed-by: %s <%s>\nReviewed-by: %s <%s>\n\ +Reviewed-by: %s <%s>\n\n" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \ + "$NAME" "$EMAIL" \ + "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" + } >expected-log && + git format-patch --stdout first >patch3 && + git reset --hard first && + git am --reviewby <patch3 && + git log --pretty=%B -2 HEAD >actual && + test_cmp expected-log actual +' +########################## review end ########################## test_expect_success 'am --keep really keeps the subject' ' rm -fr .git/rebase-apply && -- 2.49.0