Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparse-checkout builtin: upstreamable version #180

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
/git-show-branch
/git-show-index
/git-show-ref
/git-sparse-checkout
/git-stage
/git-stash
/git-status
Expand Down
2 changes: 1 addition & 1 deletion Documentation/git-read-tree.txt
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ support.
SEE ALSO
--------
linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
linkgit:gitignore[5]
linkgit:gitignore[5]; linkgit:git-sparse-checkout[1];

GIT
---
Expand Down
90 changes: 90 additions & 0 deletions Documentation/git-sparse-checkout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
git-sparse-checkout(1)
=======================

NAME
----
git-sparse-checkout - Initialize and modify the sparse-checkout
configuration, which reduces the checkout to a set of directories
given by a list of prefixes.


SYNOPSIS
--------
[verse]
'git sparse-checkout <subcommand> [options]'


DESCRIPTION
-----------

Initialize and modify the sparse-checkout configuration, which reduces
the checkout to a set of directories given by a list of prefixes.


COMMANDS
--------
'list'::
Provide a list of the contents in the sparse-checkout file.


SPARSE CHECKOUT
----------------

"Sparse checkout" allows populating the working directory sparsely.
It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
Git whether a file in the working directory is worth looking at. If
the skip-worktree bit is set, then the file is ignored in the working
directory. Git will not populate the contents of those files, which
makes a sparse checkout helpful when working in a repository with many
files, but only a few are important to the current user.

The `$GIT_DIR/info/sparse-checkout` file is used to define the
skip-worktree reference bitmap. When Git updates the working
directory, it resets the skip-worktree bit in the index based on this
file. If an entry
matches a pattern in this file, skip-worktree will not be set on
that entry. Otherwise, skip-worktree will be set.

Then it compares the new skip-worktree value with the previous one. If
skip-worktree turns from set to unset, it will add the corresponding
file back. If it turns from unset to set, that file will be removed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 paragraphs are a bit muddy. But I don't want to bog us down wordsmith-ing right now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also, many of them are copied directory from Documentation/git-read-tree.txt)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was afraid of that....


## FULL PATTERN SET

By default, the sparse-checkout file uses the same syntax as `.gitignore`
files.

While `$GIT_DIR/info/sparse-checkout` is usually used to specify what
files are in, you can also specify what files are _not_ in, using
negate patterns. For example, to remove the file `unwanted`:

----------------
/*
!unwanted
----------------

Another tricky thing is fully repopulating the working directory when you
no longer want sparse checkout. You cannot just disable "sparse
checkout" because skip-worktree bits are still in the index and your working
directory is still sparsely populated. You should re-populate the working
directory with the `$GIT_DIR/info/sparse-checkout` file content as
follows:

----------------
/*
----------------

Then you can disable sparse checkout. Sparse checkout support in 'git
read-tree' and similar commands is disabled by default. You need to
set `core.sparseCheckout` to `true` in order to have sparse checkout
support.

SEE ALSO
--------

linkgit:git-read-tree[1]
linkgit:gitignore[1]

GIT
---
Part of the linkgit:git[1] suite
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,7 @@ BUILTIN_OBJS += builtin/shortlog.o
BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-index.o
BUILTIN_OBJS += builtin/show-ref.o
BUILTIN_OBJS += builtin/sparse-checkout.o
BUILTIN_OBJS += builtin/stash.o
BUILTIN_OBJS += builtin/stripspace.o
BUILTIN_OBJS += builtin/submodule--helper.o
Expand Down
1 change: 1 addition & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix);
int cmd_show(int argc, const char **argv, const char *prefix);
int cmd_show_branch(int argc, const char **argv, const char *prefix);
int cmd_show_index(int argc, const char **argv, const char *prefix);
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix);
int cmd_status(int argc, const char **argv, const char *prefix);
int cmd_stash(int argc, const char **argv, const char *prefix);
int cmd_stripspace(int argc, const char **argv, const char *prefix);
Expand Down
90 changes: 90 additions & 0 deletions builtin/sparse-checkout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "builtin.h"
#include "config.h"
#include "dir.h"
#include "parse-options.h"
#include "pathspec.h"
#include "repository.h"
#include "run-command.h"
#include "strbuf.h"

static char const * const builtin_sparse_checkout_usage[] = {
N_("git sparse-checkout [list]"),
NULL
};

struct opts_sparse_checkout {
const char *subcommand;
int read_stdin;
} opts;

static char *get_sparse_checkout_filename(void)
{
return git_pathdup("info/sparse-checkout");
}

static void write_excludes_to_file(FILE *fp, struct exclude_list *el)
{
int i;

for (i = 0; i < el->nr; i++) {
struct exclude *x = el->excludes[i];

if (x->flags & EXC_FLAG_NEGATIVE)
fprintf(fp, "!");

fprintf(fp, "%s", x->pattern);

if (x->flags & EXC_FLAG_MUSTBEDIR)
fprintf(fp, "/");

fprintf(fp, "\n");
}
}

static int sparse_checkout_list(int argc, const char **argv)
{
struct exclude_list el;
char *sparse_filename;
int res;

memset(&el, 0, sizeof(el));

sparse_filename = get_sparse_checkout_filename();
res = add_excludes_from_file_to_list(sparse_filename, "", 0, &el, NULL);
free(sparse_filename);

if (res < 0) {
warning(_("failed to parse sparse-checkout file; it may not exist"));
return 0;
}

write_excludes_to_file(stdout, &el);
clear_exclude_list(&el);

return 0;
}

int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
{
static struct option builtin_sparse_checkout_options[] = {
OPT_END(),
};

if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_sparse_checkout_usage,
builtin_sparse_checkout_options);

git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_options,
builtin_sparse_checkout_usage,
PARSE_OPT_STOP_AT_NON_OPTION);

if (argc > 0) {
if (!strcmp(argv[0], "list"))
return sparse_checkout_list(argc, argv);
}

usage_with_options(builtin_sparse_checkout_usage,
builtin_sparse_checkout_options);
}
1 change: 1 addition & 0 deletions git.c
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ static struct cmd_struct commands[] = {
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show-index", cmd_show_index },
{ "show-ref", cmd_show_ref, RUN_SETUP },
{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
/*
* NEEDSWORK: Until the builtin stash is thoroughly robust and no
Expand Down
50 changes: 50 additions & 0 deletions t/t1091-sparse-checkout-builtin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/sh

test_description='sparse checkout builtin tests'

. ./test-lib.sh

test_expect_success 'setup' '
git init repo &&
(
cd repo &&
echo "initial" >a &&
mkdir folder1 folder2 deep &&
mkdir deep/deeper1 deep/deeper2 &&
mkdir deep/deeper1/deepest &&
cp a folder1 &&
cp a folder2 &&
cp a deep &&
cp a deep/deeper1 &&
cp a deep/deeper2 &&
cp a deep/deeper1/deepest &&
git add . &&
git commit -m "initial commit"
)
'

test_expect_success 'git sparse-checkout list (empty)' '
git -C repo sparse-checkout list >list 2>err &&
test_line_count = 0 list &&
test_i18ngrep "failed to parse sparse-checkout file; it may not exist" err
'

test_expect_success 'git sparse-checkout list (populated)' '
test_when_finished rm -f repo/.git/info/sparse-checkout &&
cat >repo/.git/info/sparse-checkout <<-EOF &&
/folder1/*
/deep/
**/a
!*bin*
EOF
git -C repo sparse-checkout list >list &&
cat >expect <<-EOF &&
/folder1/*
/deep/
**/a
!*bin*
EOF
test_cmp expect list
'

test_done