Skip to content

Commit 8fdcf31

Browse files
committed
checkout: do not check out unmerged higher stages randomly
During a conflicted merge when you have unmerged stages for a path F in the index, if you said: $ git checkout F we rewrote F as many times as we have stages for it, and the last one (typically "theirs") was left in the work tree, without resolving the conflict. This fixes it by noticing that a specified pathspec pattern matches an unmerged path, and by erroring out. Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 53d1589 commit 8fdcf31

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

builtin-checkout.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
7676
return 0;
7777
}
7878

79+
static int skip_same_name(struct cache_entry *ce, int pos)
80+
{
81+
while (++pos < active_nr &&
82+
!strcmp(active_cache[pos]->name, ce->name))
83+
; /* skip */
84+
return pos;
85+
}
86+
87+
7988
static int checkout_paths(struct tree *source_tree, const char **pathspec)
8089
{
8190
int pos;
@@ -107,14 +116,32 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
107116
if (report_path_error(ps_matched, pathspec, 0))
108117
return 1;
109118

119+
/* Any unmerged paths? */
120+
for (pos = 0; pos < active_nr; pos++) {
121+
struct cache_entry *ce = active_cache[pos];
122+
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
123+
if (!ce_stage(ce))
124+
continue;
125+
errs = 1;
126+
error("path '%s' is unmerged", ce->name);
127+
pos = skip_same_name(ce, pos) - 1;
128+
}
129+
}
130+
if (errs)
131+
return 1;
132+
110133
/* Now we are committed to check them out */
111134
memset(&state, 0, sizeof(state));
112135
state.force = 1;
113136
state.refresh_cache = 1;
114137
for (pos = 0; pos < active_nr; pos++) {
115138
struct cache_entry *ce = active_cache[pos];
116139
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
117-
errs |= checkout_entry(ce, &state, NULL);
140+
if (!ce_stage(ce)) {
141+
errs |= checkout_entry(ce, &state, NULL);
142+
continue;
143+
}
144+
pos = skip_same_name(ce, pos) - 1;
118145
}
119146
}
120147

t/t7201-co.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,26 @@ test_expect_success \
337337
test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
338338
test_must_fail git checkout --track -b track'
339339

340+
test_expect_success 'checkout an unmerged path should fail' '
341+
rm -f .git/index &&
342+
O=$(echo original | git hash-object -w --stdin) &&
343+
A=$(echo ourside | git hash-object -w --stdin) &&
344+
B=$(echo theirside | git hash-object -w --stdin) &&
345+
(
346+
echo "100644 $A 0 fild" &&
347+
echo "100644 $O 1 file" &&
348+
echo "100644 $A 2 file" &&
349+
echo "100644 $B 3 file" &&
350+
echo "100644 $A 0 filf"
351+
) | git update-index --index-info &&
352+
echo "none of the above" >sample &&
353+
cat sample >fild &&
354+
cat sample >file &&
355+
cat sample >filf &&
356+
test_must_fail git checkout fild file filf &&
357+
test_cmp sample fild &&
358+
test_cmp sample filf &&
359+
test_cmp sample file
360+
'
361+
340362
test_done

0 commit comments

Comments
 (0)