Skip to content

Commit f1043ea

Browse files
vdyederrickstolee
authored andcommitted
Merge pull request #417 from vdye/sparse-index/git-reset
Sparse index: `git reset`
2 parents da3045e + 6a7f71e commit f1043ea

File tree

5 files changed

+305
-16
lines changed

5 files changed

+305
-16
lines changed

builtin/reset.c

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,73 @@ static void update_index_from_diff(struct diff_queue_struct *q,
196196
}
197197
}
198198

199+
static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
200+
{
201+
unsigned int i, pos;
202+
int res = 0;
203+
char *skip_worktree_seen = NULL;
204+
205+
/*
206+
* When using a magic pathspec, assume for the sake of simplicity that
207+
* the index needs to be expanded to match all matchable files.
208+
*/
209+
if (pathspec->magic)
210+
return 1;
211+
212+
for (i = 0; i < pathspec->nr; i++) {
213+
struct pathspec_item item = pathspec->items[i];
214+
215+
/*
216+
* If the pathspec item has a wildcard, the index should be expanded
217+
* if the pathspec has the possibility of matching a subset of entries inside
218+
* of a sparse directory (but not the entire directory).
219+
*
220+
* If the pathspec item is a literal path, the index only needs to be expanded
221+
* if a) the pathspec isn't in the sparse checkout cone (to make sure we don't
222+
* expand for in-cone files) and b) it doesn't match any sparse directories
223+
* (since we can reset whole sparse directories without expanding them).
224+
*/
225+
if (item.nowildcard_len < item.len) {
226+
for (pos = 0; pos < active_nr; pos++) {
227+
struct cache_entry *ce = active_cache[pos];
228+
229+
if (!S_ISSPARSEDIR(ce->ce_mode))
230+
continue;
231+
232+
/*
233+
* If the pre-wildcard length is longer than the sparse
234+
* directory name and the sparse directory is the first
235+
* component of the pathspec, need to expand the index.
236+
*/
237+
if (item.nowildcard_len > ce_namelen(ce) &&
238+
!strncmp(item.original, ce->name, ce_namelen(ce))) {
239+
res = 1;
240+
break;
241+
}
242+
243+
/*
244+
* If the pre-wildcard length is shorter than the sparse
245+
* directory and the pathspec does not match the whole
246+
* directory, need to expand the index.
247+
*/
248+
if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
249+
wildmatch(item.original, ce->name, 0)) {
250+
res = 1;
251+
break;
252+
}
253+
}
254+
} else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
255+
!matches_skip_worktree(pathspec, i, &skip_worktree_seen))
256+
res = 1;
257+
258+
if (res > 0)
259+
break;
260+
}
261+
262+
free(skip_worktree_seen);
263+
return res;
264+
}
265+
199266
static int read_from_tree(const struct pathspec *pathspec,
200267
struct object_id *tree_oid,
201268
int intent_to_add)
@@ -208,7 +275,13 @@ static int read_from_tree(const struct pathspec *pathspec,
208275
opt.format_callback = update_index_from_diff;
209276
opt.format_callback_data = &intent_to_add;
210277
opt.flags.override_submodule_config = 1;
278+
opt.flags.recursive = 1;
211279
opt.repo = the_repository;
280+
opt.change = diff_change;
281+
opt.add_remove = diff_addremove;
282+
283+
if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec))
284+
ensure_full_index(&the_index);
212285

213286
if (do_diff_cache(tree_oid, &opt))
214287
return 1;
@@ -287,9 +360,6 @@ static void parse_args(struct pathspec *pathspec,
287360
}
288361
*rev_ret = rev;
289362

290-
if (read_cache() < 0)
291-
die(_("index file corrupt"));
292-
293363
parse_pathspec(pathspec, 0,
294364
PATHSPEC_PREFER_FULL |
295365
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
@@ -446,6 +516,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
446516
if (intent_to_add && reset_type != MIXED)
447517
die(_("-N can only be used with --mixed"));
448518

519+
prepare_repo_settings(the_repository);
520+
the_repository->settings.command_requires_full_index = 0;
521+
522+
if (read_cache() < 0)
523+
die(_("index file corrupt"));
524+
449525
/* Soft reset does not touch the index file nor the working tree
450526
* at all, but requires them in a good order. Other resets reset
451527
* the index file to the tree object we are switching to. */

cache-tree.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -801,15 +801,26 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
801801
return ret;
802802
}
803803

804+
static void prime_cache_tree_sparse_dir(struct cache_tree *it,
805+
struct tree *tree)
806+
{
807+
808+
oidcpy(&it->oid, &tree->object.oid);
809+
it->entry_count = 1;
810+
}
811+
804812
static void prime_cache_tree_rec(struct repository *r,
805813
struct cache_tree *it,
806-
struct tree *tree)
814+
struct tree *tree,
815+
struct strbuf *tree_path)
807816
{
808817
struct tree_desc desc;
809818
struct name_entry entry;
810819
int cnt;
820+
int base_path_len = tree_path->len;
811821

812822
oidcpy(&it->oid, &tree->object.oid);
823+
813824
init_tree_desc(&desc, tree->buffer, tree->size);
814825
cnt = 0;
815826
while (tree_entry(&desc, &entry)) {
@@ -818,26 +829,55 @@ static void prime_cache_tree_rec(struct repository *r,
818829
else {
819830
struct cache_tree_sub *sub;
820831
struct tree *subtree = lookup_tree(r, &entry.oid);
832+
821833
if (!subtree->object.parsed)
822834
parse_tree(subtree);
823835
sub = cache_tree_sub(it, entry.path);
824836
sub->cache_tree = cache_tree();
825-
prime_cache_tree_rec(r, sub->cache_tree, subtree);
837+
838+
/*
839+
* Recursively-constructed subtree path is only needed when working
840+
* in a sparse index (where it's used to determine whether the
841+
* subtree is a sparse directory in the index).
842+
*/
843+
if (r->index->sparse_index) {
844+
strbuf_setlen(tree_path, base_path_len);
845+
strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
846+
strbuf_add(tree_path, entry.path, entry.pathlen);
847+
strbuf_addch(tree_path, '/');
848+
}
849+
850+
/*
851+
* If a sparse index is in use, the directory being processed may be
852+
* sparse. To confirm that, we can check whether an entry with that
853+
* exact name exists in the index. If it does, the created subtree
854+
* should be sparse. Otherwise, cache tree expansion should continue
855+
* as normal.
856+
*/
857+
if (r->index->sparse_index &&
858+
index_entry_exists(r->index, tree_path->buf, tree_path->len))
859+
prime_cache_tree_sparse_dir(sub->cache_tree, subtree);
860+
else
861+
prime_cache_tree_rec(r, sub->cache_tree, subtree, tree_path);
826862
cnt += sub->cache_tree->entry_count;
827863
}
828864
}
865+
829866
it->entry_count = cnt;
830867
}
831868

832869
void prime_cache_tree(struct repository *r,
833870
struct index_state *istate,
834871
struct tree *tree)
835872
{
873+
struct strbuf tree_path = STRBUF_INIT;
874+
836875
trace2_region_enter("cache-tree", "prime_cache_tree", r);
837876
cache_tree_free(&istate->cache_tree);
838877
istate->cache_tree = cache_tree();
839878

840-
prime_cache_tree_rec(r, istate->cache_tree, tree);
879+
prime_cache_tree_rec(r, istate->cache_tree, tree, &tree_path);
880+
strbuf_release(&tree_path);
841881
istate->cache_changed |= CACHE_TREE_CHANGED;
842882
trace2_region_leave("cache-tree", "prime_cache_tree", r);
843883
}

cache.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,16 @@ struct cache_entry *index_file_next_match(struct index_state *istate, struct cac
833833
*/
834834
int index_name_pos(struct index_state *, const char *name, int namelen);
835835

836+
/*
837+
* Determines whether an entry with the given name exists within the
838+
* given index. The return value is 1 if an exact match is found, otherwise
839+
* it is 0. Note that, unlike index_name_pos, this function does not expand
840+
* the index if it is sparse. If an item exists within the full index but it
841+
* is contained within a sparse directory (and not in the sparse index), 0 is
842+
* returned.
843+
*/
844+
int index_entry_exists(struct index_state *, const char *name, int namelen);
845+
836846
/*
837847
* Some functions return the negative complement of an insert position when a
838848
* precise match was not found but a position was found where the entry would

read-cache.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
*/
7171
#define CACHE_ENTRY_PATH_LENGTH 80
7272

73+
enum index_search_mode {
74+
NO_EXPAND_SPARSE = 0,
75+
EXPAND_SPARSE = 1
76+
};
77+
7378
static inline struct cache_entry *mem_pool__ce_alloc(struct mem_pool *mem_pool, size_t len)
7479
{
7580
struct cache_entry *ce;
@@ -564,7 +569,10 @@ int cache_name_stage_compare(const char *name1, int len1, int stage1, const char
564569
return 0;
565570
}
566571

567-
static int index_name_stage_pos(struct index_state *istate, const char *name, int namelen, int stage)
572+
static int index_name_stage_pos(struct index_state *istate,
573+
const char *name, int namelen,
574+
int stage,
575+
enum index_search_mode search_mode)
568576
{
569577
int first, last;
570578

@@ -583,7 +591,7 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
583591
first = next+1;
584592
}
585593

586-
if (istate->sparse_index &&
594+
if (search_mode == EXPAND_SPARSE && istate->sparse_index &&
587595
first > 0) {
588596
/* Note: first <= istate->cache_nr */
589597
struct cache_entry *ce = istate->cache[first - 1];
@@ -599,7 +607,7 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
599607
ce_namelen(ce) < namelen &&
600608
!strncmp(name, ce->name, ce_namelen(ce))) {
601609
ensure_full_index(istate);
602-
return index_name_stage_pos(istate, name, namelen, stage);
610+
return index_name_stage_pos(istate, name, namelen, stage, search_mode);
603611
}
604612
}
605613

@@ -608,7 +616,12 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
608616

609617
int index_name_pos(struct index_state *istate, const char *name, int namelen)
610618
{
611-
return index_name_stage_pos(istate, name, namelen, 0);
619+
return index_name_stage_pos(istate, name, namelen, 0, EXPAND_SPARSE);
620+
}
621+
622+
int index_entry_exists(struct index_state *istate, const char *name, int namelen)
623+
{
624+
return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE) >= 0;
612625
}
613626

614627
int remove_index_entry_at(struct index_state *istate, int pos)
@@ -1250,7 +1263,7 @@ static int has_dir_name(struct index_state *istate,
12501263
*/
12511264
}
12521265

1253-
pos = index_name_stage_pos(istate, name, len, stage);
1266+
pos = index_name_stage_pos(istate, name, len, stage, EXPAND_SPARSE);
12541267
if (pos >= 0) {
12551268
/*
12561269
* Found one, but not so fast. This could
@@ -1350,7 +1363,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
13501363
strcmp(ce->name, istate->cache[istate->cache_nr - 1]->name) > 0)
13511364
pos = index_pos_to_insert_pos(istate->cache_nr);
13521365
else
1353-
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
1366+
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
13541367

13551368
/* existing match? Just replace it. */
13561369
if (pos >= 0) {
@@ -1385,7 +1398,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
13851398
if (!ok_to_replace)
13861399
return error(_("'%s' appears as both a file and as a directory"),
13871400
ce->name);
1388-
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
1401+
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
13891402
pos = -pos-1;
13901403
}
13911404
return pos + 1;

0 commit comments

Comments
 (0)