Skip to content

Commit 0dd8bec

Browse files
derrickstoleegitster
authored andcommitted
restore: avoid sparse index expansion
Teach update_some() to handle sparse directory entries at the tree level rather than expanding the entire sparse index. When iterating a source tree during checkout/restore operations: - If a directory matches a sparse directory entry with the same OID, skip it entirely (no change needed). - If the OID differs and we are in non-overlay mode (e.g., restore --staged), update the sparse directory entry's OID in place. This is semantically correct because non-overlay mode removes paths not in the source tree anyway. - In overlay mode (e.g., checkout <tree> -- .), fall through to recursive descent so individual file entries are preserved correctly. Also switch from index_name_pos() to index_name_pos_sparse() for individual file lookups to avoid triggering ensure_full_index() when the file is already individually tracked in the index. Update the test expectation in t1092 to assert that 'restore --staged' no longer expands the sparse index. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f4abe6e commit 0dd8bec

2 files changed

Lines changed: 55 additions & 10 deletions

File tree

builtin/checkout.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "revision.h"
3232
#include "sequencer.h"
3333
#include "setup.h"
34+
#include "sparse-index.h"
3435
#include "strvec.h"
3536
#include "submodule.h"
3637
#include "symlinks.h"
@@ -142,14 +143,56 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
142143
}
143144

144145
static int update_some(const struct object_id *oid, struct strbuf *base,
145-
const char *pathname, unsigned mode, void *context UNUSED)
146+
const char *pathname, unsigned mode, void *context)
146147
{
147148
int len;
148149
struct cache_entry *ce;
149150
int pos;
151+
int overlay_mode = context ? *((int *)context) : 1;
150152

151-
if (S_ISDIR(mode))
153+
if (S_ISDIR(mode)) {
154+
/*
155+
* If this directory exists as a sparse directory entry in
156+
* the index, we can handle it at the tree level without
157+
* descending into individual files.
158+
*/
159+
if (the_repository->index->sparse_index) {
160+
struct strbuf dirpath = STRBUF_INIT;
161+
162+
strbuf_addbuf(&dirpath, base);
163+
strbuf_addstr(&dirpath, pathname);
164+
strbuf_addch(&dirpath, '/');
165+
166+
pos = index_name_pos_sparse(the_repository->index,
167+
dirpath.buf, dirpath.len);
168+
if (pos >= 0) {
169+
struct cache_entry *old =
170+
the_repository->index->cache[pos];
171+
if (S_ISSPARSEDIR(old->ce_mode)) {
172+
if (oideq(oid, &old->oid)) {
173+
strbuf_release(&dirpath);
174+
return 0;
175+
}
176+
if (!overlay_mode) {
177+
/*
178+
* In non-overlay mode (e.g.,
179+
* restore --staged), we can
180+
* replace the sparse dir OID
181+
* directly since files not in
182+
* the source tree should be
183+
* removed anyway.
184+
*/
185+
oidcpy(&old->oid, oid);
186+
old->ce_flags |= CE_UPDATE;
187+
strbuf_release(&dirpath);
188+
return 0;
189+
}
190+
}
191+
}
192+
strbuf_release(&dirpath);
193+
}
152194
return READ_TREE_RECURSIVE;
195+
}
153196

154197
len = base->len + strlen(pathname);
155198
ce = make_empty_cache_entry(the_repository->index, len);
@@ -165,7 +208,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
165208
* entry in place. Whether it is UPTODATE or not, checkout_entry will
166209
* do the right thing.
167210
*/
168-
pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
211+
pos = index_name_pos_sparse(the_repository->index, ce->name, ce->ce_namelen);
169212
if (pos >= 0) {
170213
struct cache_entry *old = the_repository->index->cache[pos];
171214
if (ce->ce_mode == old->ce_mode &&
@@ -182,10 +225,11 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
182225
return 0;
183226
}
184227

185-
static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
228+
static int read_tree_some(struct tree *tree, const struct pathspec *pathspec,
229+
int overlay_mode)
186230
{
187231
read_tree(the_repository, tree,
188-
pathspec, update_some, NULL);
232+
pathspec, update_some, &overlay_mode);
189233

190234
/* update the index with the given tree's info
191235
* for all args, expanding wildcards, and exit
@@ -580,7 +624,8 @@ static int checkout_paths(const struct checkout_opts *opts,
580624
return error(_("index file corrupt"));
581625

582626
if (opts->source_tree)
583-
read_tree_some(opts->source_tree, &opts->pathspec);
627+
read_tree_some(opts->source_tree, &opts->pathspec,
628+
opts->overlay_mode);
584629
if (opts->merge)
585630
unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
586631

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,19 +2608,19 @@ test_expect_success 'restore --staged with wildcards' '
26082608
test_all_match git diff --cached
26092609
'
26102610

2611-
test_expect_success 'sparse-index is expanded: restore --staged' '
2611+
test_expect_success 'sparse-index is not expanded: restore --staged' '
26122612
init_repos &&
26132613
26142614
git -C sparse-index checkout -b restore-staged-exp base &&
26152615
git -C sparse-index reset --soft update-folder1 &&
2616-
ensure_expanded restore --staged .
2616+
ensure_not_expanded restore --staged .
26172617
'
26182618

2619-
test_expect_success 'sparse-index is expanded: restore --source --staged' '
2619+
test_expect_success 'sparse-index is not expanded: restore --source --staged' '
26202620
init_repos &&
26212621
26222622
git -C sparse-index checkout -b restore-source-staged base &&
2623-
ensure_expanded restore --source update-folder1 --staged .
2623+
ensure_not_expanded restore --source update-folder1 --staged .
26242624
'
26252625

26262626
test_done

0 commit comments

Comments
 (0)