All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH 6/8] checkout: add new options to support sparse checkout
Date: Sun, 30 Nov 2008 17:54:36 +0700	[thread overview]
Message-ID: <1228042478-1886-7-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1228042478-1886-6-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)
 --sparse: set checkout area according to given spec
 --{include,exclude}-sparse: adjust current sparse checkout area

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-checkout.txt |   55 ++++++++++++++++-
 builtin-checkout.c             |   39 ++++++++++++
 t/t2011-checkout-sparse.sh     |  128 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 219 insertions(+), 3 deletions(-)
 create mode 100755 t/t2011-checkout-sparse.sh

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 0813d9f..b32043f 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, --sparse, --include-sparse
+and --exclude-sparse. 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,35 @@ 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 --sparse,
+	--include-sparse, --exclude-sparse or <paths>.
+
+-S=<sparse patterns>::
+--sparse=<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, --include-sparse,
+	--exclude-sparse or <paths>. Multiple --sparse is not allowed.
+
+-I=<sparse patterns>::
+--include-sparse=<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, --sparse,
+	--exclude-sparse or <paths>. Multiple --include-sparse is not allowed.
+
+-E=<sparse patterns>::
+--exclude-sparse=<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, --sparse,
+	--include-sparse or <paths>. Multiple --exclude-sparse is not allowed.
+
 --conflict=<style>::
 	The same as --merge option above, but changes the way the
 	conflicting hunks are presented, overriding the
@@ -186,7 +221,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
@@ -263,6 +301,17 @@ Patterns have the following format:
  - Sparse patterns do not apply to .gitignore and .gitattributes
    files. They are always checked out.
 
+When you apply new sparse patterns to your working directory using either
+--sparse, --include-sparse or --exclude-sparse, 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. Unmerged entries will always be "checkout" regardless
+the sparse patterns. Modified entries will refuse to become "no-checkout".
+
+You can form your checkout area in one go with --sparse option,
+or do it incrementally with --include-sparse and --exclude-sparse.
+
 EXAMPLES
 --------
 
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 7f3bd7b..be4cd3a 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,
@@ -388,6 +394,7 @@ static int merge_working_tree(struct checkout_opts *opts,
 		topts.dst_index = &the_index;
 
 		topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+		topts.msgs.sparse_not_uptodate_file = "You have local changes to '%s'; cannot update sparse checkout.";
 
 		refresh_cache(REFRESH_QUIET);
 
@@ -411,7 +418,25 @@ 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);
+		free_narrow_spec(topts.narrow_spec);
 		if (ret == -1) {
 			/*
 			 * Unpack couldn't do a trivial merge; either
@@ -598,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('S', "sparse", &opts.new_path, "sparse patterns", "set new sparse checkout"),
+		OPT_STRING('I', "include-sparse", &opts.add_path, "sparse patterns", "widen checkout area"),
+		OPT_STRING('E', "exclude-sparse", &opts.remove_path, "sparse patterns", "narrow checkout area"),
 		OPT_END(),
 	};
 	int has_dash_dash;
@@ -608,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);
@@ -634,6 +664,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
 	}
 
+	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: --full, --sparse, --include-sparse and --exclude-sparse are incompatible");
+
 	if (opts.force && opts.merge)
 		die("git checkout: -f and -m are incompatible");
 
@@ -727,6 +763,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..67aea96
--- /dev/null
+++ b/t/t2011-checkout-sparse.sh
@@ -0,0 +1,128 @@
+#!/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
+	touch .gitignore .gitattributes
+	touch work3/.gitignore work3/.gitattributes
+	git add .gitignore .gitattributes
+	git add work3/.gitignore work3/.gitattributes
+	git add one work1/one
+	git commit -m work1 && WORK1=$(git rev-parse HEAD)
+	echo conflict >> work1/one
+	git add work1/one
+	git commit -m "work1 conflict" && WORK1CONFLICT=$(git rev-parse HEAD)
+	git checkout $WORK1
+	echo one >> one
+	echo work1/1 >> work1/one
+	git add one work1/one
+	git commit -m "work1 modified" && WORK1MODIFIED=$(git rev-parse HEAD)
+	git add two work2/two
+	git commit -m work2 && WORK2=$(git rev-parse HEAD)
+	git add three work3/three
+	git commit -m work3 && WORK3=$(git rev-parse HEAD)
+'
+
+test_expect_success '--full on no-narrow checkout' '
+	git checkout --full
+'
+
+test_expect_success '--full and --sparse incompatible' '
+	test_must_fail git checkout --full --sparse=work1
+'
+
+test_expect_success 'limit worktree to work1 and work2' '
+	git checkout --sparse=work1/:work2/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three
+'
+
+test_expect_success 'exit sparse checkout' '
+	git checkout --full &&
+	test -f work1/one &&
+	test -f work2/two &&
+	test -f work3/three &&
+	test -f one
+'
+
+test_expect_success 'sparse checkout does not touch .git*' '
+	git checkout --sparse=work1/:work2/ &&
+	test -f .gitignore &&
+	test -f .gitattributes &&
+	test -f work3/.gitignore &&
+	test -f work3/.gitattributes
+'
+
+# merged_entry case with old != NULL, same_entry = 1
+# work1/ turns to no-checkout
+# work3/ turns to checkout
+test_expect_success 'update worktree to work2 and work3' '
+	git checkout --sparse=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 --sparse=work1/:work2/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three &&
+	grep -q modified work2/two &&
+
+	! git checkout --sparse=work1/:work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three &&
+	grep -q modified work2/two &&
+	git checkout work2/two
+'
+
+test_expect_success 'widen checkout area' '
+	git checkout --include-sparse=work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	test -f work3/three
+'
+
+test_expect_success 'narrow checkout area' '
+	git checkout --exclude-sparse=work3/ &&
+	test -f work1/one &&
+	test -f work2/two &&
+	! test -f work3/three
+'
+
+test_expect_success 'update outside checkout area' '
+	git checkout --full &&
+	git checkout $WORK1 &&
+	git checkout --sparse work2/ &&
+	! test -f work1/one &&
+	git checkout $WORK1MODIFIED &&
+	! test -f work1/one &&
+	git checkout --full master
+'
+
+test_expect_success 'conflict outside checkout area' '
+	git checkout --sparse="work2/" $WORK1MODIFIED &&
+	test -z "$(git ls-files --sparse work1/one)" &&
+	git merge $WORK1CONFLICT
+	test $? = 1 &&
+	test -n "$(git ls-files --sparse work1/one)" &&
+	git reset --hard &&
+	git checkout master
+'
+
+test_expect_success 'removal outside checkout area' '
+	git rm work1/one &&
+	git commit -m remove &&
+	git checkout --sparse=work2/ HEAD^
+'
+
+test_done
-- 
1.6.0.3.890.g95457

  reply	other threads:[~2008-11-30 10:57 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-11-30 10:54 [PATCH 0/8] Sparse checkout, the last half of the series Nguyễn Thái Ngọc Duy
2008-11-30 10:54 ` [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place Nguyễn Thái Ngọc Duy
2008-11-30 10:54   ` [PATCH 2/8] Introduce "sparse patterns" Nguyễn Thái Ngọc Duy
2008-11-30 10:54     ` [PATCH 3/8] unpack_trees(): keep track of unmerged entries Nguyễn Thái Ngọc Duy
2008-11-30 10:54       ` [PATCH 4/8] unpack_trees(): add support for sparse checkout Nguyễn Thái Ngọc Duy
2008-11-30 10:54         ` [PATCH 5/8] clone: support sparse checkout with --sparse-checkout option Nguyễn Thái Ngọc Duy
2008-11-30 10:54           ` Nguyễn Thái Ngọc Duy [this message]
2008-11-30 10:54             ` [PATCH 7/8] Introduce default sparse patterns (core.defaultsparse) Nguyễn Thái Ngọc Duy
2008-11-30 10:54               ` [PATCH 8/8] wt-status: show sparse checkout info Nguyễn Thái Ngọc Duy
2008-12-01 14:10   ` [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place Johannes Schindelin
2008-12-01 14:11     ` Nguyen Thai Ngoc Duy
2008-12-01 14:11     ` Junio C Hamano
2008-12-01 15:39       ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1228042478-1886-7-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.