Skip to content

Commit f085087

Browse files
committed
Merge branch 'vd/sparse-reset'
Various operating modes of "git reset" have been made to work better with the sparse index. * vd/sparse-reset: unpack-trees: improve performance of next_cache_entry reset: make --mixed sparse-aware reset: make sparse-aware (except --mixed) reset: integrate with sparse index reset: expand test coverage for sparse checkouts sparse-index: update command for expand/collapse test reset: preserve skip-worktree bit in mixed reset reset: rename is_missing to !is_in_reset_tree
2 parents 4ee5cac + f2a454e commit f085087

File tree

8 files changed

+356
-37
lines changed

8 files changed

+356
-37
lines changed

builtin/reset.c

Lines changed: 107 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "cache-tree.h"
2626
#include "submodule.h"
2727
#include "submodule-config.h"
28+
#include "dir.h"
2829

2930
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
3031

@@ -136,28 +137,119 @@ static void update_index_from_diff(struct diff_queue_struct *q,
136137
int intent_to_add = *(int *)data;
137138

138139
for (i = 0; i < q->nr; i++) {
140+
int pos;
139141
struct diff_filespec *one = q->queue[i]->one;
140-
int is_missing = !(one->mode && !is_null_oid(&one->oid));
142+
int is_in_reset_tree = one->mode && !is_null_oid(&one->oid);
141143
struct cache_entry *ce;
142144

143-
if (is_missing && !intent_to_add) {
145+
if (!is_in_reset_tree && !intent_to_add) {
144146
remove_file_from_cache(one->path);
145147
continue;
146148
}
147149

148150
ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
149151
0, 0);
152+
153+
/*
154+
* If the file 1) corresponds to an existing index entry with
155+
* skip-worktree set, or 2) does not exist in the index but is
156+
* outside the sparse checkout definition, add a skip-worktree bit
157+
* to the new index entry. Note that a sparse index will be expanded
158+
* if this entry is outside the sparse cone - this is necessary
159+
* to properly construct the reset sparse directory.
160+
*/
161+
pos = cache_name_pos(one->path, strlen(one->path));
162+
if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
163+
(pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
164+
ce->ce_flags |= CE_SKIP_WORKTREE;
165+
150166
if (!ce)
151167
die(_("make_cache_entry failed for path '%s'"),
152168
one->path);
153-
if (is_missing) {
169+
if (!is_in_reset_tree) {
154170
ce->ce_flags |= CE_INTENT_TO_ADD;
155171
set_object_name_for_intent_to_add_entry(ce);
156172
}
157173
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
158174
}
159175
}
160176

177+
static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
178+
{
179+
unsigned int i, pos;
180+
int res = 0;
181+
char *skip_worktree_seen = NULL;
182+
183+
/*
184+
* When using a magic pathspec, assume for the sake of simplicity that
185+
* the index needs to be expanded to match all matchable files.
186+
*/
187+
if (pathspec->magic)
188+
return 1;
189+
190+
for (i = 0; i < pathspec->nr; i++) {
191+
struct pathspec_item item = pathspec->items[i];
192+
193+
/*
194+
* If the pathspec item has a wildcard, the index should be expanded
195+
* if the pathspec has the possibility of matching a subset of entries inside
196+
* of a sparse directory (but not the entire directory).
197+
*
198+
* If the pathspec item is a literal path, the index only needs to be expanded
199+
* if a) the pathspec isn't in the sparse checkout cone (to make sure we don't
200+
* expand for in-cone files) and b) it doesn't match any sparse directories
201+
* (since we can reset whole sparse directories without expanding them).
202+
*/
203+
if (item.nowildcard_len < item.len) {
204+
/*
205+
* Special case: if the pattern is a path inside the cone
206+
* followed by only wildcards, the pattern cannot match
207+
* partial sparse directories, so we don't expand the index.
208+
*/
209+
if (path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
210+
strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len)
211+
continue;
212+
213+
for (pos = 0; pos < active_nr; pos++) {
214+
struct cache_entry *ce = active_cache[pos];
215+
216+
if (!S_ISSPARSEDIR(ce->ce_mode))
217+
continue;
218+
219+
/*
220+
* If the pre-wildcard length is longer than the sparse
221+
* directory name and the sparse directory is the first
222+
* component of the pathspec, need to expand the index.
223+
*/
224+
if (item.nowildcard_len > ce_namelen(ce) &&
225+
!strncmp(item.original, ce->name, ce_namelen(ce))) {
226+
res = 1;
227+
break;
228+
}
229+
230+
/*
231+
* If the pre-wildcard length is shorter than the sparse
232+
* directory and the pathspec does not match the whole
233+
* directory, need to expand the index.
234+
*/
235+
if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
236+
wildmatch(item.original, ce->name, 0)) {
237+
res = 1;
238+
break;
239+
}
240+
}
241+
} else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
242+
!matches_skip_worktree(pathspec, i, &skip_worktree_seen))
243+
res = 1;
244+
245+
if (res > 0)
246+
break;
247+
}
248+
249+
free(skip_worktree_seen);
250+
return res;
251+
}
252+
161253
static int read_from_tree(const struct pathspec *pathspec,
162254
struct object_id *tree_oid,
163255
int intent_to_add)
@@ -170,7 +262,13 @@ static int read_from_tree(const struct pathspec *pathspec,
170262
opt.format_callback = update_index_from_diff;
171263
opt.format_callback_data = &intent_to_add;
172264
opt.flags.override_submodule_config = 1;
265+
opt.flags.recursive = 1;
173266
opt.repo = the_repository;
267+
opt.change = diff_change;
268+
opt.add_remove = diff_addremove;
269+
270+
if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec))
271+
ensure_full_index(&the_index);
174272

175273
if (do_diff_cache(tree_oid, &opt))
176274
return 1;
@@ -249,9 +347,6 @@ static void parse_args(struct pathspec *pathspec,
249347
}
250348
*rev_ret = rev;
251349

252-
if (read_cache() < 0)
253-
die(_("index file corrupt"));
254-
255350
parse_pathspec(pathspec, 0,
256351
PATHSPEC_PREFER_FULL |
257352
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
@@ -397,6 +492,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
397492
if (intent_to_add && reset_type != MIXED)
398493
die(_("-N can only be used with --mixed"));
399494

495+
prepare_repo_settings(the_repository);
496+
the_repository->settings.command_requires_full_index = 0;
497+
498+
if (read_cache() < 0)
499+
die(_("index file corrupt"));
500+
400501
/* Soft reset does not touch the index file nor the working tree
401502
* at all, but requires them in a good order. Other resets reset
402503
* 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
@@ -741,15 +741,26 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
741741
return ret;
742742
}
743743

744+
static void prime_cache_tree_sparse_dir(struct cache_tree *it,
745+
struct tree *tree)
746+
{
747+
748+
oidcpy(&it->oid, &tree->object.oid);
749+
it->entry_count = 1;
750+
}
751+
744752
static void prime_cache_tree_rec(struct repository *r,
745753
struct cache_tree *it,
746-
struct tree *tree)
754+
struct tree *tree,
755+
struct strbuf *tree_path)
747756
{
748757
struct tree_desc desc;
749758
struct name_entry entry;
750759
int cnt;
760+
int base_path_len = tree_path->len;
751761

752762
oidcpy(&it->oid, &tree->object.oid);
763+
753764
init_tree_desc(&desc, tree->buffer, tree->size);
754765
cnt = 0;
755766
while (tree_entry(&desc, &entry)) {
@@ -758,26 +769,55 @@ static void prime_cache_tree_rec(struct repository *r,
758769
else {
759770
struct cache_tree_sub *sub;
760771
struct tree *subtree = lookup_tree(r, &entry.oid);
772+
761773
if (!subtree->object.parsed)
762774
parse_tree(subtree);
763775
sub = cache_tree_sub(it, entry.path);
764776
sub->cache_tree = cache_tree();
765-
prime_cache_tree_rec(r, sub->cache_tree, subtree);
777+
778+
/*
779+
* Recursively-constructed subtree path is only needed when working
780+
* in a sparse index (where it's used to determine whether the
781+
* subtree is a sparse directory in the index).
782+
*/
783+
if (r->index->sparse_index) {
784+
strbuf_setlen(tree_path, base_path_len);
785+
strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
786+
strbuf_add(tree_path, entry.path, entry.pathlen);
787+
strbuf_addch(tree_path, '/');
788+
}
789+
790+
/*
791+
* If a sparse index is in use, the directory being processed may be
792+
* sparse. To confirm that, we can check whether an entry with that
793+
* exact name exists in the index. If it does, the created subtree
794+
* should be sparse. Otherwise, cache tree expansion should continue
795+
* as normal.
796+
*/
797+
if (r->index->sparse_index &&
798+
index_entry_exists(r->index, tree_path->buf, tree_path->len))
799+
prime_cache_tree_sparse_dir(sub->cache_tree, subtree);
800+
else
801+
prime_cache_tree_rec(r, sub->cache_tree, subtree, tree_path);
766802
cnt += sub->cache_tree->entry_count;
767803
}
768804
}
805+
769806
it->entry_count = cnt;
770807
}
771808

772809
void prime_cache_tree(struct repository *r,
773810
struct index_state *istate,
774811
struct tree *tree)
775812
{
813+
struct strbuf tree_path = STRBUF_INIT;
814+
776815
trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
777816
cache_tree_free(&istate->cache_tree);
778817
istate->cache_tree = cache_tree();
779818

780-
prime_cache_tree_rec(r, istate->cache_tree, tree);
819+
prime_cache_tree_rec(r, istate->cache_tree, tree, &tree_path);
820+
strbuf_release(&tree_path);
781821
istate->cache_changed |= CACHE_TREE_CHANGED;
782822
trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
783823
}

cache.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,16 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na
816816
*/
817817
int index_name_pos(struct index_state *, const char *name, int namelen);
818818

819+
/*
820+
* Determines whether an entry with the given name exists within the
821+
* given index. The return value is 1 if an exact match is found, otherwise
822+
* it is 0. Note that, unlike index_name_pos, this function does not expand
823+
* the index if it is sparse. If an item exists within the full index but it
824+
* is contained within a sparse directory (and not in the sparse index), 0 is
825+
* returned.
826+
*/
827+
int index_entry_exists(struct index_state *, const char *name, int namelen);
828+
819829
/*
820830
* Some functions return the negative complement of an insert position when a
821831
* 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
@@ -68,6 +68,11 @@
6868
*/
6969
#define CACHE_ENTRY_PATH_LENGTH 80
7070

71+
enum index_search_mode {
72+
NO_EXPAND_SPARSE = 0,
73+
EXPAND_SPARSE = 1
74+
};
75+
7176
static inline struct cache_entry *mem_pool__ce_alloc(struct mem_pool *mem_pool, size_t len)
7277
{
7378
struct cache_entry *ce;
@@ -551,7 +556,10 @@ int cache_name_stage_compare(const char *name1, int len1, int stage1, const char
551556
return 0;
552557
}
553558

554-
static int index_name_stage_pos(struct index_state *istate, const char *name, int namelen, int stage)
559+
static int index_name_stage_pos(struct index_state *istate,
560+
const char *name, int namelen,
561+
int stage,
562+
enum index_search_mode search_mode)
555563
{
556564
int first, last;
557565

@@ -570,7 +578,7 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
570578
first = next+1;
571579
}
572580

573-
if (istate->sparse_index &&
581+
if (search_mode == EXPAND_SPARSE && istate->sparse_index &&
574582
first > 0) {
575583
/* Note: first <= istate->cache_nr */
576584
struct cache_entry *ce = istate->cache[first - 1];
@@ -586,7 +594,7 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
586594
ce_namelen(ce) < namelen &&
587595
!strncmp(name, ce->name, ce_namelen(ce))) {
588596
ensure_full_index(istate);
589-
return index_name_stage_pos(istate, name, namelen, stage);
597+
return index_name_stage_pos(istate, name, namelen, stage, search_mode);
590598
}
591599
}
592600

@@ -595,7 +603,12 @@ static int index_name_stage_pos(struct index_state *istate, const char *name, in
595603

596604
int index_name_pos(struct index_state *istate, const char *name, int namelen)
597605
{
598-
return index_name_stage_pos(istate, name, namelen, 0);
606+
return index_name_stage_pos(istate, name, namelen, 0, EXPAND_SPARSE);
607+
}
608+
609+
int index_entry_exists(struct index_state *istate, const char *name, int namelen)
610+
{
611+
return index_name_stage_pos(istate, name, namelen, 0, NO_EXPAND_SPARSE) >= 0;
599612
}
600613

601614
int remove_index_entry_at(struct index_state *istate, int pos)
@@ -1237,7 +1250,7 @@ static int has_dir_name(struct index_state *istate,
12371250
*/
12381251
}
12391252

1240-
pos = index_name_stage_pos(istate, name, len, stage);
1253+
pos = index_name_stage_pos(istate, name, len, stage, EXPAND_SPARSE);
12411254
if (pos >= 0) {
12421255
/*
12431256
* Found one, but not so fast. This could
@@ -1337,7 +1350,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
13371350
strcmp(ce->name, istate->cache[istate->cache_nr - 1]->name) > 0)
13381351
pos = index_pos_to_insert_pos(istate->cache_nr);
13391352
else
1340-
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
1353+
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
13411354

13421355
/* existing match? Just replace it. */
13431356
if (pos >= 0) {
@@ -1372,7 +1385,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
13721385
if (!ok_to_replace)
13731386
return error(_("'%s' appears as both a file and as a directory"),
13741387
ce->name);
1375-
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
1388+
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce), EXPAND_SPARSE);
13761389
pos = -pos-1;
13771390
}
13781391
return pos + 1;

t/perf/p2000-sparse-operations.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,8 @@ test_perf_on_all git add -A
110110
test_perf_on_all git add .
111111
test_perf_on_all git commit -a -m A
112112
test_perf_on_all git checkout -f -
113+
test_perf_on_all git reset
114+
test_perf_on_all git reset --hard
115+
test_perf_on_all git reset -- does-not-exist
113116

114117
test_done

0 commit comments

Comments
 (0)