sparse-checkout: create 'init' subcommand

Getting started with a sparse-checkout file can be daunting. Help
users start their sparse enlistment using 'git sparse-checkout init'.
This will set 'core.sparseCheckout=true' in their config, write
an initial set of patterns to the sparse-checkout file, and update
their working directory.

Make sure to use the `extensions.worktreeConfig` setting and write
the sparse checkout config to the worktree-specific config file.
This avoids confusing interactions with other worktrees.

The use of running another process for 'git read-tree' is sub-
optimal. This will be removed in a later change.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee 2019-11-21 22:04:34 +00:00 committed by Junio C Hamano
parent 94c0956b60
commit bab3c35908
3 changed files with 125 additions and 1 deletions

View file

@ -30,6 +30,17 @@ COMMANDS
'list':: 'list'::
Provide a list of the contents in the sparse-checkout file. Provide a list of the contents in the sparse-checkout file.
'init'::
Enable the `core.sparseCheckout` setting. If the
sparse-checkout file does not exist, then populate it with
patterns that match every file in the root directory and
no other directories, then will remove all directories tracked
by Git. Add patterns to the sparse-checkout file to
repopulate the working directory.
+
To avoid interfering with other worktrees, it first enables the
`extensions.worktreeConfig` setting and makes sure to set the
`core.sparseCheckout` setting in the worktree-specific config file.
SPARSE CHECKOUT SPARSE CHECKOUT
--------------- ---------------

View file

@ -8,7 +8,7 @@
#include "strbuf.h" #include "strbuf.h"
static char const * const builtin_sparse_checkout_usage[] = { static char const * const builtin_sparse_checkout_usage[] = {
N_("git sparse-checkout list"), N_("git sparse-checkout (init|list)"),
NULL NULL
}; };
@ -59,6 +59,77 @@ static int sparse_checkout_list(int argc, const char **argv)
return 0; return 0;
} }
static int update_working_directory(void)
{
struct argv_array argv = ARGV_ARRAY_INIT;
int result = 0;
argv_array_pushl(&argv, "read-tree", "-m", "-u", "HEAD", NULL);
if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
error(_("failed to update index with new sparse-checkout paths"));
result = 1;
}
argv_array_clear(&argv);
return result;
}
enum sparse_checkout_mode {
MODE_NO_PATTERNS = 0,
MODE_ALL_PATTERNS = 1,
};
static int set_config(enum sparse_checkout_mode mode)
{
const char *config_path;
if (git_config_set_gently("extensions.worktreeConfig", "true")) {
error(_("failed to set extensions.worktreeConfig setting"));
return 1;
}
config_path = git_path("config.worktree");
git_config_set_in_file_gently(config_path,
"core.sparseCheckout",
mode ? "true" : NULL);
return 0;
}
static int sparse_checkout_init(int argc, const char **argv)
{
struct pattern_list pl;
char *sparse_filename;
FILE *fp;
int res;
if (set_config(MODE_ALL_PATTERNS))
return 1;
memset(&pl, 0, sizeof(pl));
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
goto reset_dir;
}
/* initial mode: all blobs at root */
fp = xfopen(sparse_filename, "w");
if (!fp)
die(_("failed to open '%s'"), sparse_filename);
free(sparse_filename);
fprintf(fp, "/*\n!/*/\n");
fclose(fp);
reset_dir:
return update_working_directory();
}
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
{ {
static struct option builtin_sparse_checkout_options[] = { static struct option builtin_sparse_checkout_options[] = {
@ -79,6 +150,8 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
if (argc > 0) { if (argc > 0) {
if (!strcmp(argv[0], "list")) if (!strcmp(argv[0], "list"))
return sparse_checkout_list(argc, argv); return sparse_checkout_list(argc, argv);
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
} }
usage_with_options(builtin_sparse_checkout_usage, usage_with_options(builtin_sparse_checkout_usage,

View file

@ -42,4 +42,44 @@ test_expect_success 'git sparse-checkout list (populated)' '
test_cmp expect list test_cmp expect list
' '
test_expect_success 'git sparse-checkout init' '
git -C repo sparse-checkout init &&
cat >expect <<-EOF &&
/*
!/*/
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
test_cmp_config -C repo true core.sparsecheckout &&
ls repo >dir &&
echo a >expect &&
test_cmp expect dir
'
test_expect_success 'git sparse-checkout list after init' '
git -C repo sparse-checkout list >actual &&
cat >expect <<-EOF &&
/*
!/*/
EOF
test_cmp expect actual
'
test_expect_success 'init with existing sparse-checkout' '
echo "*folder*" >> repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout init &&
cat >expect <<-EOF &&
/*
!/*/
*folder*
EOF
test_cmp expect repo/.git/info/sparse-checkout &&
ls repo >dir &&
cat >expect <<-EOF &&
a
folder1
folder2
EOF
test_cmp expect dir
'
test_done test_done