git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 13/14] checkout: add new options to support sparse checkout

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Sep 20, 2008, 10:01 UTC
Message-ID
<1221904913-25887-14-git-send-email-pclouds@gmail.com>
In-Reply-To
<1221904913-25887-13-git-send-email-pclouds@gmail.com>

This patch adds main interface to manipulate sparse checkout. New options are added to support entering/updating/leaving sparse checkout:

 --full: return to full checkout (default)
 --reset-path: set checkout area according to given spec
 --add-path/--remove-path: adjust current sparse checkout area
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt |   54 +++++++++++++++++++-
 builtin-checkout.c             |   37 ++++++++++++++
 t/t2011-checkout-sparse.sh     |  108 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 196 insertions(+), 3 deletions(-)
 create mode 100755 t/t2011-checkout-sparse.sh
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index d6f94a6..571e2a4 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -8,8 +8,10 @@ git-checkout - Checkout a branch or paths to the working tree
 SYNOPSIS
 --------
 [verse]
-'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
+'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m]
+	  [<sparse checkout options>] [<branch>]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>]
+	  [<sparse checkout options>] [--] <paths>...
 
 DESCRIPTION
 -----------
@@ -34,6 +36,10 @@ used to specify a specific tree-ish (i.e. commit, tag or tree)
 to update the index for the given paths before updating the
 working tree.
 
+<sparse checkout options> include --full, --reset-path, --add-path
+and --remove-path. The last three require sparse patterns. Please refer
+to "sparse checkout" section for more information about this mode.
+
 The index may contain unmerged entries after a failed merge.  By
 default, if you try to check out such an entry from the index, the
 checkout operation will fail and nothing will be checked out.
@@ -117,6 +123,32 @@ should result in deletion of the path).
 When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
 
+--full::
+	Quit sparse checkout mode. Return to full checkout. This option
+	cannot be used with either --reset-path, --add-path,
+	--remove-path or <paths>.
+
+--reset-path=<sparse patterns>::
+	Re-apply new sparse patterns on current working directory to
+	form new checkout area. All no-checkout bits will be wiped
+	out before applying the patterns. This option cannot be used
+	with --full, --add-path, --remove-path or <paths>. Multiple
+	--reset-path is not allowed.
+
+--add-path=<sparse patterns>::
+	Checkout more areas specified by sparse patterns to current
+	checkout area. Already checked out entries are not affected.
+	This option cannot be used with --full, --reset-path,
+	--remove-path or <paths>. Multiple --add-path is not allowed.
+
+--remove-path=<sparse patterns>::
+	Narrow checkout area by removing files specified by sparse patterns
+	from current checkout area. This operation will fail if there
+	are unmerged or modified files in the removing areas. No-checkout
+	entries are not affected. This option cannot be used with --full,
+	--reset-path, --add-path or <paths>. Multiple --remove-path is not
+	allowed.
+
 --conflict=<style>::
 	The same as --merge option above, but changes the way the
 	conflicting hunks are presented, overriding the
@@ -186,7 +218,10 @@ Because sparse checkout uses a new index format, it will be
 incompatible with git prior to 1.6.0 regarding worktree operations.
 Operations that only need access to the repository itself, such as
 clone, push, or pull/fetch from another (normal) repository... should
-not be affected by sparse checkout.
+not be affected by sparse checkout. In order to make your working
+directory work again with those versions, you can use
+`git checkout --full` to return to normal mode (and compatible index
+format).
 
 In sparse checkout mode, checkout status of every files in your
 working directory will be recorded in index. If a file is marked
@@ -251,6 +286,19 @@ Patterns have the following format:
  - Because colons are used to separate patterns, you cannot put them
    in patterns directly. You must quote them using backslash.
 
+When you apply new sparse patterns to your working directory using either
+--reset-path, --add-path or --remove-path, it will update "checkout" status
+in index accordingly. Moreover, if a file is marked "no-checkout" and
+is present in working directory, it will be removed. If a file is
+turned from "no-checkout" to "checkout", then it will be added again
+to working directory. Modified and unmerged entries can't bear
+"no-checkout" status, if sparse patterns apply to them, "git checkout"
+will refuse to update working directory.
+
+Sparse patterns are not saved by "git checkout" anywhere in the repository.
+You can form your checkout area in one go with --reset-path option,
+or do it incrementally with --add-path and --remove-path.
+
 EXAMPLES
 --------
 
diff --git a/builtin-checkout.c b/builtin-checkout.c
index c7b0aad..d459bc6 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -33,6 +33,12 @@ struct checkout_opts {
 	const char *new_branch;
 	int new_branch_log;
 	enum branch_track track;
+
+	const char *prefix;
+	char *new_path;
+	char *add_path;
+	char *remove_path;
+	int all_path;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -412,6 +418,23 @@ static int merge_working_tree(struct checkout_opts *opts,
 		tree = parse_tree_indirect(new->commit->object.sha1);
 		init_tree_desc(&trees[1], tree->buffer, tree->size);
 
+		if (opts->all_path) {
+			/* leave narrow_spec NULL */
+			topts.new_narrow_path = 1;
+		}
+		else if (opts->new_path) {
+			topts.narrow_spec = parse_narrow_spec(opts->new_path, opts->prefix);
+			topts.new_narrow_path = 1;
+		}
+		else if (opts->add_path) {
+			topts.narrow_spec = parse_narrow_spec(opts->add_path, opts->prefix);
+			topts.add_narrow_path = 1;
+		}
+		else if (opts->remove_path) {
+			topts.narrow_spec = parse_narrow_spec(opts->remove_path, opts->prefix);
+			topts.remove_narrow_path = 1;
+		}
+
 		ret = unpack_trees(2, trees, &topts);
 		if (ret == -1) {
 			/*
@@ -600,6 +623,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
 		OPT_STRING(0, "conflict", &conflict_style, "style",
 			   "conflict style (merge or diff3)"),
+		OPT_BOOLEAN(0, "full", &opts.all_path, "quit sparse checkout"),
+		OPT_STRING(0, "reset-path", &opts.new_path, "prefixes", "reset to new sparse checkout"),
+		OPT_STRING(0, "add-path", &opts.add_path, "prefixes", "widen checkout area"),
+		OPT_STRING(0, "remove-path", &opts.remove_path, "prefixes", "narrow checkout area"),
 		OPT_END(),
 	};
 	int has_dash_dash;
@@ -610,6 +637,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	git_config(git_checkout_config, NULL);
 
 	opts.track = BRANCH_TRACK_UNSPECIFIED;
+	opts.prefix = prefix;
 
 	argc = parse_options(argc, argv, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
@@ -639,6 +667,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	if (!opts.new_branch && (opts.track != git_branch_track))
 		die("git checkout: --track and --no-track require -b");
 
+	if (((opts.all_path ? 1 : 0) +
+	     (opts.new_path ? 1 : 0) +
+	     (opts.add_path ? 1 : 0) +
+	     (opts.remove_path ? 1 : 0)) > 1)
+		die("git checkout: --reset-path, --full, --add-path and --remove-path are incompatible");
+
 	if (opts.force && opts.merge)
 		die("git checkout: -f and -m are incompatible");
 
@@ -732,6 +766,9 @@ no_reference:
 		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
 			die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
 
+		if (opts.all_path || opts.new_path || opts.add_path || opts.remove_path)
+			die("git checkout: updating paths is incompatible with setting sparse checkout");
+
 		return checkout_paths(source_tree, pathspec, &opts);
 	}
 
diff --git a/t/t2011-checkout-sparse.sh b/t/t2011-checkout-sparse.sh
new file mode 100755
index 0000000..b5ccfe4
--- /dev/null
+++ b/t/t2011-checkout-sparse.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+test_description='sparse checkout'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	mkdir work1 work2 work3
+	touch one two three
+	touch work1/one work2/two work3/three
+	git add one work1/one
+	git commit -m work1
+	git add two work2/two
+	git commit -m work2
+	git add three work3/three
+	git commit -m work3
+'
+
+test_expect_success '--full on no-narrow checkout' '
+	git checkout --full
+'
+
+test_expect_success '--full and --reset-path incompatible' '
+	test_must_fail git checkout --full --reset-path=work1
+'
+
+test_expect_success 'limit worktree to work1 and work2' '
+	git checkout --reset-path=work1/:work2/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three
+'
+
+test_expect_success 'update worktree to work2 and work3' '
+	git checkout --reset-path=work2/:work3/ &&
+	! test -f work1/one &&
+	test -f work2/two &&
+	test -f work3/three
+'
+
+test_expect_success 'update narrow prefix with modification' '
+	echo modified >> work2/two &&
+	git checkout --reset-path=work1/:work2/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three &&
+	grep -q modified work2/two
+'
+
+test_expect_success 'update checkout should not lose modification' '
+	! git checkout --reset-path=work1/:work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three &&
+	grep -q modified work2/two
+'
+
+test_expect_success 'widen checkout area' '
+	git checkout --add-path=work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	test -f work3/three
+'
+
+test_expect_success 'narrow checkout area' '
+	git checkout --remove-path=work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three
+'
+
+test_expect_success 'update outside checkout area' '
+	echo one >> work1/one &&
+	git add work1/one &&
+	git commit -m update &&
+	git checkout --reset-path=work2/ &&
+	git checkout HEAD^ &&
+	git checkout master
+'
+
+test_expect_success 'conflict outside checkout area' '
+	git checkout --add-path=work1/one -b conflict HEAD~2 &&
+	echo two >> work1/one &&
+	git add work1/one &&
+	git commit -m conflict-update &&
+	git checkout --reset-path=work2/ master &&
+	test -z "$(git ls-files --sparse work1/one)"
+	git merge conflict
+	test $? = 1 &&
+	test -n "$(git ls-files --sparse work1/one)" &&
+	git reset --hard HEAD
+'
+
+test_expect_success 'removal outside checkout area' '
+	git rm work1/one &&
+	git commit -m remove &&
+	git checkout --reset-path=work2/ HEAD^
+'
+
+test_expect_success 'exit sparse checkout' '
+	git checkout --full &&
+	test -f work1/one &&
+	test -f work2/two &&
+	test -f work3/three &&
+	test one
+'
+
+test_done
-- 
1.6.0.96.g2fad1.dirty
Previous: Nguyễn Thái Ngọc DuyNext: Nguyễn Thái Ngọc Duy
Message 14 of 37 in “Sparse checkout”
  1. 00/14 Sparse checkoutNguyễn Thái Ngọc Duy, Sep 20, 2008
  2. 01/14 Extend index to save more flagsNguyễn Thái Ngọc Duy, Sep 20, 2008
  3. 02/14 Introduce CE_NO_CHECKOUT bitNguyễn Thái Ngọc Duy, Sep 20, 2008
  4. 03/14 ls-files: add options to support sparse checkoutNguyễn Thái Ngọc Duy, Sep 20, 2008
  5. 04/14 update-index: refactor mark_valid() in preparation for new optionsNguyễn Thái Ngọc Duy, Sep 20, 2008
  6. 05/14 update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bitNguyễn Thái Ngọc Duy, Sep 20, 2008
  7. 06/14 ls-files: Add tests for --sparse and friendsNguyễn Thái Ngọc Duy, Sep 20, 2008
  8. 07/14 Prevent diff machinery from examining worktree outside sparse checkoutNguyễn Thái Ngọc Duy, Sep 20, 2008
  9. 08/14 checkout_entry(): CE_NO_CHECKOUT on checked out entries.Nguyễn Thái Ngọc Duy, Sep 20, 2008
  10. 09/14 grep: skip files outside sparse checkout areaNguyễn Thái Ngọc Duy, Sep 20, 2008
  11. 10/14 ls-files: support "sparse patterns", used to form sparse checkout areasNguyễn Thái Ngọc Duy, Sep 20, 2008
  12. 11/14 unpack_trees(): add support for sparse checkoutNguyễn Thái Ngọc Duy, Sep 20, 2008
  13. 12/14 clone: support sparse checkout with --narrow-path optionNguyễn Thái Ngọc Duy, Sep 20, 2008
  14. 13/14 checkout: add new options to support sparse checkoutNguyễn Thái Ngọc Duy, Sep 20, 2008
  15. 14/14 wt-status: Show orphaned entries in "git status" outputNguyễn Thái Ngọc Duy, Sep 20, 2008
  16. Jakub NarebskiSep 20, 2008
  17. Junio C HamanoSep 20, 2008
  18. Junio C HamanoSep 20, 2008
  19. Nguyen Thai Ngoc DuySep 21, 2008
  20. Jakub NarebskiSep 21, 2008
  21. Santi BéjarSep 20, 2008
  22. Nguyen Thai Ngoc DuySep 20, 2008
  23. Jakub NarebskiSep 20, 2008
  24. Nguyen Thai Ngoc DuySep 20, 2008
  25. Jakub NarebskiSep 20, 2008
  26. Uwe Kleine-KönigSep 20, 2008
  27. Nguyen Thai Ngoc DuySep 20, 2008
  28. Junio C HamanoSep 20, 2008
  29. Nguyen Thai Ngoc DuySep 21, 2008
  30. Jakub NarebskiSep 21, 2008
  31. Nguyen Thai Ngoc DuySep 21, 2008
  32. Jakub NarebskiSep 21, 2008
  33. Santi BéjarSep 23, 2008
  34. Nguyen Thai Ngoc DuySep 23, 2008
  35. Nguyen Thai Ngoc DuySep 26, 2008
  36. Junio C HamanoSep 20, 2008
  37. Santi BéjarSep 23, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.