Skip to content

Commit 94c0956

Browse files
derrickstoleegitster
authored andcommitted
sparse-checkout: create builtin with 'list' subcommand
The sparse-checkout feature is mostly hidden to users, as its only documentation is supplementary information in the docs for 'git read-tree'. In addition, users need to know how to edit the .git/info/sparse-checkout file with the right patterns, then run the appropriate 'git read-tree -mu HEAD' command. Keeping the working directory in sync with the sparse-checkout file requires care. Begin an effort to make the sparse-checkout feature a porcelain feature by creating a new 'git sparse-checkout' builtin. This builtin will be the preferred mechanism for manipulating the sparse-checkout file and syncing the working directory. The documentation provided is adapted from the "git read-tree" documentation with a few edits for clarity in the new context. Extra sections are added to hint toward a future change to a more restricted pattern set. Helped-by: Elijah Newren <newren@gmail.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent d9f6f3b commit 94c0956

File tree

9 files changed

+226
-1
lines changed

9 files changed

+226
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
/git-show-branch
159159
/git-show-index
160160
/git-show-ref
161+
/git-sparse-checkout
161162
/git-stage
162163
/git-stash
163164
/git-status

Documentation/git-read-tree.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ support.
436436
SEE ALSO
437437
--------
438438
linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
439-
linkgit:gitignore[5]
439+
linkgit:gitignore[5]; linkgit:git-sparse-checkout[1];
440440

441441
GIT
442442
---
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
git-sparse-checkout(1)
2+
======================
3+
4+
NAME
5+
----
6+
git-sparse-checkout - Initialize and modify the sparse-checkout
7+
configuration, which reduces the checkout to a set of paths
8+
given by a list of atterns.
9+
10+
11+
SYNOPSIS
12+
--------
13+
[verse]
14+
'git sparse-checkout <subcommand> [options]'
15+
16+
17+
DESCRIPTION
18+
-----------
19+
20+
Initialize and modify the sparse-checkout configuration, which reduces
21+
the checkout to a set of paths given by a list of patterns.
22+
23+
THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR, AND THE BEHAVIOR OF OTHER
24+
COMMANDS IN THE PRESENCE OF SPARSE-CHECKOUTS, WILL LIKELY CHANGE IN
25+
THE FUTURE.
26+
27+
28+
COMMANDS
29+
--------
30+
'list'::
31+
Provide a list of the contents in the sparse-checkout file.
32+
33+
34+
SPARSE CHECKOUT
35+
---------------
36+
37+
"Sparse checkout" allows populating the working directory sparsely.
38+
It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
39+
Git whether a file in the working directory is worth looking at. If
40+
the skip-worktree bit is set, then the file is ignored in the working
41+
directory. Git will not populate the contents of those files, which
42+
makes a sparse checkout helpful when working in a repository with many
43+
files, but only a few are important to the current user.
44+
45+
The `$GIT_DIR/info/sparse-checkout` file is used to define the
46+
skip-worktree reference bitmap. When Git updates the working
47+
directory, it updates the skip-worktree bits in the index based
48+
on this file. The files matching the patterns in the file will
49+
appear in the working directory, and the rest will not.
50+
51+
## FULL PATTERN SET
52+
53+
By default, the sparse-checkout file uses the same syntax as `.gitignore`
54+
files.
55+
56+
While `$GIT_DIR/info/sparse-checkout` is usually used to specify what
57+
files are included, you can also specify what files are _not_ included,
58+
using negative patterns. For example, to remove the file `unwanted`:
59+
60+
----------------
61+
/*
62+
!unwanted
63+
----------------
64+
65+
Another tricky thing is fully repopulating the working directory when you
66+
no longer want sparse checkout. You cannot just disable "sparse
67+
checkout" because skip-worktree bits are still in the index and your working
68+
directory is still sparsely populated. You should re-populate the working
69+
directory with the `$GIT_DIR/info/sparse-checkout` file content as
70+
follows:
71+
72+
----------------
73+
/*
74+
----------------
75+
76+
Then you can disable sparse checkout. Sparse checkout support in 'git
77+
checkout' and similar commands is disabled by default. You need to
78+
set `core.sparseCheckout` to `true` in order to have sparse checkout
79+
support.
80+
81+
SEE ALSO
82+
--------
83+
84+
linkgit:git-read-tree[1]
85+
linkgit:gitignore[5]
86+
87+
GIT
88+
---
89+
Part of the linkgit:git[1] suite

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,7 @@ BUILTIN_OBJS += builtin/shortlog.o
11251125
BUILTIN_OBJS += builtin/show-branch.o
11261126
BUILTIN_OBJS += builtin/show-index.o
11271127
BUILTIN_OBJS += builtin/show-ref.o
1128+
BUILTIN_OBJS += builtin/sparse-checkout.o
11281129
BUILTIN_OBJS += builtin/stash.o
11291130
BUILTIN_OBJS += builtin/stripspace.o
11301131
BUILTIN_OBJS += builtin/submodule--helper.o

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix);
225225
int cmd_show(int argc, const char **argv, const char *prefix);
226226
int cmd_show_branch(int argc, const char **argv, const char *prefix);
227227
int cmd_show_index(int argc, const char **argv, const char *prefix);
228+
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix);
228229
int cmd_status(int argc, const char **argv, const char *prefix);
229230
int cmd_stash(int argc, const char **argv, const char *prefix);
230231
int cmd_stripspace(int argc, const char **argv, const char *prefix);

builtin/sparse-checkout.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include "builtin.h"
2+
#include "config.h"
3+
#include "dir.h"
4+
#include "parse-options.h"
5+
#include "pathspec.h"
6+
#include "repository.h"
7+
#include "run-command.h"
8+
#include "strbuf.h"
9+
10+
static char const * const builtin_sparse_checkout_usage[] = {
11+
N_("git sparse-checkout list"),
12+
NULL
13+
};
14+
15+
static char *get_sparse_checkout_filename(void)
16+
{
17+
return git_pathdup("info/sparse-checkout");
18+
}
19+
20+
static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
21+
{
22+
int i;
23+
24+
for (i = 0; i < pl->nr; i++) {
25+
struct path_pattern *p = pl->patterns[i];
26+
27+
if (p->flags & PATTERN_FLAG_NEGATIVE)
28+
fprintf(fp, "!");
29+
30+
fprintf(fp, "%s", p->pattern);
31+
32+
if (p->flags & PATTERN_FLAG_MUSTBEDIR)
33+
fprintf(fp, "/");
34+
35+
fprintf(fp, "\n");
36+
}
37+
}
38+
39+
static int sparse_checkout_list(int argc, const char **argv)
40+
{
41+
struct pattern_list pl;
42+
char *sparse_filename;
43+
int res;
44+
45+
memset(&pl, 0, sizeof(pl));
46+
47+
sparse_filename = get_sparse_checkout_filename();
48+
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
49+
free(sparse_filename);
50+
51+
if (res < 0) {
52+
warning(_("this worktree is not sparse (sparse-checkout file may not exist)"));
53+
return 0;
54+
}
55+
56+
write_patterns_to_file(stdout, &pl);
57+
clear_pattern_list(&pl);
58+
59+
return 0;
60+
}
61+
62+
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
63+
{
64+
static struct option builtin_sparse_checkout_options[] = {
65+
OPT_END(),
66+
};
67+
68+
if (argc == 2 && !strcmp(argv[1], "-h"))
69+
usage_with_options(builtin_sparse_checkout_usage,
70+
builtin_sparse_checkout_options);
71+
72+
argc = parse_options(argc, argv, prefix,
73+
builtin_sparse_checkout_options,
74+
builtin_sparse_checkout_usage,
75+
PARSE_OPT_STOP_AT_NON_OPTION);
76+
77+
git_config(git_default_config, NULL);
78+
79+
if (argc > 0) {
80+
if (!strcmp(argv[0], "list"))
81+
return sparse_checkout_list(argc, argv);
82+
}
83+
84+
usage_with_options(builtin_sparse_checkout_usage,
85+
builtin_sparse_checkout_options);
86+
}

command-list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ git-show-index plumbinginterrogators
166166
git-show-ref plumbinginterrogators
167167
git-sh-i18n purehelpers
168168
git-sh-setup purehelpers
169+
git-sparse-checkout mainporcelain worktree
169170
git-stash mainporcelain
170171
git-stage complete
171172
git-status mainporcelain info

git.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ static struct cmd_struct commands[] = {
572572
{ "show-branch", cmd_show_branch, RUN_SETUP },
573573
{ "show-index", cmd_show_index },
574574
{ "show-ref", cmd_show_ref, RUN_SETUP },
575+
{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
575576
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
576577
/*
577578
* NEEDSWORK: Until the builtin stash is thoroughly robust and no

t/t1091-sparse-checkout-builtin.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/sh
2+
3+
test_description='sparse checkout builtin tests'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' '
8+
git init repo &&
9+
(
10+
cd repo &&
11+
echo "initial" >a &&
12+
mkdir folder1 folder2 deep &&
13+
mkdir deep/deeper1 deep/deeper2 &&
14+
mkdir deep/deeper1/deepest &&
15+
cp a folder1 &&
16+
cp a folder2 &&
17+
cp a deep &&
18+
cp a deep/deeper1 &&
19+
cp a deep/deeper2 &&
20+
cp a deep/deeper1/deepest &&
21+
git add . &&
22+
git commit -m "initial commit"
23+
)
24+
'
25+
26+
test_expect_success 'git sparse-checkout list (empty)' '
27+
git -C repo sparse-checkout list >list 2>err &&
28+
test_must_be_empty list &&
29+
test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
30+
'
31+
32+
test_expect_success 'git sparse-checkout list (populated)' '
33+
test_when_finished rm -f repo/.git/info/sparse-checkout &&
34+
cat >repo/.git/info/sparse-checkout <<-EOF &&
35+
/folder1/*
36+
/deep/
37+
**/a
38+
!*bin*
39+
EOF
40+
cp repo/.git/info/sparse-checkout expect &&
41+
git -C repo sparse-checkout list >list &&
42+
test_cmp expect list
43+
'
44+
45+
test_done

0 commit comments

Comments
 (0)