rebase: extend --signoff support

Allow --signoff to be used with --interactive and --merge. In interactive mode only commits marked to be picked, edited or reworded will be signed off. The main motivation for this patch was to allow one to run 'git rebase --exec "make check" --signoff' which is useful when preparing a patch series for publication and is more convenient than doing the signoff with another --exec command. This change also allows --root without --onto to work with --signoff as well (--root with --onto was already supported). Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Phillip Wood committed Mar 20, 2018 at 11:10 UTC a852ec7f273cf61296a80ddfc26c23acf2163f2f
6 files changed +72 -9
Documentation/git-rebase.txt
+4 -3
@@ -354,9 +354,10 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
354 Incompatible with the --interactive option.
355
356 --signoff::
357 - This flag is passed to 'git am' to sign off all the rebased
358 - commits (see linkgit:git-am[1]). Incompatible with the
359 - --interactive option.
357 + Add a Signed-off-by: trailer to all the rebased commits. Note
358 + that if `--interactive` is given then only commits marked to be
359 + picked, edited or reworded will have the trailer added. Incompatible
360 + with the `--preserve-merges` option.
361
362 -i::
363 --interactive::
git-rebase--interactive.sh
+3 -3
@@ -283,7 +283,7 @@ pick_one () {
283 pick_one_preserving_merges "$@" && return
284 output eval git cherry-pick $allow_rerere_autoupdate \
285 ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
286 - "$strategy_args" $empty_args $ff "$@"
286 + $signoff "$strategy_args" $empty_args $ff "$@"
287
288 # If cherry-pick dies it leaves the to-be-picked commit unrecorded. Reschedule
289 # previous task so this commit is not lost.
@@ -524,10 +524,10 @@ do_pick () {
524 # resolve before manually running git commit --amend then git
525 # rebase --continue.
526 git commit --allow-empty --allow-empty-message --amend \
527 - --no-post-rewrite -n -q -C $sha1 &&
527 + --no-post-rewrite -n -q -C $sha1 $signoff &&
528 pick_one -n $sha1 &&
529 git commit --allow-empty --allow-empty-message \
530 - --amend --no-post-rewrite -n -q -C $sha1 \
530 + --amend --no-post-rewrite -n -q -C $sha1 $signoff \
531 ${gpg_sign_opt:+"$gpg_sign_opt"} ||
532 die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
533 else
git-rebase--merge.sh
+1 -1
@@ -27,7 +27,7 @@ continue_merge () {
27 cmt=$(cat "$state_dir/current")
28 if ! git diff-index --quiet --ignore-submodules HEAD --
29 then
30 - if ! git commit ${gpg_sign_opt:+"$gpg_sign_opt"} --no-verify -C "$cmt"
30 + if ! git commit ${gpg_sign_opt:+"$gpg_sign_opt"} $signoff --no-verify -C "$cmt"
31 then
32 echo "Commit failed, please do not call \"git commit\""
33 echo "directly, but instead do one of the following: "
git-rebase.sh
+19 -1
@@ -90,6 +90,7 @@ action=
90 preserve_merges=
91 autosquash=
92 keep_empty=
93 +signoff=
94 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
95 case "$(git config --bool commit.gpgsign)" in
96 true) gpg_sign_opt=-S ;;
@@ -119,6 +120,10 @@ read_basic_state () {
120 allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
121 test -f "$state_dir"/gpg_sign_opt &&
122 gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
123 + test -f "$state_dir"/signoff && {
124 + signoff="$(cat "$state_dir"/signoff)"
125 + force_rebase=t
126 + }
127 }
128
129 write_basic_state () {
@@ -133,6 +138,7 @@ write_basic_state () {
138 test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
139 "$state_dir"/allow_rerere_autoupdate
140 test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
141 + test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
142 }
143
144 output () {
@@ -328,7 +334,13 @@ do
334 --ignore-whitespace)
335 git_am_opt="$git_am_opt $1"
336 ;;
331 - --committer-date-is-author-date|--ignore-date|--signoff|--no-signoff)
337 + --signoff)
338 + signoff=--signoff
339 + ;;
340 + --no-signoff)
341 + signoff=
342 + ;;
343 + --committer-date-is-author-date|--ignore-date)
344 git_am_opt="$git_am_opt $1"
345 force_rebase=t
346 ;;
@@ -458,6 +470,12 @@ then
470 git_format_patch_opt="$git_format_patch_opt --progress"
471 fi
472
473 +if test -n "$signoff"
474 +then
475 + git_am_opt="$git_am_opt $signoff"
476 + force_rebase=t
477 +fi
478 +
479 if test -z "$rebase_root"
480 then
481 case "$#" in
sequencer.c
+7 -1
@@ -127,6 +127,7 @@ static GIT_PATH_FUNC(rebase_path_rewritten_pending,
127 static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
128 static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
129 static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
130 +static GIT_PATH_FUNC(rebase_path_signoff, "rebase-merge/signoff")
131 static GIT_PATH_FUNC(rebase_path_head_name, "rebase-merge/head-name")
132 static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
133 static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
@@ -1605,7 +1606,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
1606 }
1607 }
1608
1608 - if (opts->signoff)
1609 + if (opts->signoff && !is_fixup(command))
1610 append_signoff(&msgbuf, 0, 0);
1611
1612 if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
@@ -2035,6 +2036,11 @@ static int read_populate_opts(struct replay_opts *opts)
2036 if (file_exists(rebase_path_verbose()))
2037 opts->verbose = 1;
2038
2039 + if (file_exists(rebase_path_signoff())) {
2040 + opts->allow_ff = 0;
2041 + opts->signoff = 1;
2042 + }
2043 +
2044 read_strategy_opts(opts, &buf);
2045 strbuf_release(&buf);
2046
t/t3428-rebase-signoff.sh
+38
@@ -12,6 +12,13 @@ cat >file <<EOF
12 a
13 EOF
14
15 +# Expected commit message for initial commit after rebase --signoff
16 +cat >expected-initial-signed <<EOF
17 +Initial empty commit
18 +
19 +Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
20 +EOF
21 +
22 # Expected commit message after rebase --signoff
23 cat >expected-signed <<EOF
24 first
@@ -43,4 +50,35 @@ test_expect_success 'rebase --no-signoff does not add a sign-off line' '
50 test_cmp expected-unsigned actual
51 '
52
53 +test_expect_success 'rebase --exec --signoff adds a sign-off line' '
54 + test_when_finished "rm exec" &&
55 + git commit --amend -m "first" &&
56 + git rebase --exec "touch exec" --signoff HEAD^ &&
57 + test_path_is_file exec &&
58 + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
59 + test_cmp expected-signed actual
60 +'
61 +
62 +test_expect_success 'rebase --root --signoff adds a sign-off line' '
63 + git commit --amend -m "first" &&
64 + git rebase --root --keep-empty --signoff &&
65 + git cat-file commit HEAD^ | sed -e "1,/^\$/d" >actual &&
66 + test_cmp expected-initial-signed actual &&
67 + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
68 + test_cmp expected-signed actual
69 +'
70 +
71 +test_expect_success 'rebase -i --signoff fails' '
72 + git commit --amend -m "first" &&
73 + git rebase -i --signoff HEAD^ &&
74 + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
75 + test_cmp expected-signed actual
76 +'
77 +
78 +test_expect_success 'rebase -m --signoff fails' '
79 + git commit --amend -m "first" &&
80 + git rebase -m --signoff HEAD^ &&
81 + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
82 + test_cmp expected-signed actual
83 +'
84 test_done