Skip to content

Commit 93406a2

Browse files
tgummerergitster
authored andcommitted
rerere: fix crash with files rerere can't handle
Currently when a user does a conflict resolution and ends it (in any way that calls 'git rerere' again) with a file 'rerere' can't handle, subsequent rerere operations that are interested in that path, such as 'rerere clear' or 'rerere forget <path>' will fail, or even worse in the case of 'rerere clear' segfault. Such states include nested conflicts, or a conflict marker that doesn't have any match. This is because 'git rerere' calculates a conflict file and writes it to the MERGE_RR file. When the user then changes the file in any way rerere can't handle, and then calls 'git rerere' on it again to record the conflict resolution, the handle_file function fails, and removes the 'preimage' file in the rr-cache in the process, while leaving the ID in the MERGE_RR file. Now when 'rerere clear' is run, it reads the ID from the MERGE_RR file, however the 'fit_variant' function for the ID is never called as the 'preimage' file does not exist anymore. This means 'collection->status' in 'has_rerere_resolution' is NULL, and the command will crash. To fix this, remove the rerere ID from the MERGE_RR file in the case when we can't handle it, just after the 'preimage' file was removed and remove the corresponding variant from .git/rr-cache/. Removing it unconditionally is fine here, because if the user would have resolved the conflict and ran rerere, the entry would no longer be in the MERGE_RR file, so we wouldn't have this problem in the first place, while if the conflict was not resolved. Currently there is nothing left in this folder, as the 'preimage' was already deleted by the 'handle_file' function, so 'remove_variant' is a no-op. Still call the function, to make sure we clean everything up, in case we add some other files corresponding to a variant in the future. Note that other variants that have the same conflict ID will not be touched. Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent fb90dca commit 93406a2

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

rerere.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -823,18 +823,20 @@ static int do_plain_rerere(struct string_list *rr, int fd)
823823
struct rerere_id *id;
824824
unsigned char sha1[20];
825825
const char *path = conflict.items[i].string;
826-
int ret;
827-
828-
if (string_list_has_string(rr, path))
829-
continue;
826+
int ret, has_string;
830827

831828
/*
832829
* Ask handle_file() to scan and assign a
833830
* conflict ID. No need to write anything out
834831
* yet.
835832
*/
836833
ret = handle_file(path, sha1, NULL);
837-
if (ret < 1)
834+
has_string = string_list_has_string(rr, path);
835+
if (ret < 0 && has_string) {
836+
remove_variant(string_list_lookup(rr, path)->util);
837+
string_list_remove(rr, path, 1);
838+
}
839+
if (ret < 1 || has_string)
838840
continue;
839841

840842
id = new_rerere_id(sha1);

t/t4200-rerere.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,4 +580,25 @@ test_expect_success 'multiple identical conflicts' '
580580
count_pre_post 0 0
581581
'
582582

583+
test_expect_success 'rerere with unexpected conflict markers does not crash' '
584+
git reset --hard &&
585+
586+
git checkout -b branch-1 master &&
587+
echo "bar" >test &&
588+
git add test &&
589+
git commit -q -m two &&
590+
591+
git reset --hard &&
592+
git checkout -b branch-2 master &&
593+
echo "foo" >test &&
594+
git add test &&
595+
git commit -q -a -m one &&
596+
597+
test_must_fail git merge branch-1 &&
598+
echo "<<<<<<< a" >test &&
599+
git rerere &&
600+
601+
git rerere clear
602+
'
603+
583604
test_done

0 commit comments

Comments
 (0)