Skip to content

Commit 5ed75e2

Browse files
Miklos Vajnagitster
authored andcommitted
cherry-pick: don't forget -s on failure
In case 'git cherry-pick -s <commit>' failed, the user had to use 'git commit -s' (i.e. state the -s option again), which is easy to forget about. Instead, write the signed-off-by line early, so plain 'git commit' will have the same result. Also update 'git commit -s', so that in case there is already a relevant Signed-off-by line before the Conflicts: line, it won't add one more at the end of the message. If there is no such line, then add it before the the Conflicts: line. Signed-off-by: Miklos Vajna <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ce5cf6f commit 5ed75e2

File tree

6 files changed

+144
-65
lines changed

6 files changed

+144
-65
lines changed

builtin/commit.c

Lines changed: 24 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "submodule.h"
2929
#include "gpg-interface.h"
3030
#include "column.h"
31+
#include "sequencer.h"
3132

3233
static const char * const builtin_commit_usage[] = {
3334
N_("git commit [options] [--] <filepattern>..."),
@@ -466,8 +467,6 @@ static int is_a_merge(const struct commit *current_head)
466467
return !!(current_head->parents && current_head->parents->next);
467468
}
468469

469-
static const char sign_off_header[] = "Signed-off-by: ";
470-
471470
static void export_one(const char *var, const char *s, const char *e, int hack)
472471
{
473472
struct strbuf buf = STRBUF_INIT;
@@ -552,47 +551,6 @@ static void determine_author_info(struct strbuf *author_ident)
552551
}
553552
}
554553

555-
static int ends_rfc2822_footer(struct strbuf *sb)
556-
{
557-
int ch;
558-
int hit = 0;
559-
int i, j, k;
560-
int len = sb->len;
561-
int first = 1;
562-
const char *buf = sb->buf;
563-
564-
for (i = len - 1; i > 0; i--) {
565-
if (hit && buf[i] == '\n')
566-
break;
567-
hit = (buf[i] == '\n');
568-
}
569-
570-
while (i < len - 1 && buf[i] == '\n')
571-
i++;
572-
573-
for (; i < len; i = k) {
574-
for (k = i; k < len && buf[k] != '\n'; k++)
575-
; /* do nothing */
576-
k++;
577-
578-
if ((buf[k] == ' ' || buf[k] == '\t') && !first)
579-
continue;
580-
581-
first = 0;
582-
583-
for (j = 0; i + j < len; j++) {
584-
ch = buf[i + j];
585-
if (ch == ':')
586-
break;
587-
if (isalnum(ch) ||
588-
(ch == '-'))
589-
continue;
590-
return 0;
591-
}
592-
}
593-
return 1;
594-
}
595-
596554
static char *cut_ident_timestamp_part(char *string)
597555
{
598556
char *ket = strrchr(string, '>');
@@ -717,21 +675,30 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
717675
stripspace(&sb, 0);
718676

719677
if (signoff) {
720-
struct strbuf sob = STRBUF_INIT;
721-
int i;
722-
723-
strbuf_addstr(&sob, sign_off_header);
724-
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
725-
getenv("GIT_COMMITTER_EMAIL")));
726-
strbuf_addch(&sob, '\n');
727-
for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
728-
; /* do nothing */
729-
if (prefixcmp(sb.buf + i, sob.buf)) {
730-
if (!i || !ends_rfc2822_footer(&sb))
731-
strbuf_addch(&sb, '\n');
732-
strbuf_addbuf(&sb, &sob);
678+
/*
679+
* See if we have a Conflicts: block at the end. If yes, count
680+
* its size, so we can ignore it.
681+
*/
682+
int ignore_footer = 0;
683+
int i, eol, previous = 0;
684+
const char *nl;
685+
686+
for (i = 0; i < sb.len; i++) {
687+
nl = memchr(sb.buf + i, '\n', sb.len - i);
688+
if (nl)
689+
eol = nl - sb.buf;
690+
else
691+
eol = sb.len;
692+
if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
693+
ignore_footer = sb.len - previous;
694+
break;
695+
}
696+
while (i < eol)
697+
i++;
698+
previous = eol;
733699
}
734-
strbuf_release(&sob);
700+
701+
append_signoff(&sb, ignore_footer);
735702
}
736703

737704
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)

sequencer.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
1919

20+
const char sign_off_header[] = "Signed-off-by: ";
21+
2022
void remove_sequencer_state(void)
2123
{
2224
struct strbuf seq_dir = STRBUF_INIT;
@@ -233,6 +235,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
233235
die(_("%s: Unable to write new index file"), action_name(opts));
234236
rollback_lock_file(&index_lock);
235237

238+
if (opts->signoff)
239+
append_signoff(msgbuf, 0);
240+
236241
if (!clean) {
237242
int i;
238243
strbuf_addstr(msgbuf, "\nConflicts:\n");
@@ -1011,3 +1016,63 @@ int sequencer_pick_revisions(struct replay_opts *opts)
10111016
save_opts(opts);
10121017
return pick_commits(todo_list, opts);
10131018
}
1019+
1020+
static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
1021+
{
1022+
int ch;
1023+
int hit = 0;
1024+
int i, j, k;
1025+
int len = sb->len - ignore_footer;
1026+
int first = 1;
1027+
const char *buf = sb->buf;
1028+
1029+
for (i = len - 1; i > 0; i--) {
1030+
if (hit && buf[i] == '\n')
1031+
break;
1032+
hit = (buf[i] == '\n');
1033+
}
1034+
1035+
while (i < len - 1 && buf[i] == '\n')
1036+
i++;
1037+
1038+
for (; i < len; i = k) {
1039+
for (k = i; k < len && buf[k] != '\n'; k++)
1040+
; /* do nothing */
1041+
k++;
1042+
1043+
if ((buf[k] == ' ' || buf[k] == '\t') && !first)
1044+
continue;
1045+
1046+
first = 0;
1047+
1048+
for (j = 0; i + j < len; j++) {
1049+
ch = buf[i + j];
1050+
if (ch == ':')
1051+
break;
1052+
if (isalnum(ch) ||
1053+
(ch == '-'))
1054+
continue;
1055+
return 0;
1056+
}
1057+
}
1058+
return 1;
1059+
}
1060+
1061+
void append_signoff(struct strbuf *msgbuf, int ignore_footer)
1062+
{
1063+
struct strbuf sob = STRBUF_INIT;
1064+
int i;
1065+
1066+
strbuf_addstr(&sob, sign_off_header);
1067+
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
1068+
getenv("GIT_COMMITTER_EMAIL")));
1069+
strbuf_addch(&sob, '\n');
1070+
for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
1071+
; /* do nothing */
1072+
if (prefixcmp(msgbuf->buf + i, sob.buf)) {
1073+
if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
1074+
strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
1075+
strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
1076+
}
1077+
strbuf_release(&sob);
1078+
}

sequencer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,8 @@ extern void remove_sequencer_state(void);
4949

5050
int sequencer_pick_revisions(struct replay_opts *opts);
5151

52+
extern const char sign_off_header[];
53+
54+
void append_signoff(struct strbuf *msgbuf, int ignore_footer);
55+
5256
#endif

t/t3507-cherry-pick-conflict.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ test_expect_success setup '
3030
test_commit initial foo a &&
3131
test_commit base foo b &&
3232
test_commit picked foo c &&
33+
test_commit --signoff picked-signed foo d &&
3334
git config advice.detachedhead false
3435
3536
'
@@ -340,4 +341,35 @@ test_expect_success 'revert conflict, diff3 -m style' '
340341
test_cmp expected actual
341342
'
342343

344+
test_expect_success 'failed cherry-pick does not forget -s' '
345+
pristine_detach initial &&
346+
test_must_fail git cherry-pick -s picked &&
347+
test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG
348+
'
349+
350+
test_expect_success 'commit after failed cherry-pick does not add duplicated -s' '
351+
pristine_detach initial &&
352+
test_must_fail git cherry-pick -s picked-signed &&
353+
git commit -a -s &&
354+
test $(git show -s |grep -c "Signed-off-by") = 1
355+
'
356+
357+
test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
358+
pristine_detach initial &&
359+
test_must_fail git cherry-pick picked &&
360+
git commit -a -s &&
361+
pwd &&
362+
cat <<EOF > expected &&
363+
picked
364+
365+
Signed-off-by: C O Mitter <[email protected]>
366+
367+
Conflicts:
368+
foo
369+
EOF
370+
371+
git show -s --pretty=format:%B > actual &&
372+
test_cmp expected actual
373+
'
374+
343375
test_done

t/t3510-cherry-pick-sequence.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ test_expect_success '--continue respects -x in first commit in multi-pick' '
410410
grep "cherry picked from.*$picked" msg
411411
'
412412

413-
test_expect_success '--signoff is not automatically propagated to resolved conflict' '
413+
test_expect_failure '--signoff is automatically propagated to resolved conflict' '
414414
pristine_detach initial &&
415415
test_expect_code 1 git cherry-pick --signoff base..anotherpick &&
416416
echo "c" >foo &&
@@ -428,7 +428,7 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
428428
grep "Signed-off-by:" anotherpick_msg
429429
'
430430

431-
test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
431+
test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' '
432432
pristine_detach initial &&
433433
test_must_fail git cherry-pick -s picked anotherpick &&
434434
echo c >foo &&
@@ -441,7 +441,7 @@ test_expect_success '--signoff dropped for implicit commit of resolution, multi-
441441
! grep Signed-off-by: msg
442442
'
443443

444-
test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
444+
test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
445445
pristine_detach initial &&
446446
test_must_fail git cherry-pick -s picked &&
447447
echo c >foo &&

t/test-lib-functions.sh

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,30 @@ test_pause () {
144144

145145
test_commit () {
146146
notick= &&
147-
if test "z$1" = "z--notick"
148-
then
149-
notick=yes
147+
signoff= &&
148+
while test $# != 0
149+
do
150+
case "$1" in
151+
--notick)
152+
notick=yes
153+
;;
154+
--signoff)
155+
signoff="$1"
156+
;;
157+
*)
158+
break
159+
;;
160+
esac
150161
shift
151-
fi &&
162+
done &&
152163
file=${2:-"$1.t"} &&
153164
echo "${3-$1}" > "$file" &&
154165
git add "$file" &&
155166
if test -z "$notick"
156167
then
157168
test_tick
158169
fi &&
159-
git commit -m "$1" &&
170+
git commit $signoff -m "$1" &&
160171
git tag "$1"
161172
}
162173

0 commit comments

Comments
 (0)