rerere forget: do not segfault if not all stages are present

The loop that fills in the buffers that are later passed to the merge driver exits early when not all stages of a path are present in the index. But since the buffer pointers are not initialized in advance, the subsequent accesses are undefined. Initialize buffer pointers in advance to avoid undefined behavior later. That is not sufficient, though, to get correct operation of handle_cache(). The function replays a conflicted merge to extract the part inside the conflict markers. As written, the loop exits early when a stage is missing. Consequently, the buffers for later stages that would be present in the index are not filled in and the merge is replayed with incomplete data. Fix it by investigating all stages of the given path. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Sixt committed Apr 4, 2013 at 20:41 UTC b9e31f59478eeae3e4230308f6fc06713c6fc547
2 files changed +20 -8
rerere.c
+7 -8
@@ -297,7 +297,7 @@ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
297
298 static int handle_cache(const char *path, unsigned char *sha1, const char *output)
299 {
300 - mmfile_t mmfile[3];
300 + mmfile_t mmfile[3] = {{NULL}};
301 mmbuffer_t result = {NULL, 0};
302 struct cache_entry *ce;
303 int pos, len, i, hunk_no;
@@ -316,17 +316,16 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
316 for (i = 0; i < 3; i++) {
317 enum object_type type;
318 unsigned long size;
319 + int j;
320
320 - mmfile[i].size = 0;
321 - mmfile[i].ptr = NULL;
321 if (active_nr <= pos)
322 break;
323 ce = active_cache[pos++];
325 - if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
326 - || ce_stage(ce) != i + 1)
327 - break;
328 - mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
329 - mmfile[i].size = size;
324 + if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
325 + continue;
326 + j = ce_stage(ce) - 1;
327 + mmfile[j].ptr = read_sha1_file(ce->sha1, &type, &size);
328 + mmfile[j].size = size;
329 }
330 for (i = 0; i < 3; i++) {
331 if (!mmfile[i].ptr && !mmfile[i].size)
t/t2030-unresolve-info.sh
+13
@@ -54,8 +54,11 @@ test_expect_success setup '
54 test_commit second fi/le second &&
55 git checkout side &&
56 test_commit third fi/le third &&
57 + git branch add-add &&
58 git checkout another &&
59 test_commit fourth fi/le fourth &&
60 + git checkout add-add &&
61 + test_commit fifth add-differently &&
62 git checkout master
63 '
64
@@ -179,4 +182,14 @@ test_expect_success 'rerere forget (binary)' '
182 git rerere forget binary
183 '
184
185 +test_expect_success 'rerere forget (add-add conflict)' '
186 + git checkout -f master &&
187 + echo master >add-differently &&
188 + git add add-differently &&
189 + git commit -m "add differently" &&
190 + test_must_fail git merge fifth &&
191 + git rerere forget add-differently 2>actual &&
192 + test_i18ngrep "no remembered" actual
193 +'
194 +
195 test_done