Skip to content

Commit d1a43f2

Browse files
committed
reset --hard/read-tree --reset -u: remove unmerged new paths
When aborting a failed merge that has brought in a new path using "git reset --hard" or "git read-tree --reset -u", we used to first forget about the new path (via read_cache_unmerged) and then matched the working tree to what is recorded in the index, thus ending up leaving the new path in the work tree. Signed-off-by: Junio C Hamano <[email protected]>
1 parent c82efaf commit d1a43f2

File tree

2 files changed

+78
-13
lines changed

2 files changed

+78
-13
lines changed

read-cache.c

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,23 +1460,28 @@ int write_index(const struct index_state *istate, int newfd)
14601460
int read_index_unmerged(struct index_state *istate)
14611461
{
14621462
int i;
1463-
struct cache_entry **dst;
1464-
struct cache_entry *last = NULL;
1463+
int unmerged = 0;
14651464

14661465
read_index(istate);
1467-
dst = istate->cache;
14681466
for (i = 0; i < istate->cache_nr; i++) {
14691467
struct cache_entry *ce = istate->cache[i];
1470-
if (ce_stage(ce)) {
1471-
remove_name_hash(ce);
1472-
if (last && !strcmp(ce->name, last->name))
1473-
continue;
1474-
cache_tree_invalidate_path(istate->cache_tree, ce->name);
1475-
last = ce;
1468+
struct cache_entry *new_ce;
1469+
int size, len;
1470+
1471+
if (!ce_stage(ce))
14761472
continue;
1477-
}
1478-
*dst++ = ce;
1473+
unmerged = 1;
1474+
len = strlen(ce->name);
1475+
size = cache_entry_size(len);
1476+
new_ce = xcalloc(1, size);
1477+
hashcpy(new_ce->sha1, ce->sha1);
1478+
memcpy(new_ce->name, ce->name, len);
1479+
new_ce->ce_flags = create_ce_flags(len, 0);
1480+
new_ce->ce_mode = ce->ce_mode;
1481+
if (add_index_entry(istate, new_ce, 0))
1482+
return error("%s: cannot drop to stage #0",
1483+
ce->name);
1484+
i = index_name_pos(istate, new_ce->name, len);
14791485
}
1480-
istate->cache_nr = dst - istate->cache;
1481-
return !!last;
1486+
return unmerged;
14821487
}

t/t1005-read-tree-reset.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,64 @@ test_expect_success 'reset should work' '
2727
test_cmp expect actual
2828
'
2929

30+
test_expect_success 'reset should remove remnants from a failed merge' '
31+
git read-tree --reset -u HEAD &&
32+
git ls-files -s >expect &&
33+
sha1=$(git rev-parse :new) &&
34+
(
35+
echo "100644 $sha1 1 old"
36+
echo "100644 $sha1 3 old"
37+
) | git update-index --index-info &&
38+
>old &&
39+
git ls-files -s &&
40+
git read-tree --reset -u HEAD &&
41+
git ls-files -s >actual &&
42+
! test -f old
43+
'
44+
45+
test_expect_success 'Porcelain reset should remove remnants too' '
46+
git read-tree --reset -u HEAD &&
47+
git ls-files -s >expect &&
48+
sha1=$(git rev-parse :new) &&
49+
(
50+
echo "100644 $sha1 1 old"
51+
echo "100644 $sha1 3 old"
52+
) | git update-index --index-info &&
53+
>old &&
54+
git ls-files -s &&
55+
git reset --hard &&
56+
git ls-files -s >actual &&
57+
! test -f old
58+
'
59+
60+
test_expect_success 'Porcelain checkout -f should remove remnants too' '
61+
git read-tree --reset -u HEAD &&
62+
git ls-files -s >expect &&
63+
sha1=$(git rev-parse :new) &&
64+
(
65+
echo "100644 $sha1 1 old"
66+
echo "100644 $sha1 3 old"
67+
) | git update-index --index-info &&
68+
>old &&
69+
git ls-files -s &&
70+
git checkout -f &&
71+
git ls-files -s >actual &&
72+
! test -f old
73+
'
74+
75+
test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
76+
git read-tree --reset -u HEAD &&
77+
git ls-files -s >expect &&
78+
sha1=$(git rev-parse :new) &&
79+
(
80+
echo "100644 $sha1 1 old"
81+
echo "100644 $sha1 3 old"
82+
) | git update-index --index-info &&
83+
>old &&
84+
git ls-files -s &&
85+
git checkout -f HEAD &&
86+
git ls-files -s >actual &&
87+
! test -f old
88+
'
89+
3090
test_done

0 commit comments

Comments
 (0)