}
}
-static int has_rfc822_specials(const char *s, int len)
+static int needs_rfc822_quoting(const char *s, int len)
{
int i;
for (i = 0; i < len; i++)
return 0;
}
+static int last_line_length(struct strbuf *sb)
+{
+ int i;
+
+ /* How many bytes are already used on the last line? */
+ for (i = sb->len - 1; i >= 0; i--)
+ if (sb->buf[i] == '\n')
+ break;
+ return sb->len - (i + 1);
+}
+
static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
{
int i;
strbuf_addch(out, '"');
}
-static int is_rfc2047_special(char ch)
+enum rfc2047_type {
+ RFC2047_SUBJECT,
+ RFC2047_ADDRESS,
+};
+
+static int is_rfc2047_special(char ch, enum rfc2047_type type)
{
- return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
+ /*
+ * rfc2047, section 4.2:
+ *
+ * 8-bit values which correspond to printable ASCII characters other
+ * than "=", "?", and "_" (underscore), MAY be represented as those
+ * characters. (But see section 5 for restrictions.) In
+ * particular, SPACE and TAB MUST NOT be represented as themselves
+ * within encoded words.
+ */
+
+ /*
+ * rule out non-ASCII characters and non-printable characters (the
+ * non-ASCII check should be redundant as isprint() is not localized
+ * and only knows about ASCII, but be defensive about that)
+ */
+ if (non_ascii(ch) || !isprint(ch))
+ return 1;
+
+ /*
+ * rule out special printable characters (' ' should be the only
+ * whitespace character considered printable, but be defensive and use
+ * isspace())
+ */
+ if (isspace(ch) || ch == '=' || ch == '?' || ch == '_')
+ return 1;
+
+ /*
+ * rfc2047, section 5.3:
+ *
+ * As a replacement for a 'word' entity within a 'phrase', for example,
+ * one that precedes an address in a From, To, or Cc header. The ABNF
+ * definition for 'phrase' from RFC 822 thus becomes:
+ *
+ * phrase = 1*( encoded-word / word )
+ *
+ * In this case the set of characters that may be used in a "Q"-encoded
+ * 'encoded-word' is restricted to: <upper and lower case ASCII
+ * letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_"
+ * (underscore, ASCII 95.)>. An 'encoded-word' that appears within a
+ * 'phrase' MUST be separated from any adjacent 'word', 'text' or
+ * 'special' by 'linear-white-space'.
+ */
+
+ if (type != RFC2047_ADDRESS)
+ return 0;
+
+ /* '=' and '_' are special cases and have been checked above */
+ return !(isalnum(ch) || ch == '!' || ch == '*' || ch == '+' || ch == '-' || ch == '/');
}
-static void add_rfc2047(struct strbuf *sb, const char *line, int len,
- const char *encoding)
+static int needs_rfc2047_encoding(const char *line, int len,
+ enum rfc2047_type type)
{
- static const int max_length = 78; /* per rfc2822 */
int i;
- int line_len;
-
- /* How many bytes are already used on the current line? */
- for (i = sb->len - 1; i >= 0; i--)
- if (sb->buf[i] == '\n')
- break;
- line_len = sb->len - (i+1);
for (i = 0; i < len; i++) {
int ch = line[i];
if (non_ascii(ch) || ch == '\n')
- goto needquote;
+ return 1;
if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
- goto needquote;
+ return 1;
}
- strbuf_add_wrapped_bytes(sb, line, len, 0, 1, max_length - line_len);
- return;
-needquote:
+ return 0;
+}
+
+static void add_rfc2047(struct strbuf *sb, const char *line, int len,
+ const char *encoding, enum rfc2047_type type)
+{
+ static const int max_encoded_length = 76; /* per rfc2047 */
+ int i;
+ int line_len = last_line_length(sb);
+
strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
strbuf_addf(sb, "=?%s?q?", encoding);
line_len += strlen(encoding) + 5; /* 5 for =??q? */
for (i = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF;
+ int is_special = is_rfc2047_special(ch, type);
+
+ /*
+ * According to RFC 2047, we could encode the special character
+ * ' ' (space) with '_' (underscore) for readability. But many
+ * programs do not understand this and just leave the
+ * underscore in place. Thus, we do nothing special here, which
+ * causes ' ' to be encoded as '=20', avoiding this problem.
+ */
- if (line_len >= max_length - 2) {
+ if (line_len + 2 + (is_special ? 3 : 1) > max_encoded_length) {
strbuf_addf(sb, "?=\n =?%s?q?", encoding);
line_len = strlen(encoding) + 5 + 1; /* =??q? plus SP */
}
- /*
- * We encode ' ' using '=20' even though rfc2047
- * allows using '_' for readability. Unfortunately,
- * many programs do not understand this and just
- * leave the underscore in place.
- */
- if (is_rfc2047_special(ch) || ch == ' ' || ch == '\n') {
+ if (is_special) {
strbuf_addf(sb, "=%02X", ch);
line_len += 3;
- }
- else {
+ } else {
strbuf_addch(sb, ch);
line_len++;
}
const char *what, struct strbuf *sb,
const char *line, const char *encoding)
{
+ int max_length = 78; /* per rfc2822 */
char *date;
int namelen;
unsigned long time;
if (pp->fmt == CMIT_FMT_EMAIL) {
char *name_tail = strchr(line, '<');
int display_name_length;
- int final_line;
if (!name_tail)
return;
while (line < name_tail && isspace(name_tail[-1]))
name_tail--;
display_name_length = name_tail - line;
strbuf_addstr(sb, "From: ");
- if (!has_rfc822_specials(line, display_name_length)) {
- add_rfc2047(sb, line, display_name_length, encoding);
- } else {
+ if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) {
+ add_rfc2047(sb, line, display_name_length,
+ encoding, RFC2047_ADDRESS);
+ max_length = 76; /* per rfc2047 */
+ } else if (needs_rfc822_quoting(line, display_name_length)) {
struct strbuf quoted = STRBUF_INIT;
add_rfc822_quoted("ed, line, display_name_length);
- add_rfc2047(sb, quoted.buf, quoted.len, encoding);
+ strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
+ -6, 1, max_length);
strbuf_release("ed);
+ } else {
+ strbuf_add_wrapped_bytes(sb, line, display_name_length,
+ -6, 1, max_length);
}
- for (final_line = 0; final_line < sb->len; final_line++)
- if (sb->buf[sb->len - final_line - 1] == '\n')
- break;
- if (namelen - display_name_length + final_line > 78) {
+ if (namelen - display_name_length + last_line_length(sb) > max_length) {
strbuf_addch(sb, '\n');
if (!isspace(name_tail[0]))
strbuf_addch(sb, ' ');
return NULL;
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
- if (!strcmp(use_encoding, output_encoding))
+ if (same_encoding(use_encoding, output_encoding))
if (encoding) /* we'll strip encoding header later */
out = xstrdup(commit->buffer);
else
const char *encoding,
int need_8bit_cte)
{
+ static const int max_length = 78; /* per rfc2047 */
struct strbuf title;
strbuf_init(&title, 80);
strbuf_grow(sb, title.len + 1024);
if (pp->subject) {
strbuf_addstr(sb, pp->subject);
- add_rfc2047(sb, title.buf, title.len, encoding);
+ if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT))
+ add_rfc2047(sb, title.buf, title.len,
+ encoding, RFC2047_SUBJECT);
+ else
+ strbuf_add_wrapped_bytes(sb, title.buf, title.len,
+ -last_line_length(sb), 1, max_length);
} else {
strbuf_addbuf(sb, &title);
}
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-void remove_sequencer_state(void)
+const char sign_off_header[] = "Signed-off-by: ";
+
+static void remove_sequencer_state(void)
{
struct strbuf seq_dir = STRBUF_INIT;
out->reencoded_message = NULL;
out->message = commit->buffer;
- if (strcmp(encoding, git_commit_encoding))
+ if (same_encoding(encoding, git_commit_encoding))
out->reencoded_message = reencode_string(commit->buffer,
git_commit_encoding, encoding);
if (out->reencoded_message)
struct ref_lock *ref_lock;
read_cache();
- if (checkout_fast_forward(from, to))
+ if (checkout_fast_forward(from, to, 1))
exit(1); /* the callee should have complained already */
ref_lock = lock_any_ref_for_update("HEAD", from, 0);
return write_ref_sha1(ref_lock, to, "cherry-pick");
die(_("%s: Unable to write new index file"), action_name(opts));
rollback_lock_file(&index_lock);
+ if (opts->signoff)
+ append_signoff(msgbuf, 0);
+
if (!clean) {
int i;
strbuf_addstr(msgbuf, "\nConflicts:\n");
if (allow_empty)
argv_array_push(&array, "--allow-empty");
+ if (opts->allow_empty_message)
+ argv_array_push(&array, "--allow-empty-message");
+
rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
argv_array_clear(&array);
return rc;
save_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)
+{
+ struct strbuf sob = STRBUF_INIT;
+ int i;
+
+ 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);
+ }
+ strbuf_release(&sob);
+}