@@ -1212,7 +1212,7 @@ static int parse_mail(struct am_state *state, const char *mail)
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
- strbuf_stripspace(&msg, 0);
+ strbuf_stripspace(&msg, 0, 0);
assert(!state->author_name);
state->author_name = strbuf_detach(&author_name, NULL);
@@ -568,7 +568,7 @@ static int edit_branch_description(const char *branch_name)
strbuf_release(&buf);
return -1;
}
- strbuf_stripspace(&buf, 1);
+ strbuf_stripspace(&buf, 1, 0);
strbuf_addf(&name, "branch.%s.description", branch_name);
git_config_set(name.buf, buf.len ? buf.buf : NULL);
@@ -710,8 +710,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
} else if (use_message) {
char *buffer;
buffer = strstr(use_message_buffer, "\n\n");
- if (buffer)
+ if (buffer) {
strbuf_addstr(&sb, skip_blank_lines(buffer + 2));
+ strbuf_addbackslash(&sb);
+ }
hook_arg1 = "commit";
hook_arg2 = use_message;
} else if (fixup_message) {
@@ -786,7 +788,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
s->hints = 0;
if (clean_message_contents)
- strbuf_stripspace(&sb, 0);
+ strbuf_stripspace(&sb, 0, 0);
if (signoff)
append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
@@ -1621,7 +1623,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
- strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL, 1);
if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
rollback_index_files();
@@ -829,7 +829,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
abort_commit(remoteheads, NULL);
read_merge_msg(&msg);
- strbuf_stripspace(&msg, 0 < option_edit);
+ strbuf_stripspace(&msg, 0 < option_edit, 0);
if (!msg.len)
abort_commit(remoteheads, _("Empty commit message."));
strbuf_release(&merge_msg);
@@ -196,7 +196,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
if (launch_editor(d->edit_path, &d->buf, NULL)) {
die(_("please supply the note contents using either -m or -F option"));
}
- strbuf_stripspace(&d->buf, 1);
+ strbuf_stripspace(&d->buf, 1, 0);
}
}
@@ -221,7 +221,7 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
if (d->buf.len)
strbuf_addch(&d->buf, '\n');
strbuf_addstr(&d->buf, arg);
- strbuf_stripspace(&d->buf, 0);
+ strbuf_stripspace(&d->buf, 0, 0);
d->given = 1;
return 0;
@@ -240,7 +240,7 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
die_errno(_("cannot read '%s'"), arg);
} else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
die_errno(_("could not open or read '%s'"), arg);
- strbuf_stripspace(&d->buf, 0);
+ strbuf_stripspace(&d->buf, 0, 0);
d->given = 1;
return 0;
@@ -55,7 +55,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
die_errno("could not read the input");
if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
- strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+ strbuf_stripspace(&buf, mode == STRIP_COMMENTS, 0);
else
comment_lines(&buf);
@@ -261,7 +261,7 @@ static void create_tag(const struct object_id *object, const char *tag,
}
if (opt->cleanup_mode != CLEANUP_NONE)
- strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+ strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL, 0);
if (!opt->message_given && !buf->len)
die(_("no tag message?"));
@@ -61,7 +61,7 @@ int edit_todo_list(struct repository *r, unsigned flags)
if (strbuf_read_file(&buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
- strbuf_stripspace(&buf, 1);
+ strbuf_stripspace(&buf, 1, 0);
if (write_message(buf.buf, buf.len, todo_file, 0)) {
strbuf_release(&buf);
return -1;
@@ -1041,7 +1041,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
return 0;
- strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL, 0);
if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
@@ -1386,7 +1386,7 @@ static int try_to_commit(struct repository *r,
opts->default_msg_cleanup;
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
- strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL, 0);
if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
res = 1; /* run 'git commit' to display error message */
goto out;
@@ -4941,7 +4941,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
return -1;
}
- strbuf_stripspace(buf, 1);
+ strbuf_stripspace(buf, 1, 0);
if (buf->len == 0) {
apply_autostash(opts);
sequencer_remove_state(opts);
@@ -966,8 +966,12 @@ static size_t cleanup(char *line, size_t len)
*
* Enable skip_comments to skip every line starting with comment
* character.
+ *
+ * Enable escape_backslash to remove backslash in beginning of
+ * lines.
*/
-void strbuf_stripspace(struct strbuf *sb, int skip_comments)
+void strbuf_stripspace(struct strbuf *sb, int skip_comments,
+ int escape_backslash)
{
size_t empties = 0;
size_t i, j, len, newlen;
@@ -999,8 +1003,66 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments)
}
strbuf_setlen(sb, j);
+
+ if (escape_backslash) {
+ strbuf_escape_backslash(sb);
+ }
+}
+
+/*
+ * Add a backslash in front of commentChar and other backslash.
+ */
+void strbuf_addbackslash(struct strbuf *sb)
+{
+ size_t i, len = 0, total = 0;
+ char *eol;
+
+ for (i = 0; i < sb->len; i += len) {
+ eol = memchr(sb->buf + i, '\n', sb->len -i);
+ len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
+
+ if (len > 0 && sb->buf[i] == comment_line_char) {
+ memmove(sb->buf + i + 1, sb->buf + i, sb->len - i);
+ sb->buf[i] = '\\';
+ total += (len + 1);
+ } else if (len > 0 && sb->buf[i] == '\\') {
+ memmove(sb->buf + i + 1, sb->buf + i, sb->len - i);
+ sb->buf[i] = '\\';
+ total += len;
+ } else {
+ total += len;
+ }
+ }
+ strbuf_grow(sb, total-len);
+ strbuf_setlen(sb, total);
}
+/*
+ * Escape backslash in beginning of lines.
+ */
+void strbuf_escape_backslash(struct strbuf *sb)
+{
+ size_t i, len, total = sb->len;
+ char *eol;
+
+ for (i = 0; i < sb->len; i += len) {
+ eol = memchr(sb->buf + i, '\n', sb->len - i);
+ len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
+
+ if (sb->buf[i] == '\\') {
+ memmove(sb->buf + i, sb->buf + i + 1, total - 1 - i);
+ total --;
+ len --;
+ i++;
+ }
+ /* Avoid initite loop if len=0 */
+ if (len <= 0) len = 1;
+ }
+ strbuf_setlen(sb, sb->len - (sb->len - total));
+}
+
+
+
int strbuf_normalize_path(struct strbuf *src)
{
struct strbuf dst = STRBUF_INIT;
@@ -499,8 +499,14 @@ int strbuf_normalize_path(struct strbuf *sb);
/**
* Strip whitespace from a buffer. The second parameter controls if
* comments are considered contents to be removed or not.
+ * The third parameter controls if backslashes are escaped.
*/
-void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, int skip_comments,
+ int escape_backslash);
+
+void strbuf_escape_backslash(struct strbuf *sb);
+
+void strbuf_addbackslash(struct strbuf *sb);
static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
{
new file mode 100755
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='"git commit" allows # in the start of a line in commit message.
+
+'
+. ./test-lib.sh
+
+
+# set up fake editor for interactive editing
+cat >fake-editor <<'EOF'
+#!/bin/sh
+cp FAKE_MSG "$1"
+exit 0
+EOF
+chmod +x fake-editor
+
+FAKE_EDITOR="$(pwd)/fake-editor"
+export FAKE_EDITOR
+
+commit_msg_is () {
+ test "$(git log --pretty=format:%s%b -1)" = "$1"
+}
+
+ensure_fresh_upstream () {
+ rm -rf parent && git init --bare parent
+}
+
+test_expect_success 'setup bare parent' '
+ ensure_fresh_upstream &&
+ git remote add upstream parent
+'
+
+test_expect_success 'git commit "\#text" keeps "#text" as a commit message' '
+ echo test1 >file &&
+ git add file &&
+ printf "%s\n" "\\#text" >FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
+ commit_msg_is "#text"
+'
+
+test_expect_success 'git commit "\text" keeps "text" as a commit message' '
+ echo test2 >file &&
+ git add file &&
+ printf "%s\n" "\\text" >FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
+ commit_msg_is "text"
+'
+
+test_expect_success 'git commit "\\text" keeps "\text" as a commit message' '
+ echo test5 >file &&
+ git add file &&
+ printf "%s\n" "\\\\text" >FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
+ commit_msg_is "\\text"
+'
+
+test_expect_success 'git commit -m "\#text" keeps "#text" as a commit message' '
+ echo test1 >file &&
+ git add file &&
+ git commit -m "\\#text" &&
+ commit_msg_is "#text"
+'
+
+test_expect_success 'git commit -m "\text" keeps "text" as a commit message' '
+ echo test2 >file &&
+ git add file &&
+ git commit -m "\\text" &&
+ commit_msg_is "text"
+'
+
+test_expect_success 'git commit -m "\\text" keeps "\text" as a commit message' '
+ echo test3 >file &&
+ git add file &&
+ git commit -m "\\\\text" &&
+ commit_msg_is "\\text"
+'
+
+test_expect_success 'git commit --amend add backslash in front of comment
+and other backslash' '
+ echo "\\#Include something" > msg &&
+ echo "" >> msg &&
+ echo "Some content" >> msg &&
+ echo "\\\\\\Backslash example" >> msg &&
+ echo "test amend" > file &&
+ git add file &&
+ git commit -F msg &&
+ cat .git/COMMIT_EDITMSG > expect &&
+ git commit --amend &&
+ sed "/^#/d" .git/COMMIT_EDITMSG > actual &&
+ sed -i -e "$ d" actual &&
+ test_cmp expect actual
+'
+test_done