Merge branch 'bc/append-signed-off-by'
Consolidate codepaths that inspect log-message-to-be and decide to add a new Signed-off-by line in various commands. * bc/append-signed-off-by: git-commit: populate the edit buffer with 2 blank lines before s-o-b Unify appending signoff in format-patch, commit and sequencer format-patch: update append_signoff prototype t4014: more tests about appending s-o-b lines sequencer.c: teach append_signoff to avoid adding a duplicate newline sequencer.c: teach append_signoff how to detect duplicate s-o-b sequencer.c: always separate "(cherry picked from" from commit body sequencer.c: require a conforming footer to be preceded by a blank line sequencer.c: recognize "(cherry picked from ..." as part of s-o-b footer t/t3511: add some tests of 'cherry-pick -s' functionality t/test-lib-functions.sh: allow to specify the tag name to test_commit commit, cherry-pick -s: remove broken support for multiline rfc2822 fields sequencer.c: rework search for start of footer to improve clarity
This commit is contained in:
commit
900c8ecb5c
@ -702,7 +702,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
|
||||
previous = eol;
|
||||
}
|
||||
|
||||
append_signoff(&sb, ignore_footer);
|
||||
append_signoff(&sb, ignore_footer, 0);
|
||||
}
|
||||
|
||||
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
|
||||
|
@ -1086,7 +1086,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
struct commit *origin = NULL, *head = NULL;
|
||||
const char *in_reply_to = NULL;
|
||||
struct patch_ids ids;
|
||||
char *add_signoff = NULL;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int use_patch_format = 0;
|
||||
int quiet = 0;
|
||||
@ -1193,16 +1192,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
rev.subject_prefix = strbuf_detach(&sprefix, NULL);
|
||||
}
|
||||
|
||||
if (do_signoff) {
|
||||
const char *committer;
|
||||
const char *endpos;
|
||||
committer = git_committer_info(IDENT_STRICT);
|
||||
endpos = strchr(committer, '>');
|
||||
if (!endpos)
|
||||
die(_("bogus committer info %s"), committer);
|
||||
add_signoff = xmemdupz(committer, endpos - committer + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < extra_hdr.nr; i++) {
|
||||
strbuf_addstr(&buf, extra_hdr.items[i].string);
|
||||
strbuf_addch(&buf, '\n');
|
||||
@ -1393,7 +1382,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
total++;
|
||||
start_number--;
|
||||
}
|
||||
rev.add_signoff = add_signoff;
|
||||
rev.add_signoff = do_signoff;
|
||||
while (0 <= --nr) {
|
||||
int shown;
|
||||
commit = list[nr];
|
||||
|
92
log-tree.c
92
log-tree.c
@ -9,6 +9,7 @@
|
||||
#include "string-list.h"
|
||||
#include "color.h"
|
||||
#include "gpg-interface.h"
|
||||
#include "sequencer.h"
|
||||
|
||||
struct decoration name_decoration = { "object names" };
|
||||
|
||||
@ -206,89 +207,6 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
|
||||
putchar(')');
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
|
||||
* Signed-off-by: and Acked-by: lines.
|
||||
*/
|
||||
static int detect_any_signoff(char *letter, int size)
|
||||
{
|
||||
char *cp;
|
||||
int seen_colon = 0;
|
||||
int seen_at = 0;
|
||||
int seen_name = 0;
|
||||
int seen_head = 0;
|
||||
|
||||
cp = letter + size;
|
||||
while (letter <= --cp && *cp == '\n')
|
||||
continue;
|
||||
|
||||
while (letter <= cp) {
|
||||
char ch = *cp--;
|
||||
if (ch == '\n')
|
||||
break;
|
||||
|
||||
if (!seen_at) {
|
||||
if (ch == '@')
|
||||
seen_at = 1;
|
||||
continue;
|
||||
}
|
||||
if (!seen_colon) {
|
||||
if (ch == '@')
|
||||
return 0;
|
||||
else if (ch == ':')
|
||||
seen_colon = 1;
|
||||
else
|
||||
seen_name = 1;
|
||||
continue;
|
||||
}
|
||||
if (('A' <= ch && ch <= 'Z') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
ch == '-') {
|
||||
seen_head = 1;
|
||||
continue;
|
||||
}
|
||||
/* no empty last line doesn't match */
|
||||
return 0;
|
||||
}
|
||||
return seen_head && seen_name;
|
||||
}
|
||||
|
||||
static void append_signoff(struct strbuf *sb, const char *signoff)
|
||||
{
|
||||
static const char signed_off_by[] = "Signed-off-by: ";
|
||||
size_t signoff_len = strlen(signoff);
|
||||
int has_signoff = 0;
|
||||
char *cp;
|
||||
|
||||
cp = sb->buf;
|
||||
|
||||
/* First see if we already have the sign-off by the signer */
|
||||
while ((cp = strstr(cp, signed_off_by))) {
|
||||
|
||||
has_signoff = 1;
|
||||
|
||||
cp += strlen(signed_off_by);
|
||||
if (cp + signoff_len >= sb->buf + sb->len)
|
||||
break;
|
||||
if (strncmp(cp, signoff, signoff_len))
|
||||
continue;
|
||||
if (!isspace(cp[signoff_len]))
|
||||
continue;
|
||||
/* we already have him */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_signoff)
|
||||
has_signoff = detect_any_signoff(sb->buf, sb->len);
|
||||
|
||||
if (!has_signoff)
|
||||
strbuf_addch(sb, '\n');
|
||||
|
||||
strbuf_addstr(sb, signed_off_by);
|
||||
strbuf_add(sb, signoff, signoff_len);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
static unsigned int digits_in_number(unsigned int number)
|
||||
{
|
||||
unsigned int i = 10, result = 1;
|
||||
@ -669,8 +587,10 @@ void show_log(struct rev_info *opt)
|
||||
/*
|
||||
* And then the pretty-printed message itself
|
||||
*/
|
||||
if (ctx.need_8bit_cte >= 0)
|
||||
ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
|
||||
if (ctx.need_8bit_cte >= 0 && opt->add_signoff)
|
||||
ctx.need_8bit_cte =
|
||||
has_non_ascii(fmt_name(getenv("GIT_COMMITTER_NAME"),
|
||||
getenv("GIT_COMMITTER_EMAIL")));
|
||||
ctx.date_mode = opt->date_mode;
|
||||
ctx.date_mode_explicit = opt->date_mode_explicit;
|
||||
ctx.abbrev = opt->diffopt.abbrev;
|
||||
@ -683,7 +603,7 @@ void show_log(struct rev_info *opt)
|
||||
pretty_print_commit(&ctx, commit, &msgbuf);
|
||||
|
||||
if (opt->add_signoff)
|
||||
append_signoff(&msgbuf, opt->add_signoff);
|
||||
append_signoff(&msgbuf, 0, APPEND_SIGNOFF_DEDUP);
|
||||
|
||||
if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
|
||||
ctx.notes_message && *ctx.notes_message) {
|
||||
|
@ -138,7 +138,7 @@ struct rev_info {
|
||||
int reroll_count;
|
||||
char *message_id;
|
||||
struct string_list *ref_message_ids;
|
||||
const char *add_signoff;
|
||||
int add_signoff;
|
||||
const char *extra_headers;
|
||||
const char *log_reencode;
|
||||
const char *subject_prefix;
|
||||
|
192
sequencer.c
192
sequencer.c
@ -18,6 +18,89 @@
|
||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||
|
||||
const char sign_off_header[] = "Signed-off-by: ";
|
||||
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
|
||||
|
||||
static int is_rfc2822_line(const char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int ch = buf[i];
|
||||
if (ch == ':')
|
||||
return 1;
|
||||
if (!isalnum(ch) && ch != '-')
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_cherry_picked_from_line(const char *buf, int len)
|
||||
{
|
||||
/*
|
||||
* We only care that it looks roughly like (cherry picked from ...)
|
||||
*/
|
||||
return len > strlen(cherry_picked_prefix) + 1 &&
|
||||
!prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 for non-conforming footer
|
||||
* Returns 1 for conforming footer
|
||||
* Returns 2 when sob exists within conforming footer
|
||||
* Returns 3 when sob exists within conforming footer as last entry
|
||||
*/
|
||||
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
|
||||
int ignore_footer)
|
||||
{
|
||||
char prev;
|
||||
int i, k;
|
||||
int len = sb->len - ignore_footer;
|
||||
const char *buf = sb->buf;
|
||||
int found_sob = 0;
|
||||
|
||||
/* footer must end with newline */
|
||||
if (!len || buf[len - 1] != '\n')
|
||||
return 0;
|
||||
|
||||
prev = '\0';
|
||||
for (i = len - 1; i > 0; i--) {
|
||||
char ch = buf[i];
|
||||
if (prev == '\n' && ch == '\n') /* paragraph break */
|
||||
break;
|
||||
prev = ch;
|
||||
}
|
||||
|
||||
/* require at least one blank line */
|
||||
if (prev != '\n' || buf[i] != '\n')
|
||||
return 0;
|
||||
|
||||
/* advance to start of last paragraph */
|
||||
while (i < len - 1 && buf[i] == '\n')
|
||||
i++;
|
||||
|
||||
for (; i < len; i = k) {
|
||||
int found_rfc2822;
|
||||
|
||||
for (k = i; k < len && buf[k] != '\n'; k++)
|
||||
; /* do nothing */
|
||||
k++;
|
||||
|
||||
found_rfc2822 = is_rfc2822_line(buf + i, k - i - 1);
|
||||
if (found_rfc2822 && sob &&
|
||||
!strncmp(buf + i, sob->buf, sob->len))
|
||||
found_sob = k;
|
||||
|
||||
if (!(found_rfc2822 ||
|
||||
is_cherry_picked_from_line(buf + i, k - i - 1)))
|
||||
return 0;
|
||||
}
|
||||
if (found_sob == i)
|
||||
return 3;
|
||||
if (found_sob)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void remove_sequencer_state(void)
|
||||
{
|
||||
@ -237,7 +320,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
|
||||
rollback_lock_file(&index_lock);
|
||||
|
||||
if (opts->signoff)
|
||||
append_signoff(msgbuf, 0);
|
||||
append_signoff(msgbuf, 0, 0);
|
||||
|
||||
if (!clean) {
|
||||
int i;
|
||||
@ -496,7 +579,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
|
||||
}
|
||||
|
||||
if (opts->record_origin) {
|
||||
strbuf_addstr(&msgbuf, "(cherry picked from commit ");
|
||||
if (!has_conforming_footer(&msgbuf, NULL, 0))
|
||||
strbuf_addch(&msgbuf, '\n');
|
||||
strbuf_addstr(&msgbuf, cherry_picked_prefix);
|
||||
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
|
||||
strbuf_addstr(&msgbuf, ")\n");
|
||||
}
|
||||
@ -1021,62 +1106,67 @@ int sequencer_pick_revisions(struct replay_opts *opts)
|
||||
return pick_commits(todo_list, opts);
|
||||
}
|
||||
|
||||
static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
|
||||
{
|
||||
int ch;
|
||||
int hit = 0;
|
||||
int i, j, k;
|
||||
int len = sb->len - ignore_footer;
|
||||
int first = 1;
|
||||
const char *buf = sb->buf;
|
||||
|
||||
for (i = len - 1; i > 0; i--) {
|
||||
if (hit && buf[i] == '\n')
|
||||
break;
|
||||
hit = (buf[i] == '\n');
|
||||
}
|
||||
|
||||
while (i < len - 1 && buf[i] == '\n')
|
||||
i++;
|
||||
|
||||
for (; i < len; i = k) {
|
||||
for (k = i; k < len && buf[k] != '\n'; k++)
|
||||
; /* do nothing */
|
||||
k++;
|
||||
|
||||
if ((buf[k] == ' ' || buf[k] == '\t') && !first)
|
||||
continue;
|
||||
|
||||
first = 0;
|
||||
|
||||
for (j = 0; i + j < len; j++) {
|
||||
ch = buf[i + j];
|
||||
if (ch == ':')
|
||||
break;
|
||||
if (isalnum(ch) ||
|
||||
(ch == '-'))
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer)
|
||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
|
||||
{
|
||||
unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
|
||||
struct strbuf sob = STRBUF_INIT;
|
||||
int i;
|
||||
int has_footer;
|
||||
|
||||
strbuf_addstr(&sob, sign_off_header);
|
||||
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
|
||||
getenv("GIT_COMMITTER_EMAIL")));
|
||||
strbuf_addch(&sob, '\n');
|
||||
for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
|
||||
; /* do nothing */
|
||||
if (prefixcmp(msgbuf->buf + i, sob.buf)) {
|
||||
if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
|
||||
strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
|
||||
strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
|
||||
|
||||
/*
|
||||
* If the whole message buffer is equal to the sob, pretend that we
|
||||
* found a conforming footer with a matching sob
|
||||
*/
|
||||
if (msgbuf->len - ignore_footer == sob.len &&
|
||||
!strncmp(msgbuf->buf, sob.buf, sob.len))
|
||||
has_footer = 3;
|
||||
else
|
||||
has_footer = has_conforming_footer(msgbuf, &sob, 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 (msgbuf->buf[len - 1] != '\n') {
|
||||
/*
|
||||
* Incomplete line. Complete the line and add a
|
||||
* blank one so that there is an empty line between
|
||||
* the message body and the sob.
|
||||
*/
|
||||
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 sob.
|
||||
*/
|
||||
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_sob || has_footer != 2))
|
||||
strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
|
||||
sob.buf, sob.len);
|
||||
|
||||
strbuf_release(&sob);
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
#define SEQ_TODO_FILE "sequencer/todo"
|
||||
#define SEQ_OPTS_FILE "sequencer/opts"
|
||||
|
||||
#define APPEND_SIGNOFF_DEDUP (1u << 0)
|
||||
|
||||
enum replay_action {
|
||||
REPLAY_REVERT,
|
||||
REPLAY_PICK
|
||||
@ -48,6 +50,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
|
||||
|
||||
extern const char sign_off_header[];
|
||||
|
||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer);
|
||||
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
|
||||
|
||||
#endif
|
||||
|
219
t/t3511-cherry-pick-x.sh
Executable file
219
t/t3511-cherry-pick-x.sh
Executable file
@ -0,0 +1,219 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='Test cherry-pick -x and -s'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
pristine_detach () {
|
||||
git cherry-pick --quit &&
|
||||
git checkout -f "$1^0" &&
|
||||
git read-tree -u --reset HEAD &&
|
||||
git clean -d -f -f -q -x
|
||||
}
|
||||
|
||||
mesg_one_line='base: commit message'
|
||||
|
||||
mesg_no_footer="$mesg_one_line
|
||||
|
||||
OneWordBodyThatsNotA-S-o-B"
|
||||
|
||||
mesg_with_footer="$mesg_no_footer
|
||||
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
Signed-off-by: A.U. Thor <author@example.com>
|
||||
Signed-off-by: B.U. Thor <buthor@example.com>"
|
||||
|
||||
mesg_broken_footer="$mesg_no_footer
|
||||
|
||||
The signed-off-by string should begin with the words Signed-off-by followed
|
||||
by a colon and space, and then the signers name and email address. e.g.
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
|
||||
|
||||
mesg_with_footer_sob="$mesg_with_footer
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
|
||||
|
||||
mesg_with_cherry_footer="$mesg_with_footer_sob
|
||||
(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
|
||||
Tested-by: C.U. Thor <cuthor@example.com>"
|
||||
|
||||
|
||||
test_expect_success setup '
|
||||
git config advice.detachedhead false &&
|
||||
echo unrelated >unrelated &&
|
||||
git add unrelated &&
|
||||
test_commit initial foo a &&
|
||||
test_commit "$mesg_one_line" foo b mesg-one-line &&
|
||||
git reset --hard initial &&
|
||||
test_commit "$mesg_no_footer" foo b mesg-no-footer &&
|
||||
git reset --hard initial &&
|
||||
test_commit "$mesg_broken_footer" foo b mesg-broken-footer &&
|
||||
git reset --hard initial &&
|
||||
test_commit "$mesg_with_footer" foo b mesg-with-footer &&
|
||||
git reset --hard initial &&
|
||||
test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob &&
|
||||
git reset --hard initial &&
|
||||
test_commit "$mesg_with_cherry_footer" foo b mesg-with-cherry-footer &&
|
||||
pristine_detach initial &&
|
||||
test_commit conflicting unrelated
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x inserts blank line after one line subject' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-one-line^0` &&
|
||||
git cherry-pick -x mesg-one-line &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_one_line
|
||||
|
||||
(cherry picked from commit $sha1)
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s inserts blank line after one line subject' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-one-line &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_one_line
|
||||
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s inserts blank line after non-conforming footer' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-broken-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_broken_footer
|
||||
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-no-footer^0` &&
|
||||
git cherry-pick -x mesg-no-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_no_footer
|
||||
|
||||
(cherry picked from commit $sha1)
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s inserts blank line when conforming footer not found' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-no-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_no_footer
|
||||
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x -s inserts blank line when conforming footer not found' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-no-footer^0` &&
|
||||
git cherry-pick -x -s mesg-no-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_no_footer
|
||||
|
||||
(cherry picked from commit $sha1)
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s adds sob when last sob doesnt match committer' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-with-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_footer
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x -s adds sob when last sob doesnt match committer' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-with-footer^0` &&
|
||||
git cherry-pick -x -s mesg-with-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_footer
|
||||
(cherry picked from commit $sha1)
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s refrains from adding duplicate trailing sob' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-with-footer-sob &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_footer_sob
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists for committer' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-with-footer-sob^0` &&
|
||||
git cherry-pick -x -s mesg-with-footer-sob &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_footer_sob
|
||||
(cherry picked from commit $sha1)
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-with-cherry-footer^0` &&
|
||||
git cherry-pick -x mesg-with-cherry-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_cherry_footer
|
||||
(cherry picked from commit $sha1)
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as part of footer' '
|
||||
pristine_detach initial &&
|
||||
git cherry-pick -s mesg-with-cherry-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_cherry_footer
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cherry-pick -x -s treats "(cherry picked from..." line as part of footer' '
|
||||
pristine_detach initial &&
|
||||
sha1=`git rev-parse mesg-with-cherry-footer^0` &&
|
||||
git cherry-pick -x -s mesg-with-cherry-footer &&
|
||||
cat <<-EOF >expect &&
|
||||
$mesg_with_cherry_footer
|
||||
(cherry picked from commit $sha1)
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
EOF
|
||||
git log -1 --pretty=format:%B >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
@ -972,6 +972,268 @@ test_expect_success 'empty subject prefix does not have extra space' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
append_signoff()
|
||||
{
|
||||
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
|
||||
git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
|
||||
sed -n -e "1,/^---$/p" append_signoff.patch |
|
||||
egrep -n "^Subject|Sign|^$"
|
||||
}
|
||||
|
||||
test_expect_success 'signoff: commit with no body' '
|
||||
append_signoff </dev/null >actual &&
|
||||
cat <<\EOF | sed "s/EOL$//" >expected &&
|
||||
4:Subject: [PATCH] EOL
|
||||
8:
|
||||
9:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: commit with only subject' '
|
||||
echo subject | append_signoff >actual &&
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
9:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: commit with only subject that does not end with NL' '
|
||||
printf subject | append_signoff >actual &&
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
9:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: no existing signoffs' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: no existing signoffs and no trailing NL' '
|
||||
printf "subject\n\nbody" | append_signoff >actual &&
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: some random signoff' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Signed-off-by: my@house
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: my@house
|
||||
12:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: misc conforming footer elements' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Signed-off-by: my@house
|
||||
(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
|
||||
Tested-by: Some One <someone@example.com>
|
||||
Bug: 1234
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: my@house
|
||||
15:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: some random signoff-alike' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
Fooled-by-me: my@house
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
11:
|
||||
12:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: not really a signoff' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
I want to mention about Signed-off-by: here.
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
9:I want to mention about Signed-off-by: here.
|
||||
10:
|
||||
11:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: not really a signoff (2)' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
My unfortunate
|
||||
Signed-off-by: example happens to be wrapped here.
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:Signed-off-by: example happens to be wrapped here.
|
||||
11:
|
||||
12:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: valid S-o-b paragraph in the middle' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
Signed-off-by: my@house
|
||||
Signed-off-by: your@house
|
||||
|
||||
A lot of houses.
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
9:Signed-off-by: my@house
|
||||
10:Signed-off-by: your@house
|
||||
11:
|
||||
13:
|
||||
14:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: the same signoff at the end' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: the same signoff at the end, no trailing NL' '
|
||||
printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" |
|
||||
append_signoff >actual &&
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
9:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: the same signoff NOT at the end' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Signed-off-by: C O Mitter <committer@example.com>
|
||||
Signed-off-by: my@house
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
11:Signed-off-by: C O Mitter <committer@example.com>
|
||||
12:Signed-off-by: my@house
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: detect garbage in non-conforming footer' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Tested-by: my@house
|
||||
Some Trash
|
||||
Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
13:Signed-off-by: C O Mitter <committer@example.com>
|
||||
14:
|
||||
15:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'signoff: footer begins with non-signoff without @ sign' '
|
||||
append_signoff <<\EOF >actual &&
|
||||
subject
|
||||
|
||||
body
|
||||
|
||||
Reviewed-id: Noone
|
||||
Tested-by: my@house
|
||||
Change-id: Ideadbeef
|
||||
Signed-off-by: C O Mitter <committer@example.com>
|
||||
Bug: 1234
|
||||
EOF
|
||||
cat >expected <<\EOF &&
|
||||
4:Subject: [PATCH] subject
|
||||
8:
|
||||
10:
|
||||
14:Signed-off-by: C O Mitter <committer@example.com>
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'format patch ignores color.ui' '
|
||||
test_unconfig color.ui &&
|
||||
git format-patch --stdout -1 >expect &&
|
||||
|
@ -424,6 +424,18 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'commit -s places sob on third line after two empty lines' '
|
||||
git commit -s --allow-empty --allow-empty-message &&
|
||||
cat <<-EOF >expect &&
|
||||
|
||||
|
||||
Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
|
||||
|
||||
EOF
|
||||
sed -e "/^#/d" -e "s/^:.*//" .git/COMMIT_EDITMSG >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
write_script .git/FAKE_EDITOR <<\EOF
|
||||
mv "$1" "$1.orig"
|
||||
(
|
||||
|
@ -135,12 +135,12 @@ test_pause () {
|
||||
fi
|
||||
}
|
||||
|
||||
# Call test_commit with the arguments "<message> [<file> [<contents>]]"
|
||||
# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
|
||||
#
|
||||
# This will commit a file with the given contents and the given commit
|
||||
# message. It will also add a tag with <message> as name.
|
||||
# message, and tag the resulting commit with the given tag name.
|
||||
#
|
||||
# Both <file> and <contents> default to <message>.
|
||||
# <file>, <contents>, and <tag> all default to <message>.
|
||||
|
||||
test_commit () {
|
||||
notick= &&
|
||||
@ -168,7 +168,7 @@ test_commit () {
|
||||
test_tick
|
||||
fi &&
|
||||
git commit $signoff -m "$1" &&
|
||||
git tag "$1"
|
||||
git tag "${4:-$1}"
|
||||
}
|
||||
|
||||
# Call test_merge with the arguments "<message> <commit>", where <commit>
|
||||
|
Loading…
Reference in New Issue
Block a user