threads / discuss / 33254

Hard reset of a subdirectory in a sparse checkout setting

Subject: Hard reset of a subdirectory in a sparse checkout setting

## tl;dr

17 messages between Mar 22, 2013 and May 14, 2013.

replies: 16people: 5as markdown or json

Kirill Müller· Mar 22, 2013, 10:28 UTC · lore
Hi

I can't find a neat way to "git reset --hard" a subdirectory of a checkout without reading in directories or files which are excluded by a sparse checkout. This has been asked on StackOverflow in greater detail, but the "right" answer is still missing: http://stackoverflow.com/q/15404535/946850

The options I see are:
- git checkout . (will restore excluded directories)
- git reset --hard (won't accept a path argument)
- git diff | patch -R (awkward)
What's the proper way to do this in Git?

The script below illustrates the problem. The proper command should be inserted below the "How to make files ..." comment -- the current command "git checkout -- a" will restore the file a/c/ac which is supposed to be excluded by the sparse checkout. Note that I do not want to explicitly restore a/a and a/b, I only "know" a and want to restore everything below. And I also don't "know" b, or which other directories reside on the same level as a.

Thank you for your help.
Cheers
Kirill
#!/bin/sh
rm -rf repo; git init repo; cd repo
for f in a b; do
   for g in a b c; do
     mkdir -p $f/$g
     touch $f/$g/$f$g
     git add $f/$g
     git commit -m "added $f/$g"
   done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f

rm a/a/aa rm a/b/ab echo >> b/a/ba echo "After modifying:" find * -type f git status

# How to make files a/* reappear without changing b and without recreating a/c? git checkout -- a

echo "After checkout:" git status find * -type f

Duy Nguyen· Mar 23, 2013, 09:59 UTC · re: Kirill Müller · lore

Re: Hard reset of a subdirectory in a sparse checkout setting

On Fri, Mar 22, 2013 at 5:28 PM, Kirill Müller <kirill.mueller@ivt.baug.ethz.ch> wrote:

Show 12 quoted lines
> I can't find a neat way to "git reset --hard" a subdirectory of a checkout
> without reading in directories or files which are excluded by a sparse
> checkout. This has been asked on StackOverflow in greater detail, but the
> "right" answer is still missing: http://stackoverflow.com/q/15404535/946850
>
> The options I see are:
>
> - git checkout . (will restore excluded directories)
>
> - git reset --hard (won't accept a path argument)
>
> - git diff | patch -R (awkward)
Another way is
git diff --name-only -z|xargs -0 git checkout
> What's the proper way to do this in Git?

I think adding --sparse to git checkout may be more user-friendly. And it looks like a simple change. I'll make a patch soon (I need some time to recall how sparse checkout interacts with git-checkout).

-- 
Duy
Kirill Müller· Mar 23, 2013, 13:02 UTC · re: Duy Nguyen · lore

Re: Hard reset of a subdirectory in a sparse checkout setting

On 03/23/2013 10:59 AM, Duy Nguyen wrote:
Show 6 quoted lines
> On Fri, Mar 22, 2013 at 5:28 PM, Kirill Müller
> <kirill.mueller@ivt.baug.ethz.ch> wrote:
>> What's the proper way to do this in Git?
> I think adding --sparse to git checkout may be more user-friendly. And
> it looks like a simple change. I'll make a patch soon (I need some
> time to recall how sparse checkout interacts with git-checkout).

Thanks for looking into it. Would it mean an extra switch to the checkout command, or default behavior?

-Kirill
Nguyễn Thái Ngọc Duy· Mar 24, 2013, 05:06 UTC · re: Kirill Müller · lore

[PATCH] checkout: add --sparse for restoring files in sparse checkout mode

"git checkout -- <paths>" is usually used to restore all modified files in <paths>. In sparse checkout mode, this command is overloaded with another meaning: to add back all files in <paths> that are excluded by sparse patterns.

Add "--sparse" option to do what normal mode does: restore all modified files and nothing else.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 I'm not sure if rejecting --sparse when it has no effects (e.g.
 switching branches or interactive mode) like this patch does is a
 good idea. I think it's good in general to spot unused options. On
 the other hand, people may want to add an alias
 "co = checkout --sparse" to make this behavior "default". In that
 case silently ignoring --sparse may be a good idea.
 Documentation/git-checkout.txt       |  7 +++++++
 builtin/checkout.c                   | 18 ++++++++++++++++--
 t/t1011-read-tree-sparse-checkout.sh | 12 ++++++++++++
 3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 8edcdca..45a2b53 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -180,6 +180,13 @@ branch by running "git rm -rf ." from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
+
+--sparse::
+	In sparse checkout mode, `git checkout -- <paths>` would
+	update all entries matched by <paths> regardless sparse
+	patterns. This option only updates entries matched by <paths>
+	and sparse patterns.
+
 -m::
 --merge::
 	When switching branches,
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a9c1b5a..3a32145 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -35,6 +35,7 @@ struct checkout_opts {
 	int force_detach;
 	int writeout_stage;
 	int overwrite_ignore;
+	int sparse;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -224,7 +225,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 	struct checkout state;
 	static char *ps_matched;
 	unsigned char rev[20];
-	int flag;
+	int flag, sparse;
 	struct commit *head;
 	int errs = 0;
 	int stage = opts->writeout_stage;
@@ -254,9 +255,13 @@ static int checkout_paths(const struct checkout_opts *opts,
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
 		    opts->new_branch);
 
-	if (opts->patch_mode)
+	if (opts->patch_mode) {
+		if (opts->sparse != -1)
+			die(_("--[no-]sparse cannot be used in interactive mode"));
 		return run_add_interactive(revision, "--patch=checkout",
 					   opts->pathspec);
+	}
+	sparse = opts->sparse != -1 ? opts->sparse : 0;
 
 	lock_file = xcalloc(1, sizeof(struct lock_file));
 
@@ -275,6 +280,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		struct cache_entry *ce = active_cache[pos];
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			continue;
+		if (sparse && ce_skip_worktree(ce))
+			continue;
 		match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
 	}
 
@@ -315,6 +322,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		struct cache_entry *ce = active_cache[pos];
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			continue;
+		if (sparse && ce_skip_worktree(ce))
+			continue;
 		if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state, NULL);
@@ -963,6 +972,9 @@ static int checkout_branch(struct checkout_opts *opts,
 	if (opts->pathspec)
 		die(_("paths cannot be used with switching branches"));
 
+	if (opts->sparse != -1)
+		die(_("--[no-]sparse can only be used with paths"));
+
 	if (opts->patch_mode)
 		die(_("'%s' cannot be used with switching branches"),
 		    "--patch");
@@ -1029,6 +1041,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "sparse", &opts.sparse, N_("limit pathspecs to sparse entries only")),
 		{ OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
 		  N_("second guess 'git checkout no-such-branch'"),
 		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
@@ -1039,6 +1052,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&new, 0, sizeof(new));
 	opts.overwrite_ignore = 1;
 	opts.prefix = prefix;
+	opts.sparse = -1;
 
 	gitmodules_config();
 	git_config(git_checkout_config, &opts);
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 5c0053a..1441fbe 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -250,4 +250,16 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success 'checkout --sparse' '
+	echo "*" >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	test -f init.t &&
+	echo sub >.git/info/sparse-checkout &&
+	git checkout &&
+	echo modified >> sub/added &&
+	git checkout --sparse . &&
+	! test -f init.t &&
+	git diff --exit-code HEAD
+'
+
 test_done
-- 
1.8.2.83.gc99314b
Eric Sunshine· Mar 24, 2013, 06:12 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH] checkout: add --sparse for restoring files in sparse checkout mode

On Sun, Mar 24, 2013 at 1:06 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
Show 8 quoted lines
> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
> index 8edcdca..45a2b53 100644
> --- a/Documentation/git-checkout.txt
> +++ b/Documentation/git-checkout.txt
> @@ -180,6 +180,13 @@ branch by running "git rm -rf ." from the top level of the working tree.
> +--sparse::
> +       In sparse checkout mode, `git checkout -- <paths>` would
> +       update all entries matched by <paths> regardless sparse
s/regardless/regardless of/
> +       patterns. This option only updates entries matched by <paths>
> +       and sparse patterns.
> +
Jonathan Nieder· Mar 24, 2013, 18:17 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH] checkout: add --sparse for restoring files in sparse checkout mode

Hi,
Nguyễn Thái Ngọc Duy wrote:
Show 12 quoted lines
> --- a/Documentation/git-checkout.txt
> +++ b/Documentation/git-checkout.txt
> @@ -180,6 +180,13 @@ branch by running "git rm -rf ." from the top level of the working tree.
>  Afterwards you will be ready to prepare your new files, repopulating the
>  working tree, by copying them from elsewhere, extracting a tarball, etc.
>
> +
> +--sparse::
> +	In sparse checkout mode, `git checkout -- <paths>` would
> +	update all entries matched by <paths> regardless sparse
> +	patterns. This option only updates entries matched by <paths>
> +	and sparse patterns.
Hm, should this be the default?
In principle, I would expect
	git checkout -- .

to make the worktree match the index, respecting the sparse checkout. And something like

	git checkout --widen -- .

to change the sparse checkout pattern. But of course it is easily possible that I am missing some details of how sparse checkout is used in practice.

What do you think? Jonathan

Kirill Müller· Mar 24, 2013, 19:50 UTC · re: Jonathan Nieder · lore

Re: [PATCH] checkout: add --sparse for restoring files in sparse checkout mode

Hi
On 03/24/2013 07:17 PM, Jonathan Nieder wrote:
Show 32 quoted lines
> Hi,
>
> Nguyễn Thái Ngọc Duy wrote:
>
>> --- a/Documentation/git-checkout.txt
>> +++ b/Documentation/git-checkout.txt
>> @@ -180,6 +180,13 @@ branch by running "git rm -rf ." from the top level of the working tree.
>>   Afterwards you will be ready to prepare your new files, repopulating the
>>   working tree, by copying them from elsewhere, extracting a tarball, etc.
>>
>> +
>> +--sparse::
>> +	In sparse checkout mode, `git checkout -- <paths>` would
>> +	update all entries matched by <paths> regardless sparse
>> +	patterns. This option only updates entries matched by <paths>
>> +	and sparse patterns.
> Hm, should this be the default?
>
> In principle, I would expect
>
> 	git checkout -- .
>
> to make the worktree match the index, respecting the sparse checkout.
> And something like
>
> 	git checkout --widen -- .
>
> to change the sparse checkout pattern.  But of course it is easily
> possible that I am missing some details of how sparse checkout is
> used in practice.
>
> What do you think?

Thank you for your opinion. I'd second that. When I do a sparse checkout, I want to see only the directories I have included and not excluded, unless I explicitly change that.

-Kirill
Duy Nguyen· Mar 25, 2013, 01:34 UTC · re: Jonathan Nieder · lore

Re: [PATCH] checkout: add --sparse for restoring files in sparse checkout mode

On Mon, Mar 25, 2013 at 1:17 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 22 quoted lines
>> +--sparse::
>> +     In sparse checkout mode, `git checkout -- <paths>` would
>> +     update all entries matched by <paths> regardless sparse
>> +     patterns. This option only updates entries matched by <paths>
>> +     and sparse patterns.
>
> Hm, should this be the default?
>
> In principle, I would expect
>
>         git checkout -- .
>
> to make the worktree match the index, respecting the sparse checkout.
> And something like
>
>         git checkout --widen -- .
>
> to change the sparse checkout pattern.  But of course it is easily
> possible that I am missing some details of how sparse checkout is
> used in practice.
>
> What do you think?

Changing the default may involve a painful transition phase (e.g. "add -u"). I think making it the default via alias should be good enough in most cases. We also need to think how it impacts checkout usage in scripts. I think it might be ok, but I haven't finished my morning coffee yet, so..

-- 
Duy
Jonathan Nieder· Mar 25, 2013, 01:57 UTC · re: Duy Nguyen · lore

Re: [PATCH] checkout: add --sparse for restoring files in sparse checkout mode

Duy Nguyen wrote:
> On Mon, Mar 25, 2013 at 1:17 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 12 quoted lines
>> Hm, should this be the default?
>>
>> In principle, I would expect
>>
>>         git checkout -- .
>>
>> to make the worktree match the index, respecting the sparse checkout.
>> And something like
>>
>>         git checkout --widen -- .
>>
>> to change the sparse checkout pattern.
[...]
> Changing the default may involve a painful transition phase (e.g. "add
> -u").

I don't think it needs to. There aren't many people using sparse checkout even these days, and I think they'd generally be happy about the change.

But if we want to be conservative until some later point (like 2.1), perhaps --sparse should be named something like --no-widen? That way, I can do

	git checkout --no-widen -- .

to make the worktree match the index, respecting the sparse checkout. And I can do

	git checkout --widen -- .

to change the sparse checkout pattern. Meanwhile the confusing command

	git checkout -- .

would be ill-defined for sparse checkouts --- in past git versions, if I understand you correctly it acted like --widen, while in some unspecified future version it may change to mean --no-widen. No need for warnings because I doubt anyone is relying on either behavior.

Would that work? Jonathan

Nguyễn Thái Ngọc Duy· Mar 30, 2013, 10:02 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v2] checkout: add --no-widen for restoring files in sparse checkout mode

"git checkout -- <paths>" is usually used to restore all modified files in <paths>. In sparse checkout mode, this command is overloaded with another meaning: to add back all files in <paths> that are excluded by sparse patterns.

Add "--no-widen" option to do what normal mode does: restore all modified files and nothing else. The user can make it "default" by putting it to an alias (e.g. "co = checkout --no-widen") and get back to the original behavior with "git co --widen".

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 --sparse -> --no-widen and git-checkout does not error out when the
 option is specified but has no effects. Let's see how it works out.
 We could change the default from --widen to --no-widen later.
 Documentation/git-checkout.txt       | 11 +++++++++++
 builtin/checkout.c                   |  7 +++++++
 t/t1011-read-tree-sparse-checkout.sh | 12 ++++++++++++
 3 files changed, 30 insertions(+)
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 8edcdca..538b8b8 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -180,6 +180,17 @@ branch by running "git rm -rf ." from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
+--no-widen::
+	In sparse checkout mode, `git checkout -- <paths>` would
+	update all entries matched by <paths> regardless of sparse
+	patterns. This option only updates entries matched by <paths>
+	and sparse patterns.
+
+--widen::
+	Revert the effect of `--no-widen` if specified and make
+	`git checkout -- <paths>` update all entries matched by
+	<paths> regardless of sparse patterns.
+
 -m::
 --merge::
 	When switching branches,
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a9c1b5a..246de33 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -35,6 +35,7 @@ struct checkout_opts {
 	int force_detach;
 	int writeout_stage;
 	int overwrite_ignore;
+	int widen_sparse;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -275,6 +276,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		struct cache_entry *ce = active_cache[pos];
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			continue;
+		if (!opts->widen_sparse && ce_skip_worktree(ce))
+			continue;
 		match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
 	}
 
@@ -315,6 +318,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 		struct cache_entry *ce = active_cache[pos];
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			continue;
+		if (!opts->widen_sparse && ce_skip_worktree(ce))
+			continue;
 		if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state, NULL);
@@ -1029,6 +1034,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "widen", &opts.widen_sparse, N_("do not limit pathspecs to sparse entries only (default)")),
 		{ OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
 		  N_("second guess 'git checkout no-such-branch'"),
 		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
@@ -1039,6 +1045,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&new, 0, sizeof(new));
 	opts.overwrite_ignore = 1;
 	opts.prefix = prefix;
+	opts.widen_sparse = 1;
 
 	gitmodules_config();
 	git_config(git_checkout_config, &opts);
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 5c0053a..4a45714 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -250,4 +250,16 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success 'checkout --no-widen' '
+	echo "*" >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	test -f init.t &&
+	echo sub >.git/info/sparse-checkout &&
+	git checkout &&
+	echo modified >> sub/added &&
+	git checkout --no-widen . &&
+	! test -f init.t &&
+	git diff --exit-code HEAD
+'
+
 test_done
-- 
1.8.2.83.gc99314b
Jonathan Nieder· Apr 1, 2013, 04:48 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v2] checkout: add --no-widen for restoring files in sparse checkout mode

Hi,
Nguyễn Thái Ngọc Duy wrote:
Show 7 quoted lines
> "git checkout -- <paths>" is usually used to restore all modified
> files in <paths>. In sparse checkout mode, this command is overloaded
> with another meaning: to add back all files in <paths> that are
> excluded by sparse patterns.
>
> Add "--no-widen" option to do what normal mode does: restore all
> modified files and nothing else.

In an ideal world, I would like "git checkout --widen" to modify the .git/info/sparse-checkout file, to be able to do:

	git clone --sparse-checkout=Documentation git://repo.or.cz/git.git
	cd git
	git checkout --widen -- README COPYING INSTALL

and hack on a tree with Documentation/, README, COPYING, and INSTALL present with no actual code to distract. And "git checkout --no-widen" could be a way to ask to respect the existing sparse pattern.

This patch isn't about tweaking the sparse-checkout pattern; instead, it's about how "git checkout" interacts with the skip-worktree bit. Maybe a good name would be --respect-skip-worktree?

[...]
Show 16 quoted lines
> --- a/Documentation/git-checkout.txt
> +++ b/Documentation/git-checkout.txt
> @@ -180,6 +180,17 @@ branch by running "git rm -rf ." from the top level of the working tree.
>  Afterwards you will be ready to prepare your new files, repopulating the
>  working tree, by copying them from elsewhere, extracting a tarball, etc.
>  
> +--no-widen::
> +	In sparse checkout mode, `git checkout -- <paths>` would
> +	update all entries matched by <paths> regardless of sparse
> +	patterns. This option only updates entries matched by <paths>
> +	and sparse patterns.
> +
> +--widen::
> +	Revert the effect of `--no-widen` if specified and make
> +	`git checkout -- <paths>` update all entries matched by
> +	<paths> regardless of sparse patterns.
Perhaps, combining the descriptions of the positive and negative forms:
	--respect-skip-worktree::
		By default, `git checkout -- <paths>` creates or updates files
		matching <paths> regardless of the skip-worktree bit.  This
		option makes 'git checkout' skips entries with the
		skip-worktree bit set, which can be useful in sparse checkout
		mode.

I'm afraid I can't imagine when --no-respect-skip-worktree would be useful. That can easily be a failure of my imagination, though.

What do you think?

Thanks, Jonathan

Junio C Hamano· Apr 1, 2013, 05:12 UTC · re: Jonathan Nieder · lore

Re: [PATCH v2] checkout: add --no-widen for restoring files in sparse checkout mode

Jonathan Nieder <jrnieder@gmail.com> writes:
Show 20 quoted lines
> Nguyễn Thái Ngọc Duy wrote:
>
>> "git checkout -- <paths>" is usually used to restore all modified
>> files in <paths>. In sparse checkout mode, this command is overloaded
>> with another meaning: to add back all files in <paths> that are
>> excluded by sparse patterns.
>>
>> Add "--no-widen" option to do what normal mode does: restore all
>> modified files and nothing else.
>
> In an ideal world, I would like "git checkout --widen" to modify the
> .git/info/sparse-checkout file, to be able to do:
>
> 	git clone --sparse-checkout=Documentation git://repo.or.cz/git.git
> 	cd git
> 	git checkout --widen -- README COPYING INSTALL
>
> and hack on a tree with Documentation/, README, COPYING, and INSTALL
> present with no actual code to distract.  And "git checkout --no-widen"
> could be a way to ask to respect the existing sparse pattern.

Yeah, I think the above makes tons of sense, and --widen would be an ideal name for that optional behaviour. When you are limited by your original sparse pathspecs, that would be a way to explicitly widen the paths you interact with. In that sense, making it off by default would be a sensible thing to do. When you limited yourself to a subset of dir/, you do not want "git checkout dir/" to automatically widen it by accident.

> This patch isn't about tweaking the sparse-checkout pattern; instead,
> it's about how "git checkout" interacts with the skip-worktree bit.
> Maybe a good name would be --respect-skip-worktree?

Yeah, but when would one want to say --no-respect without widening the sparce pathspecs?

Duy Nguyen· Apr 1, 2013, 05:13 UTC · re: Jonathan Nieder · lore

Re: [PATCH v2] checkout: add --no-widen for restoring files in sparse checkout mode

On Mon, Apr 1, 2013 at 11:48 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 22 quoted lines
> Hi,
>
> Nguyễn Thái Ngọc Duy wrote:
>
>> "git checkout -- <paths>" is usually used to restore all modified
>> files in <paths>. In sparse checkout mode, this command is overloaded
>> with another meaning: to add back all files in <paths> that are
>> excluded by sparse patterns.
>>
>> Add "--no-widen" option to do what normal mode does: restore all
>> modified files and nothing else.
>
> In an ideal world, I would like "git checkout --widen" to modify the
> .git/info/sparse-checkout file, to be able to do:
>
>         git clone --sparse-checkout=Documentation git://repo.or.cz/git.git
>         cd git
>         git checkout --widen -- README COPYING INSTALL
>
> and hack on a tree with Documentation/, README, COPYING, and INSTALL
> present with no actual code to distract.  And "git checkout --no-widen"
> could be a way to ask to respect the existing sparse pattern.

In an ideal world, I would spend more time on this and add --edit-sparse, which opens up $EDITOR, lets you edit the patterns and reapplies the patterns after $EDITOR exits (catching faults if possible). Unfortunately I don't use sparse checkout as much as before and therefore have little motivation to do it. I would really like narrow clone to replace sparse checkout, but I haven't made much progress on that front either. I'll try to get back on that once pathspec-magics topic is settled.

> This patch isn't about tweaking the sparse-checkout pattern; instead,
> it's about how "git checkout" interacts with the skip-worktree bit.
> Maybe a good name would be --respect-skip-worktree?
I'm bad at naming. If nobody objects, I'll take this as the new option name.
Show 25 quoted lines
>> --- a/Documentation/git-checkout.txt
>> +++ b/Documentation/git-checkout.txt
>> @@ -180,6 +180,17 @@ branch by running "git rm -rf ." from the top level of the working tree.
>>  Afterwards you will be ready to prepare your new files, repopulating the
>>  working tree, by copying them from elsewhere, extracting a tarball, etc.
>>
>> +--no-widen::
>> +     In sparse checkout mode, `git checkout -- <paths>` would
>> +     update all entries matched by <paths> regardless of sparse
>> +     patterns. This option only updates entries matched by <paths>
>> +     and sparse patterns.
>> +
>> +--widen::
>> +     Revert the effect of `--no-widen` if specified and make
>> +     `git checkout -- <paths>` update all entries matched by
>> +     <paths> regardless of sparse patterns.
>
> Perhaps, combining the descriptions of the positive and negative forms:
>
>         --respect-skip-worktree::
>                 By default, `git checkout -- <paths>` creates or updates files
>                 matching <paths> regardless of the skip-worktree bit.  This
>                 option makes 'git checkout' skips entries with the
>                 skip-worktree bit set, which can be useful in sparse checkout
>                 mode.
OK
> I'm afraid I can't imagine when --no-respect-skip-worktree would be
> useful.  That can easily be a failure of my imagination, though.

There may be scripts that expect "git checkout -- foo" to reset everything in "foo". Or maybe you just want to check out a single file and do not bother to edit sparse patterns as you won't need it for long.

Jonathan Nieder· Apr 1, 2013, 06:18 UTC · re: Duy Nguyen · lore

Re: [PATCH v2] checkout: add --no-widen for restoring files in sparse checkout mode

Duy Nguyen wrote:
> On Mon, Apr 1, 2013 at 11:48 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:
Show 7 quoted lines
>> I'm afraid I can't imagine when --no-respect-skip-worktree would be
>> useful.  That can easily be a failure of my imagination, though.
>
> There may be scripts that expect "git checkout -- foo" to reset
> everything in "foo". Or maybe you just want to check out a single file
> and do not bother to edit sparse patterns as you won't need it for
> long.

Those scripts are bringing suffering on themselves by using porcelain instead of "git checkout-index". The usual way to look at a single file from the index is "git show :git.c >git.c".

So you've convinced me. I think --respect-skip-worktree can be just the normal behavior (and that there's no need for a --no-respect option). That would let me do:

	git clone --no-checkout git://repo.or.cz/git.git
	cd git
	echo '/Documentation/' >.git/info/sparse-checkout
	git config core.sparsecheckout true
	git checkout
	printf '%s\n' /README /INSTALL /COPYING >>.git/info/sparse-checkout
	git checkout
	... hack hack hack ...
	# return to what the index has
	git checkout .
without having to see any pesky actual source (*.c) files.
Nguyễn Thái Ngọc Duy· Apr 12, 2013, 23:12 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v3] checkout: add --ignore-skip-worktree-bits in sparse checkout mode

"git checkout -- <paths>" is usually used to restore all modified files in <paths>. In sparse checkout mode, this command is overloaded with another meaning: to add back all files in <paths> that are excluded by sparse patterns.

As the former makes more sense for day-to-day use. Switch it to the default and the latter enabled with --ignore-skip-worktree-bits.

While at there, add info/sparse-checkout to gitrepository-layout.txt
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt         |  6 ++++++
 Documentation/gitrepository-layout.txt |  4 ++++
 builtin/checkout.c                     |  5 +++++
 t/t1011-read-tree-sparse-checkout.sh   | 24 ++++++++++++++++++++++++
 t/t3001-ls-files-others-exclude.sh     |  2 +-
 5 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 8edcdca..23a9413 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -180,6 +180,12 @@ branch by running "git rm -rf ." from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
+--ignore-skip-worktree-bits::
+	In sparse checkout mode, `git checkout -- <paths>` would
+	update only entries matched by <paths> and sparse patterns
+	in $GIT_DIR/info/sparse-checkout. This option ignores
+	the sparse patterns and adds back any files in <paths>.
+
 -m::
 --merge::
 	When switching branches,
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index f0eef76..817337f 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -184,6 +184,10 @@ info/exclude::
 	'git clean' look at it but the core Git commands do not look
 	at it.  See also: linkgit:gitignore[5].
 
+info/sparse-checkout::
+	This file stores sparse checkout patterns.
+	See also: linkgit:git-read-tree[1].
+
 remotes::
 	Stores shorthands for URL and default refnames for use
 	when interacting with remote repositories via 'git fetch',
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f8033f4..4ed1ee7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -35,6 +35,7 @@ struct checkout_opts {
 	int force_detach;
 	int writeout_stage;
 	int overwrite_ignore;
+	int ignore_skipworktree;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -278,6 +279,8 @@ static int checkout_paths(const struct checkout_opts *opts,
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
 		ce->ce_flags &= ~CE_MATCHED;
+		if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+			continue;
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			/*
 			 * "git checkout tree-ish -- path", but this entry
@@ -1058,6 +1061,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "conflict", &conflict_style, N_("style"),
 			   N_("conflict style (merge or diff3)")),
 		OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
+		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+			 N_("do not limit pathspecs to sparse entries only")),
 		{ OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
 		  N_("second guess 'git checkout no-such-branch'"),
 		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 5c0053a..0c74bee 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -250,4 +250,28 @@ EOF
 	test_cmp expected actual
 '
 
+test_expect_success 'checkout without --ignore-skip-worktree-bits' '
+	echo "*" >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	test_path_is_file init.t &&
+	echo sub >.git/info/sparse-checkout &&
+	git checkout &&
+	echo modified >> sub/added &&
+	git checkout . &&
+	test_path_is_missing init.t &&
+	git diff --exit-code HEAD
+'
+
+test_expect_success 'checkout with --ignore-skip-worktree-bits' '
+	echo "*" >.git/info/sparse-checkout &&
+	git checkout -f top &&
+	test_path_is_file init.t &&
+	echo sub >.git/info/sparse-checkout &&
+	git checkout &&
+	echo modified >> sub/added &&
+	git checkout --ignore-skip-worktree-bits . &&
+	test_path_is_file init.t &&
+	git diff --exit-code HEAD
+'
+
 test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index efb7ebc..2d274bf 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -103,7 +103,7 @@ test_expect_success \
      test_cmp expect output'
 
 test_expect_success 'restore gitignore' '
-	git checkout $allignores &&
+	git checkout --ignore-skip-worktree-bits $allignores &&
 	rm .git/index
 '
 
-- 
1.8.2.82.gc24b958
Müller Kirill· May 14, 2013, 09:27 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v3] checkout: add --ignore-skip-worktree-bits in sparse checkout mode

Thank you, this looks nice. What needs to be done that this will eventually reach the git in my favorite distribution? Which version of Git will this be?

Cheers
Kirill
On Sat, 2013-04-13 at 09:12 +1000, Nguyễn Thái Ngọc Duy wrote:
Show 7 quoted lines
> "git checkout -- <paths>" is usually used to restore all modified
> files in <paths>. In sparse checkout mode, this command is overloaded
> with another meaning: to add back all files in <paths> that are
> excluded by sparse patterns.
> 
> As the former makes more sense for day-to-day use. Switch it to the
> default and the latter enabled with --ignore-skip-worktree-bits.
Duy Nguyen· May 14, 2013, 10:21 UTC · re: Müller Kirill · lore

Re: [PATCH v3] checkout: add --ignore-skip-worktree-bits in sparse checkout mode

On Tue, May 14, 2013 at 4:27 PM, Müller Kirill <kirill.mueller@ivt.baug.ethz.ch> wrote:

> Thank you, this looks nice. What needs to be done that this will
> eventually reach the git in my favorite distribution? Which version of
> Git will this be?

It'll be in the upcoming 1.8.3. -- Duy

← back to recent threads