Skip to content

Commit c5326bd

Browse files
peffgitster
authored andcommitted
checkout $tree: do not throw away unchanged index entries
When we "git checkout $tree", we pull paths from $tree into the index, and then check the resulting entries out to the worktree. Our method for the first step is rather heavy-handed, though; it clobbers the entire existing index entry, even if the content is the same. This means we lose our stat information, leading checkout_entry to later rewrite the entire file with identical content. Instead, let's see if we have the identical entry already in the index, in which case we leave it in place. That lets checkout_entry do the right thing. Our tests cover two interesting cases: 1. We make sure that a file which has no changes is not rewritten. 2. We make sure that we do update a file that is unchanged in the index (versus $tree), but has working tree changes. We keep the old index entry, and checkout_entry is able to realize that our stat information is out of date. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent eeff891 commit c5326bd

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

builtin/checkout.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
6767
{
6868
int len;
6969
struct cache_entry *ce;
70+
int pos;
7071

7172
if (S_ISDIR(mode))
7273
return READ_TREE_RECURSIVE;
@@ -79,6 +80,23 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
7980
ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
8081
ce->ce_namelen = len;
8182
ce->ce_mode = create_ce_mode(mode);
83+
84+
/*
85+
* If the entry is the same as the current index, we can leave the old
86+
* entry in place. Whether it is UPTODATE or not, checkout_entry will
87+
* do the right thing.
88+
*/
89+
pos = cache_name_pos(ce->name, ce->ce_namelen);
90+
if (pos >= 0) {
91+
struct cache_entry *old = active_cache[pos];
92+
if (ce->ce_mode == old->ce_mode &&
93+
!hashcmp(ce->sha1, old->sha1)) {
94+
old->ce_flags |= CE_UPDATE;
95+
free(ce);
96+
return 0;
97+
}
98+
}
99+
82100
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
83101
return 0;
84102
}

t/t2022-checkout-paths.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,21 @@ test_expect_success 'do not touch unmerged entries matching $path but not in $tr
6161
test_cmp expect.next0 actual.next0
6262
'
6363

64+
test_expect_success 'do not touch files that are already up-to-date' '
65+
git reset --hard &&
66+
echo one >file1 &&
67+
echo two >file2 &&
68+
git add file1 file2 &&
69+
git commit -m base &&
70+
echo modified >file1 &&
71+
test-chmtime =1000000000 file2 &&
72+
git update-index -q --refresh &&
73+
git checkout HEAD -- file1 file2 &&
74+
echo one >expect &&
75+
test_cmp expect file1 &&
76+
echo "1000000000 file2" >expect &&
77+
test-chmtime -v +0 file2 >actual &&
78+
test_cmp expect actual
79+
'
80+
6481
test_done

0 commit comments

Comments
 (0)