From 742d8eb897b0521bb42cb7ebfff6cdacd5a9f6dc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Sep 2026 17:52:33 +0000 Subject: [PATCH] rerere: do not record failed conflict resolution data `rerere` can mark a conflict variant as resolved even when writing its preimage or postimage fails. A later invocation may then replay incomplete data from the cache, turning a local filesystem failure into an incorrect working-tree change. 629716d256a7 (rerere: do use multiple variants, 2015-07-30) introduced the code paths without checks for those I/O results. Treat such failures as failures, report them, and leave the rerere status unchanged unless the corresponding data was recorded successfully. The defect has been latent since 2015. Git for Windows' Coverity run only reported it after merging v2.56.0-rc0, for reasons that could not be figured out in a reasonable amount of time. Assisted-by: GPT-5.6 Luna Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- rerere.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/rerere.c b/rerere.c index 1c3745d9e3..45bbe6ab2c 100644 --- a/rerere.c +++ b/rerere.c @@ -476,8 +476,11 @@ static int handle_file(struct index_state *istate, unlink_or_warn(output); return error(_("could not parse conflict hunks in '%s'"), path); } - if (io.io.wrerror) + if (io.io.wrerror) { + if (output) + unlink_or_warn(output); return -1; + } return has_conflicts; } @@ -729,8 +732,25 @@ static void do_rerere_one_path(struct index_state *istate, /* Has the user resolved it already? */ if (variant >= 0) { - if (!handle_file(istate, path, NULL, NULL)) { - copy_file(the_repository, rerere_path(&buf, id, "postimage"), path, 0666); + int ret = handle_file(istate, path, NULL, NULL); + + if (ret < 0) + goto out; + if (!ret) { + const int had_postimage = + id->collection->status[variant] & RR_HAS_POSTIMAGE; + const char *postimage = + rerere_path(&buf, id, "postimage"); + + if (copy_file(the_repository, + postimage, + path, 0666)) { + if (!had_postimage) + unlink_or_warn(postimage); + error_errno(_("could not copy resolution for '%s'"), + path); + goto out; + } id->collection->status[variant] |= RR_HAS_POSTIMAGE; fprintf_ln(stderr, _("Recorded resolution for '%s'."), path); free_rerere_id(rr_item); @@ -778,7 +798,9 @@ static void do_rerere_one_path(struct index_state *istate, assign_variant(id); variant = id->variant; - handle_file(istate, path, NULL, rerere_path(&buf, id, "preimage")); + if (handle_file(istate, path, NULL, + rerere_path(&buf, id, "preimage")) < 0) + goto out; if (id->collection->status[variant] & RR_HAS_POSTIMAGE) { const char *path = rerere_path(&buf, id, "postimage"); if (unlink(path))