sequencer.c: always separate "(cherry picked from" from commit body
Start treating the "(cherry picked from" line added by cherry-pick -x the same way that the s-o-b lines are treated. Namely, separate them from the main commit message body with an empty line. Introduce tests to test this functionality. Signed-off-by: Brandon Casey <bcasey@nvidia.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Brandon Casey committed
Feb 12, 2013 at 02:17 UTC
b971e04f54e73a5a2af54ad62540273bb90ee7bb
2 files changed
+118
-63
sequencer.c
+65
-63
@@ -20,6 +20,69 @@
20
const char sign_off_header[] = "Signed-off-by: ";
21
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
22
23
+static int is_rfc2822_line(const char *buf, int len)
24
+{
25
+ int i;
26
+
27
+ for (i = 0; i < len; i++) {
28
+ int ch = buf[i];
29
+ if (ch == ':')
30
+ return 1;
31
+ if (!isalnum(ch) && ch != '-')
32
+ break;
33
+ }
34
+
35
+ return 0;
36
+}
37
+
38
+static int is_cherry_picked_from_line(const char *buf, int len)
39
+{
40
+ /*
41
+ * We only care that it looks roughly like (cherry picked from ...)
42
+ */
43
+ return len > strlen(cherry_picked_prefix) + 1 &&
44
+ !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
45
+}
46
+
47
+static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
48
+{
49
+ char prev;
50
+ int i, k;
51
+ int len = sb->len - ignore_footer;
52
+ const char *buf = sb->buf;
53
+
54
+ /* footer must end with newline */
55
+ if (!len || buf[len - 1] != '\n')
56
+ return 0;
57
+
58
+ prev = '\0';
59
+ for (i = len - 1; i > 0; i--) {
60
+ char ch = buf[i];
61
+ if (prev == '\n' && ch == '\n') /* paragraph break */
62
+ break;
63
+ prev = ch;
64
+ }
65
+
66
+ /* require at least one blank line */
67
+ if (prev != '\n' || buf[i] != '\n')
68
+ return 0;
69
+
70
+ /* advance to start of last paragraph */
71
+ while (i < len - 1 && buf[i] == '\n')
72
+ i++;
73
+
74
+ for (; i < len; i = k) {
75
+ for (k = i; k < len && buf[k] != '\n'; k++)
76
+ ; /* do nothing */
77
+ k++;
78
+
79
+ if (!is_rfc2822_line(buf + i, k - i - 1) &&
80
+ !is_cherry_picked_from_line(buf + i, k - i - 1))
81
+ return 0;
82
+ }
83
+ return 1;
84
+}
85
+
86
static void remove_sequencer_state(void)
87
{
88
struct strbuf seq_dir = STRBUF_INIT;
@@ -493,6 +556,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
556
}
557
558
if (opts->record_origin) {
559
+ if (!has_conforming_footer(&msgbuf, 0))
560
+ strbuf_addch(&msgbuf, '\n');
561
strbuf_addstr(&msgbuf, cherry_picked_prefix);
562
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
563
strbuf_addstr(&msgbuf, ")\n");
@@ -1018,69 +1083,6 @@ int sequencer_pick_revisions(struct replay_opts *opts)
1083
return pick_commits(todo_list, opts);
1084
}
1085
1021
-static int is_rfc2822_line(const char *buf, int len)
1022
-{
1023
- int i;
1024
-
1025
- for (i = 0; i < len; i++) {
1026
- int ch = buf[i];
1027
- if (ch == ':')
1028
- return 1;
1029
- if (!isalnum(ch) && ch != '-')
1030
- break;
1031
- }
1032
-
1033
- return 0;
1034
-}
1035
-
1036
-static int is_cherry_picked_from_line(const char *buf, int len)
1037
-{
1038
- /*
1039
- * We only care that it looks roughly like (cherry picked from ...)
1040
- */
1041
- return len > strlen(cherry_picked_prefix) + 1 &&
1042
- !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
1043
-}
1044
-
1045
-static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
1046
-{
1047
- char prev;
1048
- int i, k;
1049
- int len = sb->len - ignore_footer;
1050
- const char *buf = sb->buf;
1051
-
1052
- /* footer must end with newline */
1053
- if (!len || buf[len - 1] != '\n')
1054
- return 0;
1055
-
1056
- prev = '\0';
1057
- for (i = len - 1; i > 0; i--) {
1058
- char ch = buf[i];
1059
- if (prev == '\n' && ch == '\n') /* paragraph break */
1060
- break;
1061
- prev = ch;
1062
- }
1063
-
1064
- /* require at least one blank line */
1065
- if (prev != '\n' || buf[i] != '\n')
1066
- return 0;
1067
-
1068
- /* advance to start of last paragraph */
1069
- while (i < len - 1 && buf[i] == '\n')
1070
- i++;
1071
-
1072
- for (; i < len; i = k) {
1073
- for (k = i; k < len && buf[k] != '\n'; k++)
1074
- ; /* do nothing */
1075
- k++;
1076
-
1077
- if (!is_rfc2822_line(buf + i, k - i - 1) &&
1078
- !is_cherry_picked_from_line(buf + i, k - i - 1))
1079
- return 0;
1080
- }
1081
- return 1;
1082
-}
1083
-
1086
void append_signoff(struct strbuf *msgbuf, int ignore_footer)
1087
{
1088
struct strbuf sob = STRBUF_INIT;
t/t3511-cherry-pick-x.sh
+53
@@ -57,6 +57,19 @@ test_expect_success setup '
57
test_commit conflicting unrelated
58
'
59
60
+test_expect_success 'cherry-pick -x inserts blank line after one line subject' '
61
+ pristine_detach initial &&
62
+ sha1=`git rev-parse mesg-one-line^0` &&
63
+ git cherry-pick -x mesg-one-line &&
64
+ cat <<-EOF >expect &&
65
+ $mesg_one_line
66
+
67
+ (cherry picked from commit $sha1)
68
+ EOF
69
+ git log -1 --pretty=format:%B >actual &&
70
+ test_cmp expect actual
71
+'
72
+
73
test_expect_success 'cherry-pick -s inserts blank line after one line subject' '
74
pristine_detach initial &&
75
git cherry-pick -s mesg-one-line &&
@@ -81,6 +94,19 @@ test_expect_failure 'cherry-pick -s inserts blank line after non-conforming foot
94
test_cmp expect actual
95
'
96
97
+test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' '
98
+ pristine_detach initial &&
99
+ sha1=`git rev-parse mesg-no-footer^0` &&
100
+ git cherry-pick -x mesg-no-footer &&
101
+ cat <<-EOF >expect &&
102
+ $mesg_no_footer
103
+
104
+ (cherry picked from commit $sha1)
105
+ EOF
106
+ git log -1 --pretty=format:%B >actual &&
107
+ test_cmp expect actual
108
+'
109
+
110
test_expect_success 'cherry-pick -s inserts blank line when conforming footer not found' '
111
pristine_detach initial &&
112
git cherry-pick -s mesg-no-footer &&
@@ -93,6 +119,20 @@ test_expect_success 'cherry-pick -s inserts blank line when conforming footer no
119
test_cmp expect actual
120
'
121
122
+test_expect_success 'cherry-pick -x -s inserts blank line when conforming footer not found' '
123
+ pristine_detach initial &&
124
+ sha1=`git rev-parse mesg-no-footer^0` &&
125
+ git cherry-pick -x -s mesg-no-footer &&
126
+ cat <<-EOF >expect &&
127
+ $mesg_no_footer
128
+
129
+ (cherry picked from commit $sha1)
130
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
131
+ EOF
132
+ git log -1 --pretty=format:%B >actual &&
133
+ test_cmp expect actual
134
+'
135
+
136
test_expect_success 'cherry-pick -s adds sob when last sob doesnt match committer' '
137
pristine_detach initial &&
138
git cherry-pick -s mesg-with-footer &&
@@ -163,4 +203,17 @@ test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as part
203
test_cmp expect actual
204
'
205
206
+test_expect_success 'cherry-pick -x -s treats "(cherry picked from..." line as part of footer' '
207
+ pristine_detach initial &&
208
+ sha1=`git rev-parse mesg-with-cherry-footer^0` &&
209
+ git cherry-pick -x -s mesg-with-cherry-footer &&
210
+ cat <<-EOF >expect &&
211
+ $mesg_with_cherry_footer
212
+ (cherry picked from commit $sha1)
213
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
214
+ EOF
215
+ git log -1 --pretty=format:%B >actual &&
216
+ test_cmp expect actual
217
+'
218
+
219
test_done