Skip to content

Commit 7f8ab8d

Browse files
torvaldsgitster
authored andcommitted
Don't update unchanged merge entries
In commit 34110cd ("Make 'unpack_trees()' have a separate source and destination index") I introduced a really stupid bug in that it would always add merged entries with the CE_UPDATE flag set. That caused us to always re-write the file, even when it was already up-to-date in the source index. Not only is that really stupid from a performance angle, but more importantly it's actively wrong: if we have dirty state in the tree when we merge, overwriting it with the result of the merge will incorrectly overwrite that dirty state. This trivially fixes the problem - simply don't set the CE_UPDATE flag when the merge result matches the old state. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 198724a commit 7f8ab8d

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

t/t1004-read-tree-m-u-wf.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,45 @@ test_expect_success 'three-way not complaining on an untracked file' '
116116
git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
117117
'
118118

119+
test_expect_success '3-way not overwriting local changes (setup)' '
120+
121+
git reset --hard &&
122+
git checkout -b side-a branch-point &&
123+
echo >>file1 "new line to be kept in the merge result" &&
124+
git commit -a -m "side-a changes file1" &&
125+
git checkout -b side-b branch-point &&
126+
echo >>file2 "new line to be kept in the merge result" &&
127+
git commit -a -m "side-b changes file2" &&
128+
git checkout side-a
129+
130+
'
131+
132+
test_expect_success '3-way not overwriting local changes (our side)' '
133+
134+
# At this point, file1 from side-a should be kept as side-b
135+
# did not touch it.
136+
137+
git reset --hard &&
138+
139+
echo >>file1 "local changes" &&
140+
git read-tree -m -u branch-point side-a side-b &&
141+
grep "new line to be kept" file1 &&
142+
grep "local changes" file1
143+
144+
'
145+
146+
test_expect_success '3-way not overwriting local changes (their side)' '
147+
148+
# At this point, file2 from side-b should be taken as side-a
149+
# did not touch it.
150+
151+
git reset --hard &&
152+
153+
echo >>file2 "local changes" &&
154+
test_must_fail git read-tree -m -u branch-point side-a side-b &&
155+
! grep "new line to be kept" file2 &&
156+
grep "local changes" file2
157+
158+
'
159+
119160
test_done

unpack-trees.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -595,16 +595,19 @@ static int verify_absent(struct cache_entry *ce, const char *action,
595595
static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
596596
struct unpack_trees_options *o)
597597
{
598+
int update = CE_UPDATE;
599+
598600
if (old) {
599601
/*
600602
* See if we can re-use the old CE directly?
601603
* That way we get the uptodate stat info.
602604
*
603-
* This also removes the UPDATE flag on
604-
* a match.
605+
* This also removes the UPDATE flag on a match; otherwise
606+
* we will end up overwriting local changes in the work tree.
605607
*/
606608
if (same(old, merge)) {
607609
copy_cache_entry(merge, old);
610+
update = 0;
608611
} else {
609612
if (verify_uptodate(old, o))
610613
return -1;
@@ -617,7 +620,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
617620
invalidate_ce_path(merge, o);
618621
}
619622

620-
add_entry(o, merge, CE_UPDATE, CE_STAGEMASK);
623+
add_entry(o, merge, update, CE_STAGEMASK);
621624
return 1;
622625
}
623626

0 commit comments

Comments
 (0)