Some projects require every commit, even merges, to be signed off [*1*]. Because "git merge" does not have a "--signoff" option like "git commit" does, the user needs to add one manually when the command presents an editor to describe the merge, or later use "git commit --amend --signoff". Help developers of these projects by teaching "--signoff" option to "git merge". *1* https://public-inbox.org/git/CAHv71zK5SqbwrBFX=a8-DY9H3KT4FEyMgv__p2gZzNr0WUAPUw@xxxxxxxxxxxxxx/T/#u Requested-by: Dan Kohn <dan@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Łukasz Gryglicki <lukaszgryglicki@xxxxx> --- Documentation/git-merge.txt | 8 +++++ builtin/merge.c | 4 +++ t/t7614-merge-signoff.sh | 88 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100755 t/t7614-merge-signoff.sh diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 04fdd8cf086db..630cb4b7f90d7 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -64,6 +64,14 @@ OPTIONS ------- include::merge-options.txt[] +--signoff:: + Add Signed-off-by line by the committer at the end of the commit + log message. The meaning of a signoff depends on the project, + but it typically certifies that committer has + the rights to merge work under the same license and + agrees to a Developer Certificate of Origin + (see http://developercertificate.org/ for more information). + -S[<keyid>]:: --gpg-sign[=<keyid>]:: GPG-sign the resulting merge commit. The `keyid` argument is diff --git a/builtin/merge.c b/builtin/merge.c index 900bafdb45d0b..78c36e9bf353b 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -70,6 +70,7 @@ static int continue_current_merge; static int allow_unrelated_histories; static int show_progress = -1; static int default_to_upstream = 1; +static int signoff; static const char *sign_commit; static struct strategy all_strategy[] = { @@ -233,6 +234,7 @@ static struct option builtin_merge_options[] = { { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), + OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")), OPT_END() }; @@ -763,6 +765,8 @@ static void prepare_to_commit(struct commit_list *remoteheads) strbuf_addch(&msg, '\n'); if (0 < option_edit) strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char); + if (signoff) + append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0); write_file_buf(git_path_merge_msg(), msg.buf, msg.len); if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", git_path_merge_msg(), "merge", NULL)) diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh new file mode 100755 index 0000000000000..823d840d423df --- /dev/null +++ b/t/t7614-merge-signoff.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +test_description='git merge --signoff + +This test runs git merge --signoff and makes sure that it works. +' + +. ./test-lib.sh + +# Setup test files +test_setup () { + # Expected commit message after merge --signoff + printf "Merge branch 'master' into other-branch\n\n" >expected-signed && + printf "Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")\n" >>expected-signed && + + # Expected commit message after merge without --signoff (or with --no-signoff) + echo "Merge branch 'master' into other-branch" >expected-unsigned && + + # Initial commit and feature branch to merge master into it. + git commit --allow-empty -m "Initial empty commit" && + git checkout -b other-branch && + test_commit other-branch file1 1 +} + +# Create fake editor that just copies file +create_fake_editor () { + echo 'cp "$1" "$1.saved"' | write_script save-editor +} + +test_expect_success 'setup' ' + test_setup && create_fake_editor +' + +test_expect_success 'git merge --signoff adds a sign-off line' ' + git checkout master && + test_commit master-branch-2 file2 2 && + git checkout other-branch && + git merge master --signoff --no-edit && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + test_cmp expected-signed actual +' + +test_expect_success 'git merge does not add a sign-off line' ' + git checkout master && + test_commit master-branch-3 file3 3 && + git checkout other-branch && + git merge master --no-edit && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + test_cmp expected-unsigned actual +' + +test_expect_success 'git merge --no-signoff flag cancels --signoff flag' ' + git checkout master && + test_commit master-branch-4 file4 4 && + git checkout other-branch && + git merge master --no-edit --signoff --no-signoff && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + test_cmp expected-unsigned actual +' + +test_expect_success 'git merge --signoff adds S-o-b line in commit message editor' ' + git checkout master && + test_commit master-branch-5 file5 5 && + git checkout other-branch && + GIT_EDITOR=./save-editor git merge master -m "My Message" --edit --signoff && + test_i18ngrep "^My Message" .git/MERGE_MSG.saved && + test_i18ngrep "^Signed-off-by: " .git/MERGE_MSG.saved +' + +test_expect_success 'git merge --no-signoff does not add S-o-b line in commit message editor' ' + git checkout master && + test_commit master-branch-6 file6 6 && + git checkout other-branch && + GIT_EDITOR=./save-editor git merge master -m "My Message" --edit --no-signoff && + test_i18ngrep "^My Message" .git/MERGE_MSG.saved && + test_i18ngrep ! "^Signed-off-by: " .git/MERGE_MSG.saved +' + +test_expect_success 'git merge --no-signoff cancels --signoff flag in commit message editor' ' + git checkout master && + test_commit master-branch-7 file7 7 && + git checkout other-branch && + GIT_EDITOR=./save-editor git merge master -m "My Message" --edit --signoff --no-signoff && + test_i18ngrep "^My Message" .git/MERGE_MSG.saved && + test_i18ngrep ! "^Signed-off-by: " .git/MERGE_MSG.saved +' + +test_done -- https://github.com/git/git/pull/390