Skip to content

Commit afc37de

Browse files
Merge pull request git-for-windows#204: sparse-checkout: don't update sparse-checkout file on error
If the index has an unstaged change in a file that will be dropped by a change to the sparse-checkout file, then the index update will fail. If this happens during 'git sparse-checkout set', then the entire command will fail. However, the sparse-checkout file is still updated, so the working directory is in a half-finished state. Update the sparse-checkout builtin to run `unpack_trees()` directly instead of through an external `git read-tree` process. While doing so, allow passing a set of patterns, so we can validate the patterns before trying to write to the sparse-checkout file. Also, write sparse-checkout under a .lock and take the sparse-checkout lock around the unpack_trees() check. Resolves git-for-windows#198. Merging with admin privileges because mac builds are broken due to external dependencies.
2 parents 1f3c9be + b9ef9f4 commit afc37de

7 files changed

+169
-31
lines changed

builtin/read-tree.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
186186

187187
if (opts.reset || opts.merge || opts.prefix) {
188188
if (read_cache_unmerged() && (opts.prefix || opts.merge))
189-
die("You need to resolve your current index first");
189+
die(_("You need to resolve your current index first"));
190190
stage = opts.merge = 1;
191191
}
192192
resolve_undo_clear();

builtin/sparse-checkout.c

+132-26
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#include "run-command.h"
88
#include "strbuf.h"
99
#include "string-list.h"
10+
#include "cache.h"
11+
#include "cache-tree.h"
12+
#include "lockfile.h"
13+
#include "resolve-undo.h"
14+
#include "unpack-trees.h"
1015

1116
static char const * const builtin_sparse_checkout_usage[] = {
1217
N_("git sparse-checkout [init|list|set|disable] <options>"),
@@ -60,18 +65,82 @@ static int sparse_checkout_list(int argc, const char **argv)
6065
return 0;
6166
}
6267

63-
static int update_working_directory(void)
68+
struct update_data {
69+
struct unpack_trees_options o;
70+
struct lock_file lock_file;
71+
struct object_id oid;
72+
struct tree *tree;
73+
struct tree_desc t;
74+
struct pattern_list *pl;
75+
};
76+
77+
static struct update_data *initialize_update_data(struct pattern_list *pl)
6478
{
65-
struct argv_array argv = ARGV_ARRAY_INIT;
66-
int result = 0;
67-
argv_array_pushl(&argv, "read-tree", "-m", "-u", "HEAD", NULL);
79+
struct repository *r = the_repository;
80+
struct update_data *ud = xcalloc(1, sizeof(*ud));
6881

69-
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
70-
error(_("failed to update index with new sparse-checkout paths"));
71-
result = 1;
82+
if (get_oid("HEAD", &ud->oid)) {
83+
free(ud);
84+
return NULL;
7285
}
7386

74-
argv_array_clear(&argv);
87+
ud->tree = parse_tree_indirect(&ud->oid);
88+
parse_tree(ud->tree);
89+
init_tree_desc(&ud->t, ud->tree->buffer, ud->tree->size);
90+
91+
memset(&ud->o, 0, sizeof(ud->o));
92+
ud->o.verbose_update = isatty(2);
93+
ud->o.merge = 1;
94+
ud->o.update = 1;
95+
ud->o.fn = oneway_merge;
96+
ud->o.head_idx = -1;
97+
ud->o.src_index = r->index;
98+
ud->o.dst_index = r->index;
99+
ud->o.skip_sparse_checkout = 0;
100+
ud->o.pl = pl;
101+
ud->o.keep_pattern_list = !!pl;
102+
103+
resolve_undo_clear_index(r->index);
104+
setup_work_tree();
105+
106+
cache_tree_free(&r->index->cache_tree);
107+
108+
repo_hold_locked_index(r, &ud->lock_file, LOCK_DIE_ON_ERROR);
109+
110+
return ud;
111+
}
112+
113+
static void finish_update_data(struct update_data *ud)
114+
{
115+
struct repository *r = the_repository;
116+
prime_cache_tree(r, r->index, ud->tree);
117+
write_locked_index(r->index, &ud->lock_file, COMMIT_LOCK);
118+
}
119+
120+
static int update_working_directory(struct update_data *ud)
121+
{
122+
struct repository *r = the_repository;
123+
int complete = !ud;
124+
int result;
125+
126+
if (repo_read_index_unmerged(r))
127+
die(_("You need to resolve your current index first"));
128+
129+
if (!ud)
130+
ud = initialize_update_data(NULL);
131+
132+
/* If we have no HEAD, then ud will be NULL. */
133+
if (!ud)
134+
return 0;
135+
136+
core_apply_sparse_checkout = 1;
137+
result = unpack_trees(1, &ud->t, &ud->o);
138+
139+
if (!result && complete) {
140+
finish_update_data(ud);
141+
free(ud);
142+
}
143+
75144
return result;
76145
}
77146

@@ -145,7 +214,11 @@ static int sparse_checkout_init(int argc, const char **argv)
145214
builtin_sparse_checkout_init_options,
146215
builtin_sparse_checkout_init_usage, 0);
147216

148-
mode = init_opts.cone_mode ? SPARSE_CHECKOUT_CONE : SPARSE_CHECKOUT_FULL;
217+
if (init_opts.cone_mode) {
218+
mode = SPARSE_CHECKOUT_CONE;
219+
core_sparse_checkout_cone = 1;
220+
} else
221+
mode = SPARSE_CHECKOUT_FULL;
149222

150223
if (sc_set_config(mode))
151224
return 1;
@@ -173,12 +246,14 @@ static int sparse_checkout_init(int argc, const char **argv)
173246
}
174247

175248
reset_dir:
176-
return update_working_directory();
249+
core_apply_sparse_checkout = 1;
250+
return update_working_directory(NULL);
177251
}
178252

179253
static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
180254
{
181-
struct pattern_entry *e = xmalloc(sizeof(struct pattern_entry));
255+
struct pattern_entry *e = xmalloc(sizeof(*e));
256+
182257
e->patternlen = path->len;
183258
e->pattern = strbuf_detach(path, NULL);
184259
hashmap_entry_init(e, memhash(e->pattern, e->patternlen));
@@ -190,7 +265,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
190265
char *oldpattern = e->pattern;
191266
size_t newlen;
192267

193-
if (!slash)
268+
if (slash == e->pattern)
194269
break;
195270

196271
newlen = slash - e->pattern;
@@ -232,7 +307,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
232307
char *pattern = sl.items[i].string;
233308

234309
if (strlen(pattern))
235-
fprintf(fp, "/%s/\n!/%s/*/\n", pattern, pattern);
310+
fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern);
236311
}
237312

238313
string_list_clear(&sl, 0);
@@ -252,28 +327,58 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
252327

253328
for (i = 0; i < sl.nr; i++) {
254329
char *pattern = sl.items[i].string;
255-
fprintf(fp, "/%s/\n", pattern);
330+
fprintf(fp, "%s/\n", pattern);
256331
}
257332
}
258333

259334
static int write_patterns_and_update(struct pattern_list *pl)
260335
{
261336
char *sparse_filename;
262337
FILE *fp;
338+
int fd;
339+
struct lock_file lk = LOCK_INIT;
340+
int result;
341+
struct update_data *ud;
342+
343+
/* take index.lock */
344+
ud = initialize_update_data(pl);
263345

264346
sparse_filename = get_sparse_checkout_filename();
265-
fp = fopen(sparse_filename, "w");
347+
348+
/* take sparse-checkout.lock */
349+
fd = hold_lock_file_for_update(&lk, sparse_filename,
350+
LOCK_DIE_ON_ERROR);
351+
352+
result = update_working_directory(ud);
353+
if (result) {
354+
rollback_lock_file(&lk);
355+
free(sparse_filename);
356+
clear_pattern_list(pl);
357+
free(ud);
358+
update_working_directory(NULL);
359+
return result;
360+
}
361+
362+
fp = fdopen(fd, "w");
266363

267364
if (core_sparse_checkout_cone)
268365
write_cone_to_file(fp, pl);
269366
else
270367
write_patterns_to_file(fp, pl);
271368

272-
fclose(fp);
273-
free(sparse_filename);
369+
fflush(fp);
370+
371+
/* commit sparse-checkout.lock file */
372+
commit_lock_file(&lk);
274373

374+
/* commit index.lock file */
375+
finish_update_data(ud);
376+
free(ud);
377+
378+
free(sparse_filename);
275379
clear_pattern_list(pl);
276-
return update_working_directory();
380+
381+
return 0;
277382
}
278383

279384
static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
@@ -285,11 +390,8 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
285390
if (!line->len)
286391
return;
287392

288-
if (line->buf[0] == '/')
289-
strbuf_remove(line, 0, 1);
290-
291-
if (!line->len)
292-
return;
393+
if (line->buf[0] != '/')
394+
strbuf_insert(line, 0, "/", 1);
293395

294396
insert_recursive_pattern(pl, line);
295397
}
@@ -307,6 +409,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
307409
{
308410
int i;
309411
struct pattern_list pl;
412+
static const char *empty_base = "";
310413

311414
static struct option builtin_sparse_checkout_set_options[] = {
312415
OPT_BOOL(0, "stdin", &set_opts.use_stdin,
@@ -325,6 +428,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
325428
struct strbuf line = STRBUF_INIT;
326429
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
327430
hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
431+
pl.use_cone_patterns = 1;
328432

329433
if (set_opts.use_stdin) {
330434
while (!strbuf_getline(&line, stdin))
@@ -343,11 +447,11 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
343447
while (!strbuf_getline(&line, stdin)) {
344448
size_t len;
345449
char *buf = strbuf_detach(&line, &len);
346-
add_pattern(buf, buf, len, &pl, 0);
450+
add_pattern(buf, empty_base, 0, &pl, 0);
347451
}
348452
} else {
349453
for (i = 0; i < argc; i++)
350-
add_pattern(argv[i], argv[i], strlen(argv[i]), &pl, 0);
454+
add_pattern(argv[i], empty_base, 0, &pl, 0);
351455
}
352456
}
353457

@@ -367,7 +471,9 @@ static int sparse_checkout_disable(int argc, const char **argv)
367471
fprintf(fp, "/*\n");
368472
fclose(fp);
369473

370-
if (update_working_directory())
474+
core_apply_sparse_checkout = 1;
475+
core_sparse_checkout_cone = 0;
476+
if (update_working_directory(NULL))
371477
die(_("error while refreshing working directory"));
372478

373479
unlink(sparse_filename);

dir.c

+13-1
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,17 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
622622
if (!pl->use_cone_patterns)
623623
return;
624624

625-
if (!strcmp(given->pattern, "/*"))
625+
if (given->flags & PATTERN_FLAG_NEGATIVE &&
626+
given->flags & PATTERN_FLAG_MUSTBEDIR &&
627+
!strcmp(given->pattern, "/*")) {
628+
pl->full_cone = 0;
626629
return;
630+
}
631+
632+
if (!given->flags && !strcmp(given->pattern, "/*")) {
633+
pl->full_cone = 1;
634+
return;
635+
}
627636

628637
if (given->patternlen > 2 &&
629638
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
@@ -1285,6 +1294,9 @@ enum pattern_match_result path_matches_pattern_list(
12851294
return UNDECIDED;
12861295
}
12871296

1297+
if (pl->full_cone)
1298+
return MATCHED;
1299+
12881300
strbuf_addch(&parent_pathname, '/');
12891301
strbuf_add(&parent_pathname, pathname, pathlen);
12901302

dir.h

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct pattern_list {
7171
* excludes array above. If non-zero, that check succeeded.
7272
*/
7373
unsigned use_cone_patterns;
74+
unsigned full_cone;
7475

7576
/*
7677
* Stores paths where everything starting with those paths

t/t1091-sparse-checkout-builtin.sh

+17
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,13 @@ test_expect_success 'cone mode: init and set' '
192192
a
193193
deep
194194
EOF
195+
test_cmp dir expect &&
195196
ls repo/deep >dir &&
196197
cat >expect <<-EOF &&
197198
a
198199
deeper1
199200
EOF
201+
test_cmp dir expect &&
200202
ls repo/deep/deeper1 >dir &&
201203
cat >expect <<-EOF &&
202204
a
@@ -238,5 +240,20 @@ test_expect_success 'cone mode: set with nested folders' '
238240
test_cmp repo/.git/info/sparse-checkout expect
239241
'
240242

243+
test_expect_success 'revert to old sparse-checkout on bad update' '
244+
echo update >repo/deep/deeper2/a &&
245+
cp repo/.git/info/sparse-checkout expect &&
246+
test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
247+
test_i18ngrep "Cannot update sparse checkout" err &&
248+
test_cmp repo/.git/info/sparse-checkout expect &&
249+
ls repo/deep >dir &&
250+
cat >expect <<-EOF &&
251+
a
252+
deeper1
253+
deeper2
254+
EOF
255+
test_cmp dir expect
256+
'
257+
241258
test_done
242259

unpack-trees.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
15441544
memset(&pl, 0, sizeof(pl));
15451545
if (!core_apply_sparse_checkout || !o->update)
15461546
o->skip_sparse_checkout = 1;
1547-
if (!o->skip_sparse_checkout) {
1547+
if (!o->skip_sparse_checkout && !o->pl) {
15481548
if (core_virtualfilesystem) {
15491549
o->pl = &pl;
15501550
} else {
@@ -1723,7 +1723,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
17231723

17241724
done:
17251725
trace_performance_leave("unpack_trees");
1726-
clear_pattern_list(&pl);
1726+
if (!o->keep_pattern_list)
1727+
clear_pattern_list(&pl);
17271728
trace2_data_intmax("unpack_trees", NULL, "unpack_trees/nr_unpack_entries",
17281729
(intmax_t)(get_nr_unpack_entry() - nr_unpack_entry_at_start));
17291730
trace2_region_leave("unpack_trees", "unpack_trees", NULL);

unpack-trees.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ struct unpack_trees_options {
5959
quiet,
6060
exiting_early,
6161
show_all_errors,
62-
dry_run;
62+
dry_run,
63+
keep_pattern_list;
6364
const char *prefix;
6465
int cache_bottom;
6566
struct dir_struct *dir;

0 commit comments

Comments
 (0)