From: Junio C Hamano <gitster@pobox.com>
To: git@vger.kernel.org
Subject: [PATCH 1/4] merge-ll: consolidate conflict marker scanning logic
Date: Tue, 28 Jul 2026 14:52:16 -0700 [thread overview]
Message-ID: <20260728215219.753678-2-gitster@pobox.com> (raw)
In-Reply-To: <20260728215219.753678-1-gitster@pobox.com>
The diff.c:is_conflict_marker() and rerere.c:is_cmarker() functions
implement duplicate logic for identifying conflict marker lines
(lines that begin with a run of '<', '=', '>', and '|' characters).
diff.c's original version from 049540435f (diff --check: detect
leftover conflict markers, 2008-06-26) accepts any whitespace (such
as a newline) immediately following '<<<<<<<' and '>>>>>>>', whereas
rerere.c's version from 191f241717 (rerere: prepare for customizable
conflict marker length, 2010-01-16) strictly requires a space
character (' ') after them.
Implement is_conflict_marker_line() in merge-ll.c to serve as a
replacement for both, and update diff.c and rerere.c to use the new
helper. The unified helper intentionally adopts rerere's stricter
rule, as the conflicts generated by Git always show the "ours" and
"theirs" labels after these markers separated by a space.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
diff.c | 25 +------------------------
merge-ll.c | 31 +++++++++++++++++++++++++++++++
merge-ll.h | 1 +
rerere.c | 38 ++++++--------------------------------
4 files changed, 39 insertions(+), 56 deletions(-)
diff --git a/diff.c b/diff.c
index 589c1969e4..cfe515af4e 100644
--- a/diff.c
+++ b/diff.c
@@ -3519,29 +3519,6 @@ struct checkdiff_t {
int last_line_kind;
};
-static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
-{
- char firstchar;
- int cnt;
-
- if (len < marker_size + 1)
- return 0;
- firstchar = line[0];
- switch (firstchar) {
- case '=': case '>': case '<': case '|':
- break;
- default:
- return 0;
- }
- for (cnt = 1; cnt < marker_size; cnt++)
- if (line[cnt] != firstchar)
- return 0;
- /* line[1] through line[marker_size-1] are same as firstchar */
- if (len < marker_size + 1 || !isspace(line[marker_size]))
- return 0;
- return 1;
-}
-
static void checkdiff_consume_hunk(void *priv,
long ob UNUSED, long on UNUSED,
long nb, long nn UNUSED,
@@ -3571,7 +3548,7 @@ static int checkdiff_consume(void *priv, char *line, unsigned long len)
if (line[0] == '+') {
unsigned bad;
data->lineno++;
- if (is_conflict_marker(line + 1, marker_size, len - 1)) {
+ if (is_conflict_marker_line(line + 1, len - 1, marker_size)) {
data->status |= 1;
fprintf(data->o->file,
"%s%s:%d: leftover conflict marker\n",
diff --git a/merge-ll.c b/merge-ll.c
index fafe2c9197..41c97fb90a 100644
--- a/merge-ll.c
+++ b/merge-ll.c
@@ -468,3 +468,34 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
}
return marker_size;
}
+
+int is_conflict_marker_line(const char *line, unsigned long len, int marker_size)
+{
+ char firstchar;
+ int cnt;
+
+ if (len < marker_size + 1)
+ return 0;
+
+ firstchar = line[0];
+ switch (firstchar) {
+ case '=': case '>': case '<': case '|':
+ break;
+ default:
+ return 0;
+ }
+
+ for (cnt = 1; cnt < marker_size; cnt++) {
+ if (line[cnt] != firstchar)
+ return 0;
+ }
+
+ if (((firstchar == '<') || (firstchar == '>')) &&
+ line[marker_size] != ' ')
+ return 0;
+
+ if (!isspace((unsigned char)line[marker_size]))
+ return 0;
+
+ return firstchar;
+}
diff --git a/merge-ll.h b/merge-ll.h
index d038ee0c1e..b348aee15d 100644
--- a/merge-ll.h
+++ b/merge-ll.h
@@ -109,6 +109,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
const struct ll_merge_options *opts);
int ll_merge_marker_size(struct index_state *istate, const char *path);
+int is_conflict_marker_line(const char *line, unsigned long len, int marker_size);
void reset_merge_attributes(void);
#endif
diff --git a/rerere.c b/rerere.c
index 1dda246098..4b05850479 100644
--- a/rerere.c
+++ b/rerere.c
@@ -331,33 +331,6 @@ static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
return strbuf_getwholeline(sb, io->input, '\n');
}
-/*
- * Require the exact number of conflict marker letters, no more, no
- * less, followed by SP or any whitespace
- * (including LF).
- */
-static int is_cmarker(char *buf, int marker_char, int marker_size)
-{
- int want_sp;
-
- /*
- * The beginning of our version and the end of their version
- * always are labeled like "<<<<< ours" or ">>>>> theirs",
- * hence we set want_sp for them. Note that the version from
- * the common ancestor in diff3-style output is not always
- * labelled (e.g. "||||| common" is often seen but "|||||"
- * alone is also valid), so we do not set want_sp.
- */
- want_sp = (marker_char == '<') || (marker_char == '>');
-
- while (marker_size--)
- if (*buf++ != marker_char)
- return 0;
- if (want_sp && *buf != ' ')
- return 0;
- return isspace(*buf);
-}
-
static void rerere_strbuf_putconflict(struct strbuf *buf, int ch, size_t size)
{
strbuf_addchars(buf, ch, size);
@@ -375,7 +348,8 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
int has_conflicts = -1;
while (!io->getline(&buf, io)) {
- if (is_cmarker(buf.buf, '<', marker_size)) {
+ int marker = is_conflict_marker_line(buf.buf, buf.len, marker_size);
+ if (marker == '<') {
if (handle_conflict(&conflict, io, marker_size, NULL) < 0)
break;
if (hunk == RR_SIDE_1)
@@ -383,15 +357,15 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,
else
strbuf_addbuf(&two, &conflict);
strbuf_release(&conflict);
- } else if (is_cmarker(buf.buf, '|', marker_size)) {
+ } else if (marker == '|') {
if (hunk != RR_SIDE_1)
break;
hunk = RR_ORIGINAL;
- } else if (is_cmarker(buf.buf, '=', marker_size)) {
+ } else if (marker == '=') {
if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
break;
hunk = RR_SIDE_2;
- } else if (is_cmarker(buf.buf, '>', marker_size)) {
+ } else if (marker == '>') {
if (hunk != RR_SIDE_2)
break;
if (strbuf_cmp(&one, &two) > 0)
@@ -442,7 +416,7 @@ static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_siz
git_hash_init(&ctx, the_hash_algo);
while (!io->getline(&buf, io)) {
- if (is_cmarker(buf.buf, '<', marker_size)) {
+ if (is_conflict_marker_line(buf.buf, buf.len, marker_size) == '<') {
has_conflicts = handle_conflict(&out, io, marker_size,
hash ? &ctx : NULL);
if (has_conflicts < 0)
--
2.55.0-594-g42d2bf033e
next prev parent reply other threads:[~2026-07-28 21:52 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-28 21:52 [PATCH 0/4] git add --resolved Junio C Hamano
2026-07-28 21:52 ` Junio C Hamano [this message]
2026-07-28 21:52 ` [PATCH 2/4] read-cache: add remove_file_from_index_with_flags() Junio C Hamano
2026-07-28 21:52 ` [PATCH 3/4] add: introduce '--resolved' option Junio C Hamano
2026-07-29 3:28 ` Michael Montalbo
2026-07-28 21:52 ` [PATCH 4/4] read-cache: reindent Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260728215219.753678-2-gitster@pobox.com \
--to=gitster@pobox.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.