git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [BUG] `rerere remaining` skips consecutive conflicted paths

From
Junio C Hamano <gitster@pobox.com>
Date
Sep 18, 2026, 12:30 UTC
Message-ID
<xmqqwlsioi6c.fsf@gitster.g>
In-Reply-To
<32062ff9-6dfc-4452-b8f3-66881c3957cd@peda.net>
Mikko Rantalainen <mikko.rantalainen@peda.net> writes:
Show 26 quoted lines
> The issue is probably  caused by `check_one_conflict()` in `rerere.c.
> There is currently a loop of the form:
>
> ```
> *type = PUNTED;
> while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
>          i++;
> ```
>
> According to ChatGPT, this is probably intended to skip multiple stage-1
> entries belonging to the same conflicted pathname, but it also skips a
> stage-1 entry belonging to the next pathname.
>
> The loop may need an additional same-path check, maybe
> something like:
>
> ```
> while (i < istate->cache_nr &&
>         ce_stage(istate->cache[i]) == 1 &&
>         ce_same_name(e, istate->cache[i]))
>          i++;
> ```
>
> I have not checked whether `ce_same_name()` is necessarily the
> preferred helper here, so this is only a possible fix rather than
> a proposed patch.

Spot on, I would say, even though I find that it is a bit iffy for the merge machinery to leave a "delete-delete" conflict in the first place.

The idea of that function is to return for the current path if we (1) don't need to do anything as it is cleanly resolved (RESOLVED), (2) know it is conflicting but we cannot handle (PUNTED), or (3) know it is conflicting and we are willing to handle (THREE_STAGED).

For (1), we only need to see that the current entry is resolved (because in istate->cache[], resolved entry for a single path appears only once) and return, telling the caller that we consumed only one entry. For THREE_STAGED, we would want to see a stage 2 (i.e., ours) entry followed by a stage 3 (i.e., theirs) entry, and the way the code does so is to skip over stage 1 entries for the same path, and we must see stage 2 and then stage 3 entries after that. Again in istate->cache[], by definition more than one stage 2 entries (i.e., "ours") cannot exist for a single path, so we check if the first entry after skipping over the stage 1 entries (i.e., "common") is a stage 2 entry and immediately after that is a stage 3 entry, and the stage 3 entry has the same name as the first entry we started looking at upon entry to the function. And to conclude one iteration, we skip the entries of the same name at the end.

And as you pointed out, the same "must be the same name" check must be done also while we are skipping over stage 1 entries. If you have a sequence of stage 1 entries for different paths, all of them would probably be skipped over at once.

Note that the low-level merge machinery and rerere machinery are both prepared to see multiple stage #1 and stage #3 entries for a same path, even though multiple stage #0 and stage #2 entries is a sign of index corruption. The "resolve" merge strategy will use multiple stage #1 entries when dealing with a criss-cross merges, where multiple merge-bases exist. Being prepared for multiple stage #3 entries is purely for philosophical consistency---an Octopus merge ought to be representing more than one "their" branches as stage #3 entries, even though the current implementation of octopus merge of N branches happens to do N pair-wise merges and do not require multiple stage #3 entries.

 rerere.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git c/rerere.c w/rerere.c
index 1c3745d9e3..296f254c1e 100644
--- c/rerere.c
+++ w/rerere.c
@@ -499,7 +499,11 @@ static int check_one_conflict(struct index_state *istate, int i, int *type)
 	}
 
 	*type = PUNTED;
-	while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
+
+	/* First ignore stage #1 entries */
+	while (i < istate->cache_nr &&
+	       ce_same_name(e, istate->cache[i]) &&
+	       ce_stage(istate->cache[i]) == 1)
 		i++;
 
 	/* Only handle regular files with both stages #2 and #3 */
Previous: Mikko Rantalainen
Message 2 of 2 in “[BUG] `rerere remaining` skips consecutive conflicted paths”
  1. Mikko RantalainenSep 17, 2026
  2. Junio C HamanoSep 18, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.