Subject: Re: [PATCH v5 02/17] sparse-checkout: create 'init' subcommand

From: SZEDER Gábor <[email-masked]>
Date: Tue, 19 Nov 2019 15:33:08 +0100

On Mon, Oct 21, 2019 at 01:56:11PM +0000, Derrick Stolee via GitGitGadget wrote:
> From: Derrick Stolee <[email-masked]>
> 
> 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.

Reading this I was wandering what those "initial set of patterns"
might be ...

> 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 <[email-masked]>
> ---
>  Documentation/git-sparse-checkout.txt | 11 ++++
>  builtin/sparse-checkout.c             | 79 ++++++++++++++++++++++++++-
>  t/t1091-sparse-checkout-builtin.sh    | 41 ++++++++++++++
>  3 files changed, 130 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
> index 9d6ca22917..930a361567 100644
> --- a/Documentation/git-sparse-checkout.txt
> +++ b/Documentation/git-sparse-checkout.txt
> @@ -30,6 +30,17 @@ COMMANDS
>  'list'::
>  	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.

... and then reading this I was wandering why these are those "initial
set of patterns".

> ++
> +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
>  ---------------
> diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
> index 5717c9b2cb..77aa52ca01 100644
> --- a/builtin/sparse-checkout.c
> +++ b/builtin/sparse-checkout.c
> @@ -8,7 +8,7 @@
>  #include "strbuf.h"
>  
>  static char const * const builtin_sparse_checkout_usage[] = {
> -	N_("git sparse-checkout list"),
> +	N_("git sparse-checkout (init|list)"),
>  	NULL
>  };
>  
> @@ -59,6 +59,81 @@ static int sparse_checkout_list(int argc, const char **argv)
>  	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 sc_set_config(enum sparse_checkout_mode mode)

Nit: s/sc_//, perhaps?  I suppose that "sc" prefix stands for "sparse
checkout", but this is a static function in
'builtin/sparse-checkout.c', so it doesn't need a distinguising
prefix.  Even at the end of this patch series no other functions have
this "sc" prefix.

> +{
> +	struct argv_array argv = ARGV_ARRAY_INIT;

This 'argv_array' is not cleared at the end of the function, but...

> +	if (git_config_set_gently("extensions.worktreeConfig", "true")) {
> +		error(_("failed to set extensions.worktreeConfig setting"));
> +		return 1;
> +	}
> +
> +	argv_array_pushl(&argv, "config", "--worktree", "core.sparseCheckout", NULL);
> +
> +	if (mode)
> +		argv_array_pushl(&argv, "true", NULL);
> +	else
> +		argv_array_pushl(&argv, "false", NULL);
> +
> +	if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
> +		error(_("failed to enable core.sparseCheckout"));
> +		return 1;
> +	}

Why the external 'git config' invocation?

  git_config_set_in_file_gently(git_path("config.worktree"),
                                "core.sparseCheckout",
                                mode ? "true" : "false")

> +
> +	return 0;
> +}
> +
> +static int sparse_checkout_init(int argc, const char **argv)
> +{
> +	struct pattern_list pl;
> +	char *sparse_filename;
> +	FILE *fp;
> +	int res;
> +
> +	if (sc_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");
> +	free(sparse_filename);
> +	fprintf(fp, "/*\n!/*/\n");

What if this fprintf() call were to fail?

> +	fclose(fp);
> +
> +reset_dir:
> +	return update_working_directory();
> +}
> +
>  int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
>  {
>  	static struct option builtin_sparse_checkout_options[] = {
> @@ -79,6 +154,8 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
>  	if (argc > 0) {
>  		if (!strcmp(argv[0], "list"))
>  			return sparse_checkout_list(argc, argv);
> +		if (!strcmp(argv[0], "init"))
> +			return sparse_checkout_init(argc, argv);
>  	}
>  
>  	usage_with_options(builtin_sparse_checkout_usage,
> diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
> index 9b73d44907..cd56cc384b 100755
> --- a/t/t1091-sparse-checkout-builtin.sh
> +++ b/t/t1091-sparse-checkout-builtin.sh
> @@ -42,4 +42,45 @@ test_expect_success 'git sparse-checkout list (populated)' '
>  	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 &&
> +	git -C repo config --list >config &&
> +	test_i18ngrep "core.sparsecheckout=true" config &&

We have the 'test_cmp_config' helper function to check the expected
value of configuration variables.

> +	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
> -- 
> gitgitgadget
>