Skip to content

Commit efd9c53

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: update working directory in-process
The sparse-checkout builtin used 'git read-tree -mu HEAD' to update the skip-worktree bits in the index and to update the working directory. This extra process is overly complex, and prone to failure. It also requires that we write our changes to the sparse-checkout file before trying to update the index. Remove this extra process call by creating a direct call to unpack_trees() in the same way 'git read-tree -mu HEAD' does. In addition, provide an in-memory list of patterns so we can avoid reading from the sparse-checkout file. This allows us to test a proposed change to the file before writing to it. An earlier version of this patch included a bug when the 'set' command failed due to the "Sparse checkout leaves no entry on working directory" error. It would not rollback the index.lock file, so the replay of the old sparse-checkout specification would fail. A test in t1091 now covers that scenario. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f9e96bf commit efd9c53

File tree

5 files changed

+105
-16
lines changed

5 files changed

+105
-16
lines changed

builtin/read-tree.c

Lines changed: 1 addition & 1 deletion
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

Lines changed: 71 additions & 12 deletions
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,54 @@ static int sparse_checkout_list(int argc, const char **argv)
6065
return 0;
6166
}
6267

63-
static int update_working_directory(void)
68+
static int update_working_directory(struct pattern_list *pl)
6469
{
65-
struct argv_array argv = ARGV_ARRAY_INIT;
6670
int result = 0;
67-
argv_array_pushl(&argv, "read-tree", "-m", "-u", "HEAD", NULL);
71+
struct unpack_trees_options o;
72+
struct lock_file lock_file = LOCK_INIT;
73+
struct object_id oid;
74+
struct tree *tree;
75+
struct tree_desc t;
76+
struct repository *r = the_repository;
6877

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;
72-
}
78+
if (repo_read_index_unmerged(r))
79+
die(_("You need to resolve your current index first"));
80+
81+
if (get_oid("HEAD", &oid))
82+
return 0;
83+
84+
tree = parse_tree_indirect(&oid);
85+
parse_tree(tree);
86+
init_tree_desc(&t, tree->buffer, tree->size);
87+
88+
memset(&o, 0, sizeof(o));
89+
o.verbose_update = isatty(2);
90+
o.merge = 1;
91+
o.update = 1;
92+
o.fn = oneway_merge;
93+
o.head_idx = -1;
94+
o.src_index = r->index;
95+
o.dst_index = r->index;
96+
o.skip_sparse_checkout = 0;
97+
o.pl = pl;
98+
o.keep_pattern_list = !!pl;
99+
100+
resolve_undo_clear_index(r->index);
101+
setup_work_tree();
102+
103+
cache_tree_free(&r->index->cache_tree);
104+
105+
repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
106+
107+
core_apply_sparse_checkout = 1;
108+
result = unpack_trees(1, &t, &o);
109+
110+
if (!result) {
111+
prime_cache_tree(r, r->index, tree);
112+
write_locked_index(r->index, &lock_file, COMMIT_LOCK);
113+
} else
114+
rollback_lock_file(&lock_file);
73115

74-
argv_array_clear(&argv);
75116
return result;
76117
}
77118

@@ -147,7 +188,11 @@ static int sparse_checkout_init(int argc, const char **argv)
147188
builtin_sparse_checkout_init_options,
148189
builtin_sparse_checkout_init_usage, 0);
149190

150-
mode = init_opts.cone_mode ? MODE_CONE_PATTERNS : MODE_ALL_PATTERNS;
191+
if (init_opts.cone_mode) {
192+
mode = MODE_CONE_PATTERNS;
193+
core_sparse_checkout_cone = 1;
194+
} else
195+
mode = MODE_ALL_PATTERNS;
151196

152197
if (sc_set_config(mode))
153198
return 1;
@@ -175,7 +220,8 @@ static int sparse_checkout_init(int argc, const char **argv)
175220
}
176221

177222
reset_dir:
178-
return update_working_directory();
223+
core_apply_sparse_checkout = 1;
224+
return update_working_directory(NULL);
179225
}
180226

181227
static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path)
@@ -260,6 +306,15 @@ static int write_patterns_and_update(struct pattern_list *pl)
260306
{
261307
char *sparse_filename;
262308
FILE *fp;
309+
int result;
310+
311+
result = update_working_directory(pl);
312+
313+
if (result) {
314+
clear_pattern_list(pl);
315+
update_working_directory(NULL);
316+
return result;
317+
}
263318

264319
sparse_filename = get_sparse_checkout_filename();
265320
fp = fopen(sparse_filename, "w");
@@ -270,9 +325,11 @@ static int write_patterns_and_update(struct pattern_list *pl)
270325
write_patterns_to_file(fp, pl);
271326

272327
fclose(fp);
328+
273329
free(sparse_filename);
330+
clear_pattern_list(pl);
274331

275-
return update_working_directory();
332+
return 0;
276333
}
277334

278335
static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
@@ -324,6 +381,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
324381
struct strbuf line = STRBUF_INIT;
325382
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
326383
hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
384+
pl.use_cone_patterns = 1;
327385

328386
if (set_opts.use_stdin) {
329387
while (!strbuf_getline(&line, stdin))
@@ -378,7 +436,8 @@ static int sparse_checkout_disable(int argc, const char **argv)
378436
fprintf(fp, "/*\n");
379437
fclose(fp);
380438

381-
if (update_working_directory())
439+
core_apply_sparse_checkout = 1;
440+
if (update_working_directory(NULL))
382441
die(_("error while refreshing working directory"));
383442

384443
unlink(sparse_filename);

t/t1091-sparse-checkout-builtin.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,32 @@ test_expect_success 'cone mode: set with nested folders' '
248248
test_cmp repo/.git/info/sparse-checkout expect
249249
'
250250

251+
test_expect_success 'revert to old sparse-checkout on bad update' '
252+
echo update >repo/deep/deeper2/a &&
253+
cp repo/.git/info/sparse-checkout expect &&
254+
test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
255+
test_i18ngrep "Cannot update sparse checkout" err &&
256+
test_cmp repo/.git/info/sparse-checkout expect &&
257+
ls repo/deep >dir &&
258+
cat >expect <<-EOF &&
259+
a
260+
deeper1
261+
deeper2
262+
EOF
263+
test_cmp dir expect
264+
'
265+
266+
test_expect_success 'revert to old sparse-checkout on empty update' '
267+
git init empty-test &&
268+
(
269+
echo >file &&
270+
git add file &&
271+
git commit -m "test" &&
272+
test_must_fail git sparse-checkout set nothing 2>err &&
273+
test_i18ngrep "Sparse checkout leaves no entry on working directory" err &&
274+
test_i18ngrep ! ".git/index.lock" err &&
275+
git sparse-checkout set file
276+
)
277+
'
278+
251279
test_done

unpack-trees.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,7 +1511,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
15111511
memset(&pl, 0, sizeof(pl));
15121512
if (!core_apply_sparse_checkout || !o->update)
15131513
o->skip_sparse_checkout = 1;
1514-
if (!o->skip_sparse_checkout) {
1514+
if (!o->skip_sparse_checkout && !o->pl) {
15151515
char *sparse = git_pathdup("info/sparse-checkout");
15161516
pl.use_cone_patterns = core_sparse_checkout_cone;
15171517
if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0)
@@ -1684,7 +1684,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
16841684

16851685
done:
16861686
trace_performance_leave("unpack_trees");
1687-
clear_pattern_list(&pl);
1687+
if (!o->keep_pattern_list)
1688+
clear_pattern_list(&pl);
16881689
return ret;
16891690

16901691
return_failed:

unpack-trees.h

Lines changed: 2 additions & 1 deletion
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)