diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index 2d4bbdb25f..bbe869c497 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -520,13 +520,28 @@ core.editor:: `GIT_EDITOR` is not set. See linkgit:git-var[1]. core.commentChar:: +core.commentString:: Commands such as `commit` and `tag` that let you edit - messages consider a line that begins with this ASCII character + messages consider a line that begins with this character commented, and removes them after the editor returns (default '#'). + If set to "auto", `git-commit` would select a character that is not the beginning character of any line in existing commit messages. ++ +Note that these two variables are aliases of each other, and in modern +versions of Git you are free to use a string (e.g., `//` or `⁑⁕⁑`) with +`commentChar`. Versions of Git prior to v2.45.0 will ignore +`commentString` but will reject a value of `commentChar` that consists +of more than a single ASCII byte. If you plan to use your config with +older and newer versions of Git, you may want to specify both: ++ + [core] + # single character for older versions + commentChar = "#" + # string for newer versions (which will override commentChar + # because it comes later in the file) + commentString = "//" core.filesRefLockTimeout:: The length of time, in milliseconds, to retry when trying to diff --git a/add-patch.c b/add-patch.c index 68f525b35c..d599ca53e1 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1105,26 +1105,26 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk) size_t i; strbuf_reset(&s->buf); - strbuf_commented_addf(&s->buf, comment_line_char, + strbuf_commented_addf(&s->buf, comment_line_str, _("Manual hunk edit mode -- see bottom for " "a quick guide.\n")); render_hunk(s, hunk, 0, 0, &s->buf); - strbuf_commented_addf(&s->buf, comment_line_char, + strbuf_commented_addf(&s->buf, comment_line_str, _("---\n" "To remove '%c' lines, make them ' ' lines " "(context).\n" "To remove '%c' lines, delete them.\n" - "Lines starting with %c will be removed.\n"), + "Lines starting with %s will be removed.\n"), s->mode->is_reverse ? '+' : '-', s->mode->is_reverse ? '-' : '+', - comment_line_char); - strbuf_commented_addf(&s->buf, comment_line_char, "%s", + comment_line_str); + strbuf_commented_addf(&s->buf, comment_line_str, "%s", _(s->mode->edit_hunk_hint)); /* * TRANSLATORS: 'it' refers to the patch mentioned in the previous * messages. */ - strbuf_commented_addf(&s->buf, comment_line_char, + strbuf_commented_addf(&s->buf, comment_line_str, _("If it does not apply cleanly, you will be " "given an opportunity to\n" "edit again. If all lines of the hunk are " @@ -1139,7 +1139,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk) for (i = 0; i < s->buf.len; ) { size_t next = find_next_line(&s->buf, i); - if (s->buf.buf[i] != comment_line_char) + if (!starts_with(s->buf.buf + i, comment_line_str)) strbuf_add(&s->plain, s->buf.buf + i, next - i); i = next; } diff --git a/builtin/am.c b/builtin/am.c index ff8e9db089..022cae2e8d 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1290,7 +1290,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, NULL); assert(!state->author_name); state->author_name = strbuf_detach(&author_name, NULL); diff --git a/builtin/branch.c b/builtin/branch.c index 8c2305ad2c..dd3e3a7dc0 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -677,18 +677,18 @@ static int edit_branch_description(const char *branch_name) exists = !read_branch_desc(&buf, branch_name); if (!buf.len || buf.buf[buf.len-1] != '\n') strbuf_addch(&buf, '\n'); - strbuf_commented_addf(&buf, comment_line_char, + strbuf_commented_addf(&buf, comment_line_str, _("Please edit the description for the branch\n" " %s\n" - "Lines starting with '%c' will be stripped.\n"), - branch_name, comment_line_char); + "Lines starting with '%s' will be stripped.\n"), + branch_name, comment_line_str); write_file_buf(edit_description(), buf.buf, buf.len); strbuf_reset(&buf); if (launch_editor(edit_description(), &buf, NULL)) { strbuf_release(&buf); return -1; } - strbuf_stripspace(&buf, comment_line_char); + strbuf_stripspace(&buf, comment_line_str); strbuf_addf(&name, "branch.%s.description", branch_name); if (buf.len || exists) diff --git a/builtin/commit.c b/builtin/commit.c index b27b56c8be..7ba7201cfb 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -685,9 +685,10 @@ static void adjust_comment_line_char(const struct strbuf *sb) char *candidate; const char *p; - comment_line_char = candidates[0]; - if (!memchr(sb->buf, comment_line_char, sb->len)) + if (!memchr(sb->buf, candidates[0], sb->len)) { + comment_line_str = xstrfmt("%c", candidates[0]); return; + } p = sb->buf; candidate = strchr(candidates, *p); @@ -706,7 +707,7 @@ static void adjust_comment_line_char(const struct strbuf *sb) if (!*p) die(_("unable to select a comment character that is not used\n" "in the current commit message")); - comment_line_char = *p; + comment_line_str = xstrfmt("%c", *p); } static void prepare_amend_commit(struct commit *commit, struct strbuf *sb, @@ -889,7 +890,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, NULL); if (signoff) append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0); @@ -909,18 +910,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix, struct ident_split ci, ai; const char *hint_cleanup_all = allow_empty_message ? _("Please enter the commit message for your changes." - " Lines starting\nwith '%c' will be ignored.\n") : + " Lines starting\nwith '%s' will be ignored.\n") : _("Please enter the commit message for your changes." - " Lines starting\nwith '%c' will be ignored, and an empty" + " Lines starting\nwith '%s' will be ignored, and an empty" " message aborts the commit.\n"); const char *hint_cleanup_space = allow_empty_message ? _("Please enter the commit message for your changes." " Lines starting\n" - "with '%c' will be kept; you may remove them" + "with '%s' will be kept; you may remove them" " yourself if you want to.\n") : _("Please enter the commit message for your changes." " Lines starting\n" - "with '%c' will be kept; you may remove them" + "with '%s' will be kept; you may remove them" " yourself if you want to.\n" "An empty message aborts the commit.\n"); if (whence != FROM_COMMIT) { @@ -943,12 +944,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, fprintf(s->fp, "\n"); if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) - status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char); + status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_str); else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { if (whence == FROM_COMMIT) wt_status_add_cut_line(s); } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */ - status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char); + status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_str); /* * These should never fail because they come from our own diff --git a/builtin/merge.c b/builtin/merge.c index 1cbd6a899c..6f4fec87fc 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -822,7 +822,7 @@ static const char scissors_editor_comment[] = N_("An empty message aborts the commit.\n"); static const char no_scissors_editor_comment[] = -N_("Lines starting with '%c' will be ignored, and an empty message aborts\n" +N_("Lines starting with '%s' will be ignored, and an empty message aborts\n" "the commit.\n"); static void write_merge_heads(struct commit_list *); @@ -853,16 +853,16 @@ static void prepare_to_commit(struct commit_list *remoteheads) strbuf_addch(&msg, '\n'); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { wt_status_append_cut_line(&msg); - strbuf_commented_addf(&msg, comment_line_char, "\n"); + strbuf_commented_addf(&msg, comment_line_str, "\n"); } - strbuf_commented_addf(&msg, comment_line_char, + strbuf_commented_addf(&msg, comment_line_str, _(merge_editor_comment)); if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) - strbuf_commented_addf(&msg, comment_line_char, + strbuf_commented_addf(&msg, comment_line_str, _(scissors_editor_comment)); else - strbuf_commented_addf(&msg, comment_line_char, - _(no_scissors_editor_comment), comment_line_char); + strbuf_commented_addf(&msg, comment_line_str, + _(no_scissors_editor_comment), comment_line_str); } if (signoff) append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0); diff --git a/builtin/notes.c b/builtin/notes.c index caf20fd5bd..cb011303e6 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -179,7 +179,7 @@ static void write_commented_object(int fd, const struct object_id *object) if (strbuf_read(&buf, show.out, 0) < 0) die_errno(_("could not read 'show' output")); - strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char); + strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_str); write_or_die(fd, cbuf.buf, cbuf.len); strbuf_release(&cbuf); @@ -207,10 +207,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data * copy_obj_to_fd(fd, old_note); strbuf_addch(&buf, '\n'); - strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str); strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)), - comment_line_char); - strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char); + comment_line_str); + strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str); write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); @@ -223,7 +223,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data * die(_("please supply the note contents using either -m or -F option")); } if (d->stripspace) - strbuf_stripspace(&d->buf, comment_line_char); + strbuf_stripspace(&d->buf, comment_line_str); } } @@ -264,7 +264,7 @@ static void concat_messages(struct note_data *d) if ((d->stripspace == UNSPECIFIED && d->messages[i]->stripspace == STRIPSPACE) || d->stripspace == STRIPSPACE) - strbuf_stripspace(&d->buf, 0); + strbuf_stripspace(&d->buf, NULL); strbuf_reset(&msg); } strbuf_release(&msg); diff --git a/builtin/rebase.c b/builtin/rebase.c index b9d0fb3269..49d676f42b 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -204,7 +204,7 @@ static int edit_todo_file(unsigned flags) if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error_errno(_("could not read '%s'."), todo_file); - strbuf_stripspace(&todo_list.buf, comment_line_char); + strbuf_stripspace(&todo_list.buf, comment_line_str); res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 7b700a9fb1..e5626e5126 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -13,7 +13,7 @@ static void comment_lines(struct strbuf *buf) size_t len; msg = strbuf_detach(buf, &len); - strbuf_add_commented_lines(buf, msg, len, comment_line_char); + strbuf_add_commented_lines(buf, msg, len, comment_line_str); free(msg); } @@ -59,7 +59,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS) strbuf_stripspace(&buf, - mode == STRIP_COMMENTS ? comment_line_char : '\0'); + mode == STRIP_COMMENTS ? comment_line_str : NULL); else comment_lines(&buf); diff --git a/builtin/tag.c b/builtin/tag.c index e2ff749832..9a33cb50b4 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -193,11 +193,11 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid, static const char tag_template[] = N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be ignored.\n"); + "Lines starting with '%s' will be ignored.\n"); static const char tag_template_nocleanup[] = N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be kept; you may remove them" + "Lines starting with '%s' will be kept; you may remove them" " yourself if you want to.\n"); static int git_tag_config(const char *var, const char *value, @@ -328,11 +328,11 @@ static void create_tag(const struct object_id *object, const char *object_ref, struct strbuf buf = STRBUF_INIT; strbuf_addch(&buf, '\n'); if (opt->cleanup_mode == CLEANUP_ALL) - strbuf_commented_addf(&buf, comment_line_char, - _(tag_template), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_str, + _(tag_template), tag, comment_line_str); else - strbuf_commented_addf(&buf, comment_line_char, - _(tag_template_nocleanup), tag, comment_line_char); + strbuf_commented_addf(&buf, comment_line_str, + _(tag_template_nocleanup), tag, comment_line_str); write_or_die(fd, buf.buf, buf.len); strbuf_release(&buf); } @@ -347,7 +347,7 @@ static void create_tag(const struct object_id *object, const char *object_ref, if (opt->cleanup_mode != CLEANUP_NONE) strbuf_stripspace(buf, - opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0'); + opt->cleanup_mode == CLEANUP_ALL ? comment_line_str : NULL); if (!opt->message_given && !buf->len) die(_("no tag message?")); diff --git a/builtin/worktree.c b/builtin/worktree.c index a20cc8820e..7c6c72536b 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -657,7 +657,7 @@ static int can_use_local_refs(const struct add_opts *opts) strbuf_add_real_path(&path, get_worktree_git_dir(NULL)); strbuf_addstr(&path, "/HEAD"); strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, 0); + strbuf_stripspace(&contents, NULL); strbuf_strip_suffix(&contents, "\n"); warning(_("HEAD points to an invalid (or orphaned) reference.\n" diff --git a/commit.c b/commit.c index 1da10ec916..1a479a997c 100644 --- a/commit.c +++ b/commit.c @@ -1928,7 +1928,8 @@ size_t ignored_log_message_bytes(const char *buf, size_t len) else next_line++; - if (buf[bol] == comment_line_char || buf[bol] == '\n') { + if (starts_with_mem(buf + bol, cutoff - bol, comment_line_str) || + buf[bol] == '\n') { /* is this the first of the run of comments? */ if (!boc) boc = bol; diff --git a/config.c b/config.c index dab92e581b..eebce8c7e0 100644 --- a/config.c +++ b/config.c @@ -1565,16 +1565,19 @@ static int git_default_core_config(const char *var, const char *value, if (!strcmp(var, "core.editor")) return git_config_string(&editor_program, var, value); - if (!strcmp(var, "core.commentchar")) { + if (!strcmp(var, "core.commentchar") || + !strcmp(var, "core.commentstring")) { if (!value) return config_error_nonbool(var); else if (!strcasecmp(value, "auto")) auto_comment_line_char = 1; - else if (value[0] && !value[1]) { - comment_line_char = value[0]; + else if (value[0]) { + if (strchr(value, '\n')) + return error(_("%s cannot contain newline"), var); + comment_line_str = xstrdup(value); auto_comment_line_char = 0; } else - return error(_("core.commentChar should only be one ASCII character")); + return error(_("%s must have at least one character"), var); return 0; } diff --git a/environment.c b/environment.c index 60706ea398..a73ba9c12c 100644 --- a/environment.c +++ b/environment.c @@ -110,7 +110,7 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT; * The character that begins a commented line in user-editable file * that is subject to stripspace. */ -char comment_line_char = '#'; +const char *comment_line_str = "#"; int auto_comment_line_char; /* Parallel index stat data preload? */ diff --git a/environment.h b/environment.h index 5cec19cecc..05fd94d7be 100644 --- a/environment.h +++ b/environment.h @@ -8,7 +8,7 @@ struct strvec; * The character that begins a commented line in user-editable file * that is subject to stripspace. */ -extern char comment_line_char; +extern const char *comment_line_str; extern int auto_comment_line_char; /* diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index 66e47449a0..ae201e21db 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -321,7 +321,7 @@ static void credit_people(struct strbuf *out, skip_prefix(me, them->items->string, &me) && starts_with(me, " <"))) return; - strbuf_addf(out, "\n%c %s ", comment_line_char, label); + strbuf_addf(out, "\n%s %s ", comment_line_str, label); add_people_count(out, them); } @@ -510,7 +510,7 @@ static void fmt_tag_signature(struct strbuf *tagbuf, if (sig->len) { strbuf_addch(tagbuf, '\n'); strbuf_add_commented_lines(tagbuf, sig->buf, sig->len, - comment_line_char); + comment_line_str); } } @@ -557,7 +557,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out) strbuf_add_commented_lines(&tagline, origins.items[first_tag].string, strlen(origins.items[first_tag].string), - comment_line_char); + comment_line_str); strbuf_insert(&tagbuf, 0, tagline.buf, tagline.len); strbuf_release(&tagline); @@ -566,7 +566,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out) strbuf_add_commented_lines(&tagbuf, origins.items[i].string, strlen(origins.items[i].string), - comment_line_char); + comment_line_str); fmt_tag_signature(&tagbuf, &sig, buf, len); } strbuf_release(&payload); diff --git a/gpg-interface.c b/gpg-interface.c index 95e764acb1..b5993385ff 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -586,8 +586,8 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc, } } - strbuf_stripspace(&ssh_keygen_out, '\0'); - strbuf_stripspace(&ssh_keygen_err, '\0'); + strbuf_stripspace(&ssh_keygen_out, NULL); + strbuf_stripspace(&ssh_keygen_err, NULL); /* Add stderr outputs to show the user actual ssh-keygen errors */ strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len); strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len); diff --git a/rebase-interactive.c b/rebase-interactive.c index d9718409b3..c343e16fcd 100644 --- a/rebase-interactive.c +++ b/rebase-interactive.c @@ -71,14 +71,14 @@ void append_todo_help(int command_count, if (!edit_todo) { strbuf_addch(buf, '\n'); - strbuf_commented_addf(buf, comment_line_char, + strbuf_commented_addf(buf, comment_line_str, Q_("Rebase %s onto %s (%d command)", "Rebase %s onto %s (%d commands)", command_count), shortrevisions, shortonto, command_count); } - strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char); + strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str); if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR) msg = _("\nDo not remove any line. Use 'drop' " @@ -87,7 +87,7 @@ void append_todo_help(int command_count, msg = _("\nIf you remove a line here " "THAT COMMIT WILL BE LOST.\n"); - strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char); + strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str); if (edit_todo) msg = _("\nYou are editing the todo file " @@ -98,7 +98,7 @@ void append_todo_help(int command_count, msg = _("\nHowever, if you remove everything, " "the rebase will be aborted.\n\n"); - strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char); + strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str); } int edit_todo_list(struct repository *r, struct todo_list *todo_list, @@ -130,7 +130,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list, if (launch_sequence_editor(todo_file, &new_todo->buf, NULL)) return -2; - strbuf_stripspace(&new_todo->buf, comment_line_char); + strbuf_stripspace(&new_todo->buf, comment_line_str); if (initial && new_todo->buf.len == 0) return -3; diff --git a/sequencer.c b/sequencer.c index 72ab2dd90a..e1bdd72bea 100644 --- a/sequencer.c +++ b/sequencer.c @@ -678,15 +678,15 @@ void append_conflicts_hint(struct index_state *istate, if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { strbuf_addch(msgbuf, '\n'); wt_status_append_cut_line(msgbuf); - strbuf_addch(msgbuf, comment_line_char); + strbuf_addstr(msgbuf, comment_line_str); } strbuf_addch(msgbuf, '\n'); - strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n"); + strbuf_commented_addf(msgbuf, comment_line_str, "Conflicts:\n"); for (i = 0; i < istate->cache_nr;) { const struct cache_entry *ce = istate->cache[i++]; if (ce_stage(ce)) { - strbuf_commented_addf(msgbuf, comment_line_char, + strbuf_commented_addf(msgbuf, comment_line_str, "\t%s\n", ce->name); while (i < istate->cache_nr && !strcmp(ce->name, istate->cache[i]->name)) @@ -1169,7 +1169,7 @@ void cleanup_message(struct strbuf *msgbuf, strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len)); if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE) strbuf_stripspace(msgbuf, - cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); + cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL); } /* @@ -1201,7 +1201,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file, return 0; strbuf_stripspace(&tmpl, - cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); + cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL); if (!skip_prefix(sb->buf, tmpl.buf, &start)) start = sb->buf; strbuf_release(&tmpl); @@ -1574,7 +1574,7 @@ static int try_to_commit(struct repository *r, if (cleanup != COMMIT_MSG_CLEANUP_NONE) strbuf_stripspace(msg, - cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0'); + cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL); if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) { res = 1; /* run 'git commit' to display error message */ goto out; @@ -1796,6 +1796,8 @@ static const char *command_to_string(const enum todo_command command) { if (command < TODO_COMMENT) return todo_command_info[command].str; + if (command == TODO_COMMENT) + return comment_line_str; die(_("unknown command: %d"), command); } @@ -1803,7 +1805,7 @@ static char command_to_char(const enum todo_command command) { if (command < TODO_COMMENT) return todo_command_info[command].c; - return comment_line_char; + return 0; } static int is_noop(const enum todo_command command) @@ -1857,7 +1859,7 @@ static int is_fixup_flag(enum todo_command command, unsigned flag) static void add_commented_lines(struct strbuf *buf, const void *str, size_t len) { const char *s = str; - while (len > 0 && s[0] == comment_line_char) { + while (starts_with_mem(s, len, comment_line_str)) { size_t count; const char *n = memchr(s, '\n', len); if (!n) @@ -1868,7 +1870,7 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len) s += count; len -= count; } - strbuf_add_commented_lines(buf, s, len, comment_line_char); + strbuf_add_commented_lines(buf, s, len, comment_line_str); } /* Does the current fixup chain contain a squash command? */ @@ -1963,11 +1965,11 @@ static int append_squash_message(struct strbuf *buf, const char *body, (starts_with(body, "squash!") || starts_with(body, "fixup!")))) commented_len = commit_subject_length(body); - strbuf_addf(buf, "\n%c ", comment_line_char); + strbuf_addf(buf, "\n%s ", comment_line_str); strbuf_addf(buf, _(nth_commit_msg_fmt), ++opts->current_fixup_count + 1); strbuf_addstr(buf, "\n\n"); - strbuf_add_commented_lines(buf, body, commented_len, comment_line_char); + strbuf_add_commented_lines(buf, body, commented_len, comment_line_str); /* buf->buf may be reallocated so store an offset into the buffer */ fixup_off = buf->len; strbuf_addstr(buf, body + commented_len); @@ -2020,10 +2022,10 @@ static int update_squash_messages(struct repository *r, return error(_("could not read '%s'"), rebase_path_squash_msg()); - eol = buf.buf[0] != comment_line_char ? + eol = !starts_with(buf.buf, comment_line_str) ? buf.buf : strchrnul(buf.buf, '\n'); - strbuf_addf(&header, "%c ", comment_line_char); + strbuf_addf(&header, "%s ", comment_line_str); strbuf_addf(&header, _(combined_commit_msg_fmt), opts->current_fixup_count + 2); strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len); @@ -2049,16 +2051,16 @@ static int update_squash_messages(struct repository *r, repo_unuse_commit_buffer(r, head_commit, head_message); return error(_("cannot write '%s'"), rebase_path_fixup_msg()); } - strbuf_addf(&buf, "%c ", comment_line_char); + strbuf_addf(&buf, "%s ", comment_line_str); strbuf_addf(&buf, _(combined_commit_msg_fmt), 2); - strbuf_addf(&buf, "\n%c ", comment_line_char); + strbuf_addf(&buf, "\n%s ", comment_line_str); strbuf_addstr(&buf, is_fixup_flag(command, flag) ? _(skip_first_commit_msg_str) : _(first_commit_msg_str)); strbuf_addstr(&buf, "\n\n"); if (is_fixup_flag(command, flag)) strbuf_add_commented_lines(&buf, body, strlen(body), - comment_line_char); + comment_line_str); else strbuf_addstr(&buf, body); @@ -2073,12 +2075,12 @@ static int update_squash_messages(struct repository *r, if (command == TODO_SQUASH || is_fixup_flag(command, flag)) { res = append_squash_message(&buf, body, command, opts, flag); } else if (command == TODO_FIXUP) { - strbuf_addf(&buf, "\n%c ", comment_line_char); + strbuf_addf(&buf, "\n%s ", comment_line_str); strbuf_addf(&buf, _(skip_nth_commit_msg_fmt), ++opts->current_fixup_count + 1); strbuf_addstr(&buf, "\n\n"); strbuf_add_commented_lines(&buf, body, strlen(body), - comment_line_char); + comment_line_str); } else return error(_("unknown command: %d"), command); repo_unuse_commit_buffer(r, commit, message); @@ -2579,7 +2581,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item, /* left-trim */ bol += strspn(bol, " \t"); - if (bol == eol || *bol == '\r' || *bol == comment_line_char) { + if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) { item->command = TODO_COMMENT; item->commit = NULL; item->arg_offset = bol - buf; @@ -5682,8 +5684,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, oid_to_hex(&commit->object.oid), oneline.buf); if (is_empty) - strbuf_addf(&buf, " %c empty", - comment_line_char); + strbuf_addf(&buf, " %s empty", + comment_line_str); FLEX_ALLOC_STR(entry, string, buf.buf); oidcpy(&entry->entry.oid, &commit->object.oid); @@ -5773,7 +5775,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, entry = oidmap_get(&state.commit2label, &commit->object.oid); if (entry) - strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string); + strbuf_addf(out, "\n%s Branch %s\n", comment_line_str, entry->string); else strbuf_addch(out, '\n'); @@ -5910,7 +5912,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, oid_to_hex(&commit->object.oid)); pretty_print_commit(&pp, commit, out); if (is_empty) - strbuf_addf(out, " %c empty", comment_line_char); + strbuf_addf(out, " %s empty", comment_line_str); strbuf_addch(out, '\n'); } if (skipped_commit) diff --git a/strbuf.c b/strbuf.c index 449eb610f1..1492a08225 100644 --- a/strbuf.c +++ b/strbuf.c @@ -24,6 +24,17 @@ int istarts_with(const char *str, const char *prefix) return 0; } +int starts_with_mem(const char *str, size_t len, const char *prefix) +{ + const char *end = str + len; + for (; ; str++, prefix++) { + if (!*prefix) + return 1; + else if (str == end || *str != *prefix) + return 0; + } +} + int skip_to_optional_arg_default(const char *str, const char *prefix, const char **arg, const char *def) { @@ -340,18 +351,17 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) } static void add_lines(struct strbuf *out, - const char *prefix1, - const char *prefix2, - const char *buf, size_t size) + const char *prefix, + const char *buf, size_t size, + int space_after_prefix) { while (size) { - const char *prefix; const char *next = memchr(buf, '\n', size); next = next ? (next + 1) : (buf + size); - prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t')) - ? prefix2 : prefix1); strbuf_addstr(out, prefix); + if (space_after_prefix && buf[0] != '\n' && buf[0] != '\t') + strbuf_addch(out, ' '); strbuf_add(out, buf, next - buf); size -= next - buf; buf = next; @@ -360,19 +370,12 @@ static void add_lines(struct strbuf *out, } void strbuf_add_commented_lines(struct strbuf *out, const char *buf, - size_t size, char comment_line_char) + size_t size, const char *comment_prefix) { - static char prefix1[3]; - static char prefix2[2]; - - if (prefix1[0] != comment_line_char) { - xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char); - xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char); - } - add_lines(out, prefix1, prefix2, buf, size); + add_lines(out, comment_prefix, buf, size, 1); } -void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, +void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix, const char *fmt, ...) { va_list params; @@ -383,7 +386,7 @@ void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, strbuf_vaddf(&buf, fmt, params); va_end(params); - strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_line_char); + strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_prefix); if (incomplete_line) sb->buf[--sb->len] = '\0'; @@ -770,7 +773,7 @@ ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) void strbuf_add_lines(struct strbuf *out, const char *prefix, const char *buf, size_t size) { - add_lines(out, prefix, NULL, buf, size); + add_lines(out, prefix, buf, size, 0); } void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) @@ -1025,10 +1028,10 @@ static size_t cleanup(char *line, size_t len) * * If last line does not have a newline at the end, one is added. * - * Pass a non-NUL comment_line_char to skip every line starting + * Pass a non-NULL comment_prefix to skip every line starting * with it. */ -void strbuf_stripspace(struct strbuf *sb, char comment_line_char) +void strbuf_stripspace(struct strbuf *sb, const char *comment_prefix) { size_t empties = 0; size_t i, j, len, newlen; @@ -1041,8 +1044,8 @@ void strbuf_stripspace(struct strbuf *sb, char comment_line_char) eol = memchr(sb->buf + i, '\n', sb->len - i); len = eol ? eol - (sb->buf + i) + 1 : sb->len - i; - if (comment_line_char && len && - sb->buf[i] == comment_line_char) { + if (comment_prefix && len && + starts_with(sb->buf + i, comment_prefix)) { newlen = 0; continue; } diff --git a/strbuf.h b/strbuf.h index c758de3729..97fa4a3d01 100644 --- a/strbuf.h +++ b/strbuf.h @@ -288,7 +288,7 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, */ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size, - char comment_line_char); + const char *comment_prefix); /** @@ -384,7 +384,7 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...); * blank to the buffer. */ __attribute__((format (printf, 3, 4))) -void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, const char *fmt, ...); +void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix, const char *fmt, ...); __attribute__((format (printf,2,0))) void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); @@ -518,11 +518,11 @@ int strbuf_getcwd(struct strbuf *sb); int strbuf_normalize_path(struct strbuf *sb); /** - * Strip whitespace from a buffer. If comment_line_char is non-NUL, + * Strip whitespace from a buffer. If comment_prefix is non-NULL, * then lines beginning with that character are considered comments, * thus removed. */ -void strbuf_stripspace(struct strbuf *buf, char comment_line_char); +void strbuf_stripspace(struct strbuf *buf, const char *comment_prefix); static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix) { @@ -678,6 +678,7 @@ char *xstrfmt(const char *fmt, ...); int starts_with(const char *str, const char *prefix); int istarts_with(const char *str, const char *prefix); +int starts_with_mem(const char *str, size_t len, const char *prefix); /* * If the string "str" is the same as the string in "prefix", then the "arg" diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index d1b3be8725..f10f42ff1e 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -401,6 +401,21 @@ test_expect_success 'strip comments with changed comment char' ' test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)" ' +test_expect_success 'strip comments with changed comment string' ' + test ! -z "$(echo "// comment" | git -c core.commentchar=// stripspace)" && + test -z "$(echo "// comment" | git -c core.commentchar="//" stripspace -s)" +' + +test_expect_success 'newline as commentchar is forbidden' ' + test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err && + grep "core.commentchar cannot contain newline" err +' + +test_expect_success 'empty commentchar is forbidden' ' + test_must_fail git -c core.commentchar= stripspace -s 2>err && + grep "core.commentchar must have at least one character" err +' + test_expect_success '-c with single line' ' printf "# foo\n" >expect && printf "foo" | git stripspace -c >actual && diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh index c3281b192e..4c7db19ce7 100755 --- a/t/t7507-commit-verbose.sh +++ b/t/t7507-commit-verbose.sh @@ -101,6 +101,16 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' ' test_grep "Aborting commit due to empty commit message." err ' +test_expect_success 'verbose diff is stripped with multi-byte comment char' ' + ( + GIT_EDITOR=cat && + export GIT_EDITOR && + test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err + ) && + grep "^foo> " out && + test_grep "Aborting commit due to empty commit message." err +' + test_expect_success 'status does not verbose without --verbose' ' git status >actual && ! grep "^diff --git" actual diff --git a/t/t7508-status.sh b/t/t7508-status.sh index e9afa59968..773383fefb 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1415,7 +1415,9 @@ test_expect_success "status (core.commentchar with submodule summary)" ' test_expect_success "status (core.commentchar with two chars with submodule summary)" ' test_config core.commentchar ";;" && - test_must_fail git -c status.displayCommentPrefix=true status + sed "s/^/;/" expect.double && + git -c status.displayCommentPrefix=true status >output && + test_cmp expect.double output ' test_expect_success "--ignore-submodules=all suppresses submodule summary" ' diff --git a/trailer.c b/trailer.c index 57b4aa7d5a..dc15d850b4 100644 --- a/trailer.c +++ b/trailer.c @@ -871,7 +871,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len) /* The first paragraph is the title and cannot be trailers */ for (s = buf; s < buf + len; s = next_line(s)) { - if (s[0] == comment_line_char) + if (starts_with_mem(s, buf + len - s, comment_line_str)) continue; if (is_blank_line(s)) break; @@ -891,7 +891,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len) const char **p; ssize_t separator_pos; - if (bol[0] == comment_line_char) { + if (starts_with_mem(bol, buf + len - bol, comment_line_str)) { non_trailer_lines += possible_continuation_lines; possible_continuation_lines = 0; continue; @@ -1002,7 +1002,7 @@ void parse_trailers(const struct process_trailer_options *opts, for (i = 0; i < info->trailer_nr; i++) { int separator_pos; char *trailer = info->trailers[i]; - if (trailer[0] == comment_line_char) + if (starts_with(trailer, comment_line_str)) continue; separator_pos = find_separator(trailer, separators); if (separator_pos >= 1) { diff --git a/wt-status.c b/wt-status.c index 2db4bb3a12..bdfc23e2ae 100644 --- a/wt-status.c +++ b/wt-status.c @@ -70,7 +70,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_vaddf(&sb, fmt, ap); if (!sb.len) { if (s->display_comment_prefix) { - strbuf_addch(&sb, comment_line_char); + strbuf_addstr(&sb, comment_line_str); if (!trail) strbuf_addch(&sb, ' '); } @@ -85,7 +85,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color, strbuf_reset(&linebuf); if (at_bol && s->display_comment_prefix) { - strbuf_addch(&linebuf, comment_line_char); + strbuf_addstr(&linebuf, comment_line_str); if (*line != '\n' && *line != '\t') strbuf_addch(&linebuf, ' '); } @@ -1028,7 +1028,7 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom if (s->display_comment_prefix) { size_t len; summary_content = strbuf_detach(&summary, &len); - strbuf_add_commented_lines(&summary, summary_content, len, comment_line_char); + strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str); free(summary_content); } @@ -1090,7 +1090,7 @@ size_t wt_status_locate_end(const char *s, size_t len) const char *p; struct strbuf pattern = STRBUF_INIT; - strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line); + strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line); if (starts_with(s, pattern.buf + 1)) len = 0; else if ((p = strstr(s, pattern.buf))) { @@ -1106,8 +1106,8 @@ void wt_status_append_cut_line(struct strbuf *buf) { const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored."); - strbuf_commented_addf(buf, comment_line_char, "%s", cut_line); - strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char); + strbuf_commented_addf(buf, comment_line_str, "%s", cut_line); + strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str); } void wt_status_add_cut_line(struct wt_status *s) @@ -1183,8 +1183,6 @@ static void wt_longstatus_print_tracking(struct wt_status *s) struct strbuf sb = STRBUF_INIT; const char *cp, *ep, *branch_name; struct branch *branch; - char comment_line_string[3]; - int i; uint64_t t_begin = 0; assert(s->branch && !s->is_initial); @@ -1209,20 +1207,15 @@ static void wt_longstatus_print_tracking(struct wt_status *s) } } - i = 0; - if (s->display_comment_prefix) { - comment_line_string[i++] = comment_line_char; - comment_line_string[i++] = ' '; - } - comment_line_string[i] = '\0'; - for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1) color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), - "%s%.*s", comment_line_string, + "%s%s%.*s", + s->display_comment_prefix ? comment_line_str : "", + s->display_comment_prefix ? " " : "", (int)(ep - cp), cp); if (s->display_comment_prefix) - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", - comment_line_char); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s", + comment_line_str); else fputs("\n", s->fp); strbuf_release(&sb); @@ -1389,7 +1382,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines) git_path("%s", fname)); } while (!strbuf_getline_lf(&line, f)) { - if (line.len && line.buf[0] == comment_line_char) + if (starts_with(line.buf, comment_line_str)) continue; strbuf_trim(&line); if (!line.len)