]> git.ipfire.org Git - thirdparty/git.git/blame - sparse-index.c
sparse-index: check index conversion happens
[thirdparty/git.git] / sparse-index.c
CommitLineData
3964fc2a
DS
1#include "cache.h"
2#include "repository.h"
3#include "sparse-index.h"
4300f844
DS
4#include "tree.h"
5#include "pathspec.h"
6#include "trace2.h"
6e773527
DS
7#include "cache-tree.h"
8#include "config.h"
9#include "dir.h"
10#include "fsmonitor.h"
11
12static struct cache_entry *construct_sparse_dir_entry(
13 struct index_state *istate,
14 const char *sparse_dir,
15 struct cache_tree *tree)
16{
17 struct cache_entry *de;
18
19 de = make_cache_entry(istate, S_IFDIR, &tree->oid, sparse_dir, 0, 0);
20
21 de->ce_flags |= CE_SKIP_WORKTREE;
22 return de;
23}
24
25/*
26 * Returns the number of entries "inserted" into the index.
27 */
28static int convert_to_sparse_rec(struct index_state *istate,
29 int num_converted,
30 int start, int end,
31 const char *ct_path, size_t ct_pathlen,
32 struct cache_tree *ct)
33{
34 int i, can_convert = 1;
35 int start_converted = num_converted;
36 enum pattern_match_result match;
37 int dtype;
38 struct strbuf child_path = STRBUF_INIT;
39 struct pattern_list *pl = istate->sparse_checkout_patterns;
40
41 /*
42 * Is the current path outside of the sparse cone?
43 * Then check if the region can be replaced by a sparse
44 * directory entry (everything is sparse and merged).
45 */
46 match = path_matches_pattern_list(ct_path, ct_pathlen,
47 NULL, &dtype, pl, istate);
48 if (match != NOT_MATCHED)
49 can_convert = 0;
50
51 for (i = start; can_convert && i < end; i++) {
52 struct cache_entry *ce = istate->cache[i];
53
54 if (ce_stage(ce) ||
f442313e 55 S_ISGITLINK(ce->ce_mode) ||
6e773527
DS
56 !(ce->ce_flags & CE_SKIP_WORKTREE))
57 can_convert = 0;
58 }
59
60 if (can_convert) {
61 struct cache_entry *se;
62 se = construct_sparse_dir_entry(istate, ct_path, ct);
63
64 istate->cache[num_converted++] = se;
65 return 1;
66 }
67
68 for (i = start; i < end; ) {
69 int count, span, pos = -1;
70 const char *base, *slash;
71 struct cache_entry *ce = istate->cache[i];
72
73 /*
74 * Detect if this is a normal entry outside of any subtree
75 * entry.
76 */
77 base = ce->name + ct_pathlen;
78 slash = strchr(base, '/');
79
80 if (slash)
81 pos = cache_tree_subtree_pos(ct, base, slash - base);
82
83 if (pos < 0) {
84 istate->cache[num_converted++] = ce;
85 i++;
86 continue;
87 }
88
89 strbuf_setlen(&child_path, 0);
90 strbuf_add(&child_path, ce->name, slash - ce->name + 1);
91
92 span = ct->down[pos]->cache_tree->entry_count;
93 count = convert_to_sparse_rec(istate,
94 num_converted, i, i + span,
95 child_path.buf, child_path.len,
96 ct->down[pos]->cache_tree);
97 num_converted += count;
98 i += span;
99 }
100
101 strbuf_release(&child_path);
102 return num_converted - start_converted;
103}
104
105int convert_to_sparse(struct index_state *istate)
106{
107 if (istate->split_index || istate->sparse_index ||
108 !core_apply_sparse_checkout || !core_sparse_checkout_cone)
109 return 0;
110
111 /*
112 * For now, only create a sparse index with the
113 * GIT_TEST_SPARSE_INDEX environment variable. We will relax
114 * this once we have a proper way to opt-in (and later still,
115 * opt-out).
116 */
117 if (!git_env_bool("GIT_TEST_SPARSE_INDEX", 0))
118 return 0;
119
120 if (!istate->sparse_checkout_patterns) {
121 istate->sparse_checkout_patterns = xcalloc(1, sizeof(struct pattern_list));
122 if (get_sparse_checkout_patterns(istate->sparse_checkout_patterns) < 0)
123 return 0;
124 }
125
126 if (!istate->sparse_checkout_patterns->use_cone_patterns) {
127 warning(_("attempting to use sparse-index without cone mode"));
128 return -1;
129 }
130
131 if (cache_tree_update(istate, 0)) {
132 warning(_("unable to update cache-tree, staying full"));
133 return -1;
134 }
135
136 remove_fsmonitor(istate);
137
138 trace2_region_enter("index", "convert_to_sparse", istate->repo);
139 istate->cache_nr = convert_to_sparse_rec(istate,
140 0, 0, istate->cache_nr,
141 "", 0, istate->cache_tree);
142 istate->drop_cache_tree = 1;
143 istate->sparse_index = 1;
144 trace2_region_leave("index", "convert_to_sparse", istate->repo);
145 return 0;
146}
4300f844
DS
147
148static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
149{
150 ALLOC_GROW(istate->cache, nr + 1, istate->cache_alloc);
151
152 istate->cache[nr] = ce;
153 add_name_hash(istate, ce);
154}
155
156static int add_path_to_index(const struct object_id *oid,
157 struct strbuf *base, const char *path,
158 unsigned int mode, void *context)
159{
160 struct index_state *istate = (struct index_state *)context;
161 struct cache_entry *ce;
162 size_t len = base->len;
163
164 if (S_ISDIR(mode))
165 return READ_TREE_RECURSIVE;
166
167 strbuf_addstr(base, path);
168
169 ce = make_cache_entry(istate, mode, oid, base->buf, 0, 0);
170 ce->ce_flags |= CE_SKIP_WORKTREE;
171 set_index_entry(istate, istate->cache_nr++, ce);
172
173 strbuf_setlen(base, len);
174 return 0;
175}
3964fc2a
DS
176
177void ensure_full_index(struct index_state *istate)
178{
4300f844
DS
179 int i;
180 struct index_state *full;
181 struct strbuf base = STRBUF_INIT;
182
183 if (!istate || !istate->sparse_index)
184 return;
185
186 if (!istate->repo)
187 istate->repo = the_repository;
188
189 trace2_region_enter("index", "ensure_full_index", istate->repo);
190
191 /* initialize basics of new index */
192 full = xcalloc(1, sizeof(struct index_state));
193 memcpy(full, istate, sizeof(struct index_state));
194
195 /* then change the necessary things */
196 full->sparse_index = 0;
197 full->cache_alloc = (3 * istate->cache_alloc) / 2;
198 full->cache_nr = 0;
199 ALLOC_ARRAY(full->cache, full->cache_alloc);
200
201 for (i = 0; i < istate->cache_nr; i++) {
202 struct cache_entry *ce = istate->cache[i];
203 struct tree *tree;
204 struct pathspec ps;
205
206 if (!S_ISSPARSEDIR(ce->ce_mode)) {
207 set_index_entry(full, full->cache_nr++, ce);
208 continue;
209 }
210 if (!(ce->ce_flags & CE_SKIP_WORKTREE))
211 warning(_("index entry is a directory, but not sparse (%08x)"),
212 ce->ce_flags);
213
214 /* recursively walk into cd->name */
215 tree = lookup_tree(istate->repo, &ce->oid);
216
217 memset(&ps, 0, sizeof(ps));
218 ps.recursive = 1;
219 ps.has_wildcard = 1;
220 ps.max_depth = -1;
221
222 strbuf_setlen(&base, 0);
223 strbuf_add(&base, ce->name, strlen(ce->name));
224
225 read_tree_at(istate->repo, tree, &base, &ps,
226 add_path_to_index, full);
227
228 /* free directory entries. full entries are re-used */
229 discard_cache_entry(ce);
230 }
231
232 /* Copy back into original index. */
233 memcpy(&istate->name_hash, &full->name_hash, sizeof(full->name_hash));
234 istate->sparse_index = 0;
235 free(istate->cache);
236 istate->cache = full->cache;
237 istate->cache_nr = full->cache_nr;
238 istate->cache_alloc = full->cache_alloc;
239
240 strbuf_release(&base);
241 free(full);
242
243 trace2_region_leave("index", "ensure_full_index", istate->repo);
3964fc2a 244}