{"thread":{"id":"20673","subject":"[RFC PATCH v4 00/19] Sparse checkout","startedAt":"2009-08-20T13:46:54Z","lastAt":"2009-08-22T11:56:32Z","messageCount":27,"participants":["Nguyễn Thái Ngọc Duy","Jakub Narebski","Matthieu Moy","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":true,"patchVersion":4,"patchTotal":19},"messages":[{"id":"121327","messageId":"1250776033-12395-1-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":null,"subject":"[RFC PATCH v4 00/19] Sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:54Z","receivedAt":"2009-08-20T13:46:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Welcome to the fourth round of sparse checkout this year, dubbed \"The\nmummy^W^W^W^W^Wno-checkout returns\", although the bit now comes under\na new name \"skip-worktree\" [1]. This series has two parts: sparse\nworktree and sparse checkout. Details will be given below.\n\nFor brave users of this series: I have pushed it to my sparse-checkout\nbranch [2] so you can just clone it and test away. Visible changes:\n\n - the spec is now .git/info/sparse-checkout\n - the spec is positive matching, that is it matches what _is_ in\n   worktree, not what is out of worktree like the last series\n - you need to set core.sparsecheckout no additional command argument\n   is needed.\n\nExcept from running tests, I have only used it a little bit, so be\ncareful, it may burn your trees.\n\n\nNguyá»n ThÃ¡i Ngá»c Duy (8):\n  update-index: refactor mark_valid() in preparation for new options\n  Add test-index-version\n  Introduce \"skip-worktree\" bit in index, teach Git to get/set this bit\n  Teach Git to respect skip-worktree bit (reading part)\n  Teach Git to respect skip-worktree bit (writing part)\n  Avoid writing to buffer in add_excludes_from_file_1()\n  Read .gitignore from index if it is skip-worktree\n  unpack-trees(): carry skip-worktree bit over in merged_entry()\n\nThe three main patches in this part are 3, 4 and 5, which define how\n\"skip-worktree\" should work (and I'd love to have feedback whether I\nget it right) and implement it.\n\n.gitignore patches are from last series and do not change much.\n\n\nNguyá»n ThÃ¡i Ngá»c Duy (11):\n  excluded_1(): support exclude files in index\n  dir.c: export excluded_1() and add_excludes_from_file_1()\n  Introduce \"sparse checkout\"\n  unpack-trees(): add CE_WT_REMOVE to remove on worktree alone\n  unpack-trees.c: generalize verify_* functions\n  unpack-trees(): \"enable\" sparse checkout and load\n    $GIT_DIR/info/sparse-checkout\n  unpack_trees(): apply $GIT_DIR/info/sparse-checkout to the final\n    index\n  unpack-trees(): ignore worktree check outside checkout area\n  read-tree: add --no-sparse-checkout to disable sparse checkout\n    support\n  Add tests for sparse checkout\n  sparse checkout: inhibit empty worktree\n\nThe recent assume-unchanged \"breakage\" that lets Git overwrite\nassume-unchanged files worried me. I sat back, checked and wrote tests\nto catch similar situations. As a result, core sparse checkout code\nbecomes more complicated and is splitted into smaller parts for easier\nread.\n\nInteresting patches are 11/19 (OK not that interesting, it's just\nboring text), 14/19-16/19. Patch 18/19 also covers \"foo\" vs \"foo/\"\nmatching bug in tests.\n\n\n[1] There are a few reasons behind this rename:\n - there is nothing about \"checkout\" in the first part, it's about\n   \"sparse worktree\"\n - the double negation issue with \"no-\" part of \"no-checkout\"\n - new name makes cache.h diff align beautifully\n\n[2] http://repo.or.cz/w/git/pclouds.git?a=shortlog;h=refs/heads/sparse-checkout\n\n .gitignore                                        |    1 +\n Documentation/config.txt                          |    4 +\n Documentation/git-ls-files.txt                    |    1 +\n Documentation/git-read-tree.txt                   |   52 ++++++-\n Documentation/git-update-index.txt                |   29 ++++\n Documentation/technical/api-directory-listing.txt |    3 +\n Makefile                                          |    1 +\n builtin-clean.c                                   |    4 +-\n builtin-commit.c                                  |    5 +\n builtin-grep.c                                    |    2 +-\n builtin-ls-files.c                                |   11 +-\n builtin-read-tree.c                               |    4 +-\n builtin-update-index.c                            |   78 ++++++----\n cache.h                                           |    8 +-\n config.c                                          |    5 +\n diff-lib.c                                        |    5 +-\n diff.c                                            |    2 +-\n dir.c                                             |  100 ++++++++----\n dir.h                                             |    4 +\n environment.c                                     |    1 +\n read-cache.c                                      |    8 +-\n t/t1009-read-tree-sparse-checkout.sh              |  150 ++++++++++++++++++\n t/t2104-update-index-skip-worktree.sh             |   57 +++++++\n t/t3001-ls-files-others-exclude.sh                |   22 +++\n t/t7011-skip-worktree-reading.sh                  |  163 +++++++++++++++++++\n t/t7012-skip-worktree-writing.sh                  |  146 +++++++++++++++++\n t/t7300-clean.sh                                  |   19 +++\n test-index-version.c                              |   14 ++\n unpack-trees.c                                    |  175 +++++++++++++++++++--\n unpack-trees.h                                    |    6 +\n 30 files changed, 983 insertions(+), 97 deletions(-)\n create mode 100755 t/t1009-read-tree-sparse-checkout.sh\n create mode 100755 t/t2104-update-index-skip-worktree.sh\n create mode 100755 t/t7011-skip-worktree-reading.sh\n create mode 100755 t/t7012-skip-worktree-writing.sh\n create mode 100644 test-index-version.c\n"},{"id":"121328","messageId":"1250776033-12395-2-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-1-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 01/19] update-index: refactor mark_valid() in preparation for new options","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:55Z","receivedAt":"2009-08-20T13:46:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin-update-index.c |   24 ++++++++++--------------\n 1 files changed, 10 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex 92beaaf..f1b6c8e 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -24,8 +24,8 @@ static int info_only;\n static int force_remove;\n static int verbose;\n static int mark_valid_only;\n-#define MARK_VALID 1\n-#define UNMARK_VALID 2\n+#define MARK_FLAG 1\n+#define UNMARK_FLAG 2\n \n static void report(const char *fmt, ...)\n {\n@@ -40,19 +40,15 @@ static void report(const char *fmt, ...)\n \tva_end(vp);\n }\n \n-static int mark_valid(const char *path)\n+static int mark_ce_flags(const char *path, int flag, int mark)\n {\n \tint namelen = strlen(path);\n \tint pos = cache_name_pos(path, namelen);\n \tif (0 <= pos) {\n-\t\tswitch (mark_valid_only) {\n-\t\tcase MARK_VALID:\n-\t\t\tactive_cache[pos]->ce_flags |= CE_VALID;\n-\t\t\tbreak;\n-\t\tcase UNMARK_VALID:\n-\t\t\tactive_cache[pos]->ce_flags &= ~CE_VALID;\n-\t\t\tbreak;\n-\t\t}\n+\t\tif (mark)\n+\t\t\tactive_cache[pos]->ce_flags |= flag;\n+\t\telse\n+\t\t\tactive_cache[pos]->ce_flags &= ~flag;\n \t\tcache_tree_invalidate_path(active_cache_tree, path);\n \t\tactive_cache_changed = 1;\n \t\treturn 0;\n@@ -276,7 +272,7 @@ static void update_one(const char *path, const char *prefix, int prefix_length)\n \t\tgoto free_return;\n \t}\n \tif (mark_valid_only) {\n-\t\tif (mark_valid(p))\n+\t\tif (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))\n \t\t\tdie(\"Unable to mark file %s\", path);\n \t\tgoto free_return;\n \t}\n@@ -647,11 +643,11 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = MARK_VALID;\n+\t\t\t\tmark_valid_only = MARK_FLAG;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--no-assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = UNMARK_VALID;\n+\t\t\t\tmark_valid_only = UNMARK_FLAG;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--info-only\")) {\n-- \n1.6.3.GIT\n"},{"id":"121329","messageId":"1250776033-12395-3-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-2-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 02/19] Add test-index-version","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:56Z","receivedAt":"2009-08-20T13:46:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Commit 06aaaa0bf70fe37d198893f4e25fa73b6516f8a9 may step index format\nversion up and down, depends on whether extended flags present in the\nindex. This adds a test to check for index format version.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore           |    1 +\n Makefile             |    1 +\n test-index-version.c |   14 ++++++++++++++\n 3 files changed, 16 insertions(+), 0 deletions(-)\n create mode 100644 test-index-version.c\n\ndiff --git a/.gitignore b/.gitignore\nindex c446290..01753f8 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -157,6 +157,7 @@ test-date\n test-delta\n test-dump-cache-tree\n test-genrandom\n+test-index-version\n test-match-trees\n test-parse-options\n test-path-utils\ndiff --git a/Makefile b/Makefile\nindex 4190a5d..f49435a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1586,6 +1586,7 @@ TEST_PROGRAMS += test-parse-options$X\n TEST_PROGRAMS += test-path-utils$X\n TEST_PROGRAMS += test-sha1$X\n TEST_PROGRAMS += test-sigchain$X\n+TEST_PROGRAMS += test-index-version$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/test-index-version.c b/test-index-version.c\nnew file mode 100644\nindex 0000000..bfaad9e\n--- /dev/null\n+++ b/test-index-version.c\n@@ -0,0 +1,14 @@\n+#include \"cache.h\"\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct cache_header hdr;\n+\tint version;\n+\n+\tmemset(&hdr,0,sizeof(hdr));\n+\tif (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))\n+\t\treturn 0;\n+\tversion = ntohl(hdr.hdr_version);\n+\tprintf(\"%d\\n\", version);\n+\treturn 0;\n+}\n-- \n1.6.3.GIT\n"},{"id":"121330","messageId":"1250776033-12395-4-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-3-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 03/19] Introduce \"skip-worktree\" bit in index, teach Git to get/set this bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:57Z","receivedAt":"2009-08-20T13:46:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Detail about this bit is in Documentation/git-update-index.txt.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt        |    1 +\n Documentation/git-update-index.txt    |   29 +++++++++++++++++\n builtin-ls-files.c                    |    5 ++-\n builtin-update-index.c                |   16 +++++++++-\n cache.h                               |    4 ++-\n t/t2104-update-index-skip-worktree.sh |   57 +++++++++++++++++++++++++++++++++\n 6 files changed, 109 insertions(+), 3 deletions(-)\n create mode 100755 t/t2104-update-index-skip-worktree.sh\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 021066e..6f9d880 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -107,6 +107,7 @@ OPTIONS\n \tIdentify the file status with the following tags (followed by\n \ta space) at the start of each line:\n \tH::\tcached\n+\tS::\tskip-worktree\n \tM::\tunmerged\n \tR::\tremoved/deleted\n \tC::\tmodified/changed\ndiff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt\nindex 25e0bbe..a10f355 100644\n--- a/Documentation/git-update-index.txt\n+++ b/Documentation/git-update-index.txt\n@@ -15,6 +15,7 @@ SYNOPSIS\n \t     [--cacheinfo <mode> <object> <file>]\\*\n \t     [--chmod=(+|-)x]\n \t     [--assume-unchanged | --no-assume-unchanged]\n+\t     [--skip-worktree | --no-skip-worktree]\n \t     [--ignore-submodules]\n \t     [--really-refresh] [--unresolve] [--again | -g]\n \t     [--info-only] [--index-info]\n@@ -99,6 +100,13 @@ in the index e.g. when merging in a commit;\n thus, in case the assumed-untracked file is changed upstream,\n you will need to handle the situation manually.\n \n+--skip-worktree::\n+--no-skip-worktree::\n+\tWhen one of these flags is specified, the object name recorded\n+\tfor the paths are not updated. Instead, these options\n+\tset and unset the \"skip-worktree\" bit for the paths. See\n+\tsection \"Skip-worktree bit\" below for more information.\n+\n -g::\n --again::\n \tRuns 'git-update-index' itself on the paths whose index\n@@ -304,6 +312,27 @@ M foo.c\n <9> now it checks with lstat(2) and finds it has been changed.\n \n \n+Skip-worktree bit\n+-----------------\n+\n+Skip-worktree bit can be defined in one (long) sentence: When reading\n+an entry, if it is marked as skip-worktree, then Git pretends its\n+working directory version is up to date and read the index version\n+instead.\n+\n+To elaborate, \"reading\" means checking for file existence, reading\n+file attributes or file content. The working directory version may be\n+present or absent. If present, its content may match against the index\n+version or not. Writing is not affected by this bit, content safety\n+is still first priority. Note that Git _can_ update working directory\n+file, that is marked skip-worktree, if it is safe to do so (i.e.\n+working directory version matches index version)\n+\n+Although this bit looks similar to assume-unchanged bit, its goal is\n+different from assume-unchanged bit's. Skip-worktree also takes\n+precedence over assume-unchanged bit when both are set.\n+\n+\n Configuration\n -------------\n \ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex f473220..c1afbad 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -37,6 +37,7 @@ static const char *tag_removed = \"\";\n static const char *tag_other = \"\";\n static const char *tag_killed = \"\";\n static const char *tag_modified = \"\";\n+static const char *tag_skip_worktree = \"\";\n \n static void show_dir_entry(const char *tag, struct dir_entry *ent)\n {\n@@ -178,7 +179,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tif (ce->ce_flags & CE_UPDATE)\n \t\t\t\tcontinue;\n-\t\t\tshow_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);\n+\t\t\tshow_ce_entry(ce_stage(ce) ? tag_unmerged :\n+\t\t\t\t(ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);\n \t\t}\n \t}\n \tif (show_deleted | show_modified) {\n@@ -490,6 +492,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\ttag_modified = \"C \";\n \t\ttag_other = \"? \";\n \t\ttag_killed = \"K \";\n+\t\ttag_skip_worktree = \"S \";\n \t}\n \tif (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)\n \t\trequire_work_tree = 1;\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex f1b6c8e..5e97d09 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -24,6 +24,7 @@ static int info_only;\n static int force_remove;\n static int verbose;\n static int mark_valid_only;\n+static int mark_skip_worktree_only;\n #define MARK_FLAG 1\n #define UNMARK_FLAG 2\n \n@@ -276,6 +277,11 @@ static void update_one(const char *path, const char *prefix, int prefix_length)\n \t\t\tdie(\"Unable to mark file %s\", path);\n \t\tgoto free_return;\n \t}\n+\tif (mark_skip_worktree_only) {\n+\t\tif (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))\n+\t\t\tdie(\"Unable to mark file %s\", path);\n+\t\tgoto free_return;\n+\t}\n \n \tif (force_remove) {\n \t\tif (remove_file_from_cache(p))\n@@ -384,7 +390,7 @@ static void read_index_info(int line_termination)\n }\n \n static const char update_index_usage[] =\n-\"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...\";\n+\"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...\";\n \n static unsigned char head_sha1[20];\n static unsigned char merge_head_sha1[20];\n@@ -650,6 +656,14 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\t\t\tmark_valid_only = UNMARK_FLAG;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(path, \"--no-skip-worktree\")) {\n+\t\t\t\tmark_skip_worktree_only = UNMARK_FLAG;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(path, \"--skip-worktree\")) {\n+\t\t\t\tmark_skip_worktree_only = MARK_FLAG;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(path, \"--info-only\")) {\n \t\t\t\tinfo_only = 1;\n \t\t\t\tcontinue;\ndiff --git a/cache.h b/cache.h\nindex f793789..3b0e5fc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -181,10 +181,11 @@ struct cache_entry {\n  * Extended on-disk flags\n  */\n #define CE_INTENT_TO_ADD 0x20000000\n+#define CE_SKIP_WORKTREE 0x40000000\n /* CE_EXTENDED2 is for future extension */\n #define CE_EXTENDED2 0x80000000\n \n-#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)\n+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)\n \n /*\n  * Safeguard to avoid saving wrong flags:\n@@ -233,6 +234,7 @@ static inline size_t ce_namelen(const struct cache_entry *ce)\n \t\t\t    ondisk_cache_entry_size(ce_namelen(ce)))\n #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)\n #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)\n+#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)\n #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)\n \n #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)\ndiff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh\nnew file mode 100755\nindex 0000000..1d0879b\n--- /dev/null\n+++ b/t/t2104-update-index-skip-worktree.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy\n+#\n+\n+test_description='skip-worktree bit test'\n+\n+. ./test-lib.sh\n+\n+cat >expect.full <<EOF\n+H 1\n+H 2\n+H sub/1\n+H sub/2\n+EOF\n+\n+cat >expect.skip <<EOF\n+S 1\n+H 2\n+S sub/1\n+H sub/2\n+EOF\n+\n+test_expect_success 'setup' '\n+\tmkdir sub &&\n+\ttouch ./1 ./2 sub/1 sub/2 &&\n+\tgit add 1 2 sub/1 sub/2 &&\n+\tgit ls-files -t | test_cmp expect.full -\n+'\n+\n+test_expect_success 'index is at version 2' '\n+\ttest \"$(test-index-version < .git/index)\" = 2\n+'\n+\n+test_expect_success 'update-index --skip-worktree' '\n+\tgit update-index --skip-worktree 1 sub/1 &&\n+\tgit ls-files -t | test_cmp expect.skip -\n+'\n+\n+test_expect_success 'index is at version 3 after having some skip-worktree entries' '\n+\ttest \"$(test-index-version < .git/index)\" = 3\n+'\n+\n+test_expect_success 'ls-files -t' '\n+\tgit ls-files -t | test_cmp expect.skip -\n+'\n+\n+test_expect_success 'update-index --no-skip-worktree' '\n+\tgit update-index --no-skip-worktree 1 sub/1 &&\n+\tgit ls-files -t | test_cmp expect.full -\n+'\n+\n+test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '\n+\ttest \"$(test-index-version < .git/index)\" = 2\n+'\n+\n+test_done\n-- \n1.6.3.GIT\n"},{"id":"121332","messageId":"1250776033-12395-5-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-4-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 04/19] Teach Git to respect skip-worktree bit (reading part)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:58Z","receivedAt":"2009-08-20T13:46:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"grep: turn on --cached for files that is marked skip-worktree\nls-files: do not check for deleted file that is marked skip-worktree\nupdate-index: ignore update request if it's skip-worktree, while still allows removing\ndiff*: skip worktree version\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin-commit.c                 |    5 +\n builtin-grep.c                   |    2 +-\n builtin-ls-files.c               |    2 +\n builtin-update-index.c           |   38 +++++----\n diff-lib.c                       |    5 +-\n diff.c                           |    2 +-\n read-cache.c                     |    8 +--\n t/t7011-skip-worktree-reading.sh |  163 ++++++++++++++++++++++++++++++++++++++\n 8 files changed, 199 insertions(+), 26 deletions(-)\n create mode 100755 t/t7011-skip-worktree-reading.sh\n\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 200ffda..cb64cde 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -181,6 +181,11 @@ static void add_remove_files(struct string_list *list)\n \tfor (i = 0; i < list->nr; i++) {\n \t\tstruct stat st;\n \t\tstruct string_list_item *p = &(list->items[i]);\n+\t\tint pos = index_name_pos(&the_index, p->string, strlen(p->string));\n+\t\tstruct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];\n+\n+\t\tif (ce && ce_skip_worktree(ce))\n+\t\t\tcontinue;\n \n \t\tif (!lstat(p->string, &st)) {\n \t\t\tif (add_to_cache(p->string, &st, 0))\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex ad0e0a5..813fe97 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -517,7 +517,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,\n \t\t * are identical, even if worktree file has been modified, so use\n \t\t * cache version instead\n \t\t */\n-\t\tif (cached || (ce->ce_flags & CE_VALID)) {\n+\t\tif (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {\n \t\t\tif (ce_stage(ce))\n \t\t\t\tcontinue;\n \t\t\thit |= grep_sha1(opt, ce->sha1, ce->name, 0);\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex c1afbad..ad7e447 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -194,6 +194,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tif (ce->ce_flags & CE_UPDATE)\n \t\t\t\tcontinue;\n+\t\t\tif (ce_skip_worktree(ce))\n+\t\t\t\tcontinue;\n \t\t\terr = lstat(ce->name, &st);\n \t\t\tif (show_deleted && err)\n \t\t\t\tshow_ce_entry(tag_removed, ce);\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex 5e97d09..97b9ea6 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -172,29 +172,29 @@ static int process_directory(const char *path, int len, struct stat *st)\n \treturn error(\"%s: is a directory - add files inside instead\", path);\n }\n \n-/*\n- * Process a regular file\n- */\n-static int process_file(const char *path, int len, struct stat *st)\n-{\n-\tint pos = cache_name_pos(path, len);\n-\tstruct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];\n-\n-\tif (ce && S_ISGITLINK(ce->ce_mode))\n-\t\treturn error(\"%s is already a gitlink, not replacing\", path);\n-\n-\treturn add_one_path(ce, path, len, st);\n-}\n-\n static int process_path(const char *path)\n {\n-\tint len;\n+\tint pos, len;\n \tstruct stat st;\n+\tstruct cache_entry *ce;\n \n \tlen = strlen(path);\n \tif (has_symlink_leading_path(path, len))\n \t\treturn error(\"'%s' is beyond a symbolic link\", path);\n \n+\tpos = cache_name_pos(path, len);\n+\tce = pos < 0 ? NULL : active_cache[pos];\n+\tif (ce && ce_skip_worktree(ce)) {\n+\t\t/*\n+\t\t * working directory version is assumed \"good\"\n+\t\t * so updating it does not make sense.\n+\t\t * On the other hand, removing it from index should work\n+\t\t */\n+\t\tif (allow_remove && remove_file_from_cache(path))\n+\t\t\treturn error(\"%s: cannot remove from the index\", path);\n+\t\treturn 0;\n+\t}\n+\n \t/*\n \t * First things first: get the stat information, to decide\n \t * what to do about the pathname!\n@@ -205,7 +205,13 @@ static int process_path(const char *path)\n \tif (S_ISDIR(st.st_mode))\n \t\treturn process_directory(path, len, &st);\n \n-\treturn process_file(path, len, &st);\n+\t/*\n+\t * Process a regular file\n+\t */\n+\tif (ce && S_ISGITLINK(ce->ce_mode))\n+\t\treturn error(\"%s is already a gitlink, not replacing\", path);\n+\n+\treturn add_one_path(ce, path, len, &st);\n }\n \n static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\ndiff --git a/diff-lib.c b/diff-lib.c\nindex e7e8e88..fc98678 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -159,7 +159,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (ce_uptodate(ce))\n+\t\tif (ce_uptodate(ce) || ce_skip_worktree(ce))\n \t\t\tcontinue;\n \n \t\tchanged = check_removed(ce, &st);\n@@ -321,6 +321,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,\n \tstruct rev_info *revs = o->unpack_data;\n \tint match_missing, cached;\n \n+\t/* if the entry is not checked out, don't examine work tree */\n+\tcached = o->index_only || (idx && ce_skip_worktree(idx));\n \t/*\n \t * Backward compatibility wart - \"diff-index -m\" does\n \t * not mean \"do not ignore merges\", but \"match_missing\".\n@@ -328,7 +330,6 @@ static void do_oneway_diff(struct unpack_trees_options *o,\n \t * But with the revision flag parsing, that's found in\n \t * \"!revs->ignore_merges\".\n \t */\n-\tcached = o->index_only;\n \tmatch_missing = !revs->ignore_merges;\n \n \tif (cached && idx && ce_stage(idx)) {\ndiff --git a/diff.c b/diff.c\nindex 91d6ea2..215e6b3 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1805,7 +1805,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int\n \t * If ce is marked as \"assume unchanged\", there is no\n \t * guarantee that work tree matches what we are looking for.\n \t */\n-\tif (ce->ce_flags & CE_VALID)\n+\tif ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))\n \t\treturn 0;\n \n \t/*\ndiff --git a/read-cache.c b/read-cache.c\nindex 4e3e272..5ee7d9d 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -265,7 +265,7 @@ int ie_match_stat(const struct index_state *istate,\n \t * If it's marked as always valid in the index, it's\n \t * valid whatever the checked-out copy says.\n \t */\n-\tif (!ignore_valid && (ce->ce_flags & CE_VALID))\n+\tif (!ignore_valid && ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)))\n \t\treturn 0;\n \n \t/*\n@@ -1004,11 +1004,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \tif (ce_uptodate(ce))\n \t\treturn ce;\n \n-\t/*\n-\t * CE_VALID means the user promised us that the change to\n-\t * the work tree does not matter and told us not to worry.\n-\t */\n-\tif (!ignore_valid && (ce->ce_flags & CE_VALID)) {\n+\tif (!ignore_valid && ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))) {\n \t\tce_mark_uptodate(ce);\n \t\treturn ce;\n \t}\ndiff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh\nnew file mode 100755\nindex 0000000..e996928\n--- /dev/null\n+++ b/t/t7011-skip-worktree-reading.sh\n@@ -0,0 +1,163 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy\n+#\n+\n+test_description='skip-worktree bit test'\n+\n+. ./test-lib.sh\n+\n+cat >expect.full <<EOF\n+H 1\n+H 2\n+H init.t\n+H sub/1\n+H sub/2\n+EOF\n+\n+cat >expect.skip <<EOF\n+S 1\n+H 2\n+H init.t\n+S sub/1\n+H sub/2\n+EOF\n+\n+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391\n+ZERO_SHA0=0000000000000000000000000000000000000000\n+setup_absent() {\n+\ttest -f 1 && rm 1\n+\tgit update-index --remove 1 &&\n+\tgit update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&\n+\tgit update-index --skip-worktree 1\n+}\n+\n+test_absent() {\n+\techo \"100644 $NULL_SHA1 0\t1\" > expected &&\n+\tgit ls-files --stage 1 > result &&\n+\ttest_cmp expected result &&\n+\ttest ! -f 1\n+}\n+\n+setup_dirty() {\n+\tgit update-index --force-remove 1 &&\n+\techo dirty > 1 &&\n+\tgit update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&\n+\tgit update-index --skip-worktree 1\n+}\n+\n+test_dirty() {\n+\techo \"100644 $NULL_SHA1 0\t1\" > expected &&\n+\tgit ls-files --stage 1 > result &&\n+\ttest_cmp expected result &&\n+\techo dirty > expected\n+\ttest_cmp expected 1\n+}\n+\n+test_expect_success 'setup' '\n+\ttest_commit init &&\n+\tmkdir sub &&\n+\ttouch ./1 ./2 sub/1 sub/2 &&\n+\tgit add 1 2 sub/1 sub/2 &&\n+\tgit update-index --skip-worktree 1 sub/1 &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expect.skip result\n+'\n+\n+test_expect_success 'update-index' '\n+\tsetup_absent &&\n+\tgit update-index 1 &&\n+\ttest_absent\n+'\n+\n+test_expect_success 'update-index' '\n+\tsetup_dirty &&\n+\tgit update-index 1 &&\n+\ttest_dirty\n+'\n+\n+test_expect_success 'update-index --remove' '\n+\tsetup_absent &&\n+\tgit update-index --remove 1 &&\n+\ttest -z \"$(git ls-files 1)\" &&\n+\ttest ! -f 1\n+'\n+\n+test_expect_success 'update-index --remove' '\n+\tsetup_dirty &&\n+\tgit update-index --remove 1 &&\n+\ttest -z \"$(git ls-files 1)\" &&\n+\techo dirty > expected &&\n+\ttest_cmp expected 1\n+'\n+\n+test_expect_success 'ls-files --delete' '\n+\tsetup_absent &&\n+\ttest -z \"$(git ls-files -d)\"\n+'\n+\n+test_expect_success 'ls-files --delete' '\n+\tsetup_dirty &&\n+\ttest -z \"$(git ls-files -d)\"\n+'\n+\n+test_expect_success 'ls-files --modified' '\n+\tsetup_absent &&\n+\ttest -z \"$(git ls-files -m)\"\n+'\n+\n+test_expect_success 'ls-files --modified' '\n+\tsetup_dirty &&\n+\ttest -z \"$(git ls-files -m)\"\n+'\n+\n+test_expect_success 'grep with skip-worktree file' '\n+\tgit update-index --no-skip-worktree 1 &&\n+\techo test > 1 &&\n+\tgit update-index 1 &&\n+\tgit update-index --skip-worktree 1 &&\n+\trm 1 &&\n+\ttest \"$(git grep --no-ext-grep test)\" = \"1:test\"\n+'\n+\n+echo \":000000 100644 $ZERO_SHA0 $NULL_SHA1 A\t1\" > expected\n+test_expect_success 'diff-index does not examine skip-worktree absent entries' '\n+\tsetup_absent &&\n+\tgit diff-index HEAD -- 1 > result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'diff-index does not examine skip-worktree dirty entries' '\n+\tsetup_dirty &&\n+\tgit diff-index HEAD -- 1 > result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'diff-files does not examine skip-worktree absent entries' '\n+\tsetup_absent &&\n+\ttest -z \"$(git diff-files -- one)\"\n+'\n+\n+test_expect_success 'diff-files does not examine skip-worktree dirty entries' '\n+\tsetup_dirty &&\n+\ttest -z \"$(git diff-files -- one)\"\n+'\n+\n+test_expect_success 'git-rm succeeds on skip-worktree absent entries' '\n+\tsetup_absent &&\n+\tgit rm 1\n+'\n+\n+test_expect_failure 'commit on skip-worktree absent entries' '\n+\tgit reset &&\n+\tsetup_absent &&\n+\ttest_must_fail git commit -m null 1\n+'\n+\n+test_expect_failure 'commit on skip-worktree dirty entries' '\n+\tgit reset &&\n+\tsetup_dirty &&\n+\ttest_must_fail git commit -m null 1\n+'\n+\n+test_done\n-- \n1.6.3.GIT\n"},{"id":"121333","messageId":"1250776033-12395-6-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-5-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 05/19] Teach Git to respect skip-worktree bit (writing part)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:46:59Z","receivedAt":"2009-08-20T13:46:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This part is mainly to remove CE_VALID shortcuts (and as a\nconsequence, ce_uptodate() shortcuts as it may be turned on by\nCE_VALID) in writing code path if skip-worktree is used. Various tests\nare added to avoid future breakages.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t7012-skip-worktree-writing.sh |  146 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.c                   |    4 +-\n 2 files changed, 148 insertions(+), 2 deletions(-)\n create mode 100755 t/t7012-skip-worktree-writing.sh\n\ndiff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh\nnew file mode 100755\nindex 0000000..8d8b1c0\n--- /dev/null\n+++ b/t/t7012-skip-worktree-writing.sh\n@@ -0,0 +1,146 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy\n+#\n+\n+test_description='test worktree writing operations when skip-worktree is used'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit init &&\n+\techo modified >> init.t &&\n+\ttouch added &&\n+\tgit add init.t added &&\n+\tgit commit -m \"modified and added\" &&\n+\tgit tag top\n+'\n+\n+test_expect_success 'read-tree updates worktree, absent case' '\n+\tgit checkout -f top &&\n+\tgit update-index --skip-worktree init.t &&\n+\trm init.t &&\n+\tgit read-tree -m -u HEAD^ &&\n+\techo init > expected &&\n+\ttest_cmp expected init.t\n+'\n+\n+test_expect_success 'read-tree updates worktree, dirty case' '\n+\tgit checkout -f top &&\n+\tgit update-index --skip-worktree init.t &&\n+\techo dirty >> init.t &&\n+\ttest_must_fail git read-tree -m -u HEAD^ &&\n+\tgrep -q dirty init.t &&\n+\ttest \"$(git ls-files -t init.t)\" = \"S init.t\" &&\n+\tgit update-index --no-skip-worktree init.t\n+'\n+\n+test_expect_success 'read-tree removes worktree, absent case' '\n+\tgit checkout -f top &&\n+\tgit update-index --skip-worktree added &&\n+\trm added &&\n+\tgit read-tree -m -u HEAD^ &&\n+\ttest ! -f added\n+'\n+\n+test_expect_success 'read-tree removes worktree, dirty case' '\n+\tgit checkout -f top &&\n+\tgit update-index --skip-worktree added &&\n+\techo dirty >> added &&\n+\ttest_must_fail git read-tree -m -u HEAD^ &&\n+\tgrep -q dirty added &&\n+\ttest \"$(git ls-files -t added)\" = \"S added\" &&\n+\tgit update-index --no-skip-worktree added\n+'\n+\n+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391\n+ZERO_SHA0=0000000000000000000000000000000000000000\n+setup_absent() {\n+\ttest -f 1 && rm 1\n+\tgit update-index --remove 1 &&\n+\tgit update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&\n+\tgit update-index --skip-worktree 1\n+}\n+\n+test_absent() {\n+\techo \"100644 $NULL_SHA1 0\t1\" > expected &&\n+\tgit ls-files --stage 1 > result &&\n+\ttest_cmp expected result &&\n+\ttest ! -f 1\n+}\n+\n+setup_dirty() {\n+\tgit update-index --force-remove 1 &&\n+\techo dirty > 1 &&\n+\tgit update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&\n+\tgit update-index --skip-worktree 1\n+}\n+\n+test_dirty() {\n+\techo \"100644 $NULL_SHA1 0\t1\" > expected &&\n+\tgit ls-files --stage 1 > result &&\n+\ttest_cmp expected result &&\n+\techo dirty > expected\n+\ttest_cmp expected 1\n+}\n+\n+cat >expected <<EOF\n+S 1\n+H 2\n+H init.t\n+S sub/1\n+H sub/2\n+EOF\n+\n+test_expect_success 'index setup' '\n+\tgit checkout -f init &&\n+\tmkdir sub &&\n+\ttouch ./1 ./2 sub/1 sub/2 &&\n+\tgit add 1 2 sub/1 sub/2 &&\n+\tgit update-index --skip-worktree 1 sub/1 &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'git-add ignores worktree content' '\n+\tsetup_absent &&\n+\tgit add 1 &&\n+\ttest_absent\n+'\n+\n+test_expect_success 'git-add ignores worktree content' '\n+\tsetup_dirty &&\n+\tgit add 1 &&\n+\ttest_dirty\n+'\n+\n+test_expect_success 'git-rm fails if worktree is dirty' '\n+\tsetup_dirty &&\n+\ttest_must_fail git rm 1 &&\n+\ttest_dirty\n+'\n+\n+cat >expected <<EOF\n+Would remove expected\n+Would remove result\n+EOF\n+test_expect_success 'git-clean, absent case' '\n+\tsetup_absent &&\n+\tgit clean -n > result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'git-clean, dirty case' '\n+\tsetup_dirty &&\n+\tgit clean -n > result &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_failure 'git-apply adds file' false\n+test_expect_failure 'git-apply updates file' false\n+test_expect_failure 'git-apply removes file' false\n+test_expect_failure 'git-mv to skip-worktree' false\n+test_expect_failure 'git-mv from skip-worktree' false\n+test_expect_failure 'git-checkout' false\n+\n+test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 720f7a1..3eda263 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -450,7 +450,7 @@ static int verify_uptodate(struct cache_entry *ce,\n {\n \tstruct stat st;\n \n-\tif (o->index_only || o->reset || ce_uptodate(ce))\n+\tif (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))\n \t\treturn 0;\n \n \tif (!lstat(ce->name, &st)) {\n@@ -1004,7 +1004,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \n \tif (old && same(old, a)) {\n \t\tint update = 0;\n-\t\tif (o->reset && !ce_uptodate(old)) {\n+\t\tif (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {\n \t\t\tstruct stat st;\n \t\t\tif (lstat(old->name, &st) ||\n \t\t\t    ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))\n-- \n1.6.3.GIT\n"},{"id":"121334","messageId":"1250776033-12395-7-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-6-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 06/19] Avoid writing to buffer in add_excludes_from_file_1()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:00Z","receivedAt":"2009-08-20T13:47:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In the next patch, the buffer that is being used within\nadd_excludes_from_file_1() comes from another function and does not\nhave extra space to put \\n at the end.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c |    5 ++---\n 1 files changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex d0999ba..e9254d6 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -229,10 +229,9 @@ static int add_excludes_from_file_1(const char *fname,\n \n \tif (buf_p)\n \t\t*buf_p = buf;\n-\tbuf[size++] = '\\n';\n \tentry = buf;\n-\tfor (i = 0; i < size; i++) {\n-\t\tif (buf[i] == '\\n') {\n+\tfor (i = 0; i <= size; i++) {\n+\t\tif (i == size || buf[i] == '\\n') {\n \t\t\tif (entry != buf + i && entry[0] != '#') {\n \t\t\t\tbuf[i - (i && buf[i-1] == '\\r')] = 0;\n \t\t\t\tadd_exclude(entry, base, baselen, which);\n-- \n1.6.3.GIT\n"},{"id":"121335","messageId":"1250776033-12395-8-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-7-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 07/19] Read .gitignore from index if it is skip-worktree","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:01Z","receivedAt":"2009-08-20T13:47:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This adds index as a prerequisite for directory listing (with\nexclude).  At the moment directory listing is used by \"git clean\",\n\"git add\", \"git ls-files\" and \"git status\"/\"git commit\" and\nunpack_trees()-related commands.  These commands have been\nchecked/modified to populate index before doing directory listing.\n\nadd_excludes_from_file() does not enable this feature, because it\nis used to read .git/info/exclude and some explicit files specified\nby \"git ls-files\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/technical/api-directory-listing.txt |    3 +\n builtin-clean.c                                   |    4 +-\n builtin-ls-files.c                                |    4 +-\n dir.c                                             |   65 ++++++++++++++------\n t/t3001-ls-files-others-exclude.sh                |   22 +++++++\n t/t7300-clean.sh                                  |   19 ++++++\n 6 files changed, 95 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt\nindex 5bbd18f..add6f43 100644\n--- a/Documentation/technical/api-directory-listing.txt\n+++ b/Documentation/technical/api-directory-listing.txt\n@@ -58,6 +58,9 @@ The result of the enumeration is left in these fields::\n Calling sequence\n ----------------\n \n+Note: index may be looked at for .gitignore files that are CE_SKIP_WORKTREE\n+marked. If you to exclude files, make sure you have loaded index first.\n+\n * Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,\n   sizeof(dir))`.\n \ndiff --git a/builtin-clean.c b/builtin-clean.c\nindex 05c763c..96ada75 100644\n--- a/builtin-clean.c\n+++ b/builtin-clean.c\n@@ -75,11 +75,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)\n \n \tdir.flags |= DIR_SHOW_OTHER_DIRECTORIES;\n \n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n \tif (!ignored)\n \t\tsetup_standard_excludes(&dir);\n \n \tpathspec = get_pathspec(prefix, argv);\n-\tread_cache();\n \n \tfill_directory(&dir, pathspec);\n \ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex ad7e447..2e47242 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -485,6 +485,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\tprefix_offset = strlen(prefix);\n \tgit_config(git_default_config, NULL);\n \n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n \targc = parse_options(argc, argv, prefix, builtin_ls_files_options,\n \t\t\tls_files_usage, 0);\n \tif (show_tag || show_valid_bit) {\n@@ -513,7 +516,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \tpathspec = get_pathspec(prefix, argv);\n \n \t/* be nice with submodule paths ending in a slash */\n-\tread_cache();\n \tif (pathspec)\n \t\tstrip_trailing_slash_from_submodules();\n \ndiff --git a/dir.c b/dir.c\nindex e9254d6..cda2556 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -200,11 +200,35 @@ void add_exclude(const char *string, const char *base,\n \twhich->excludes[which->nr++] = x;\n }\n \n+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n+{\n+\tint pos, len;\n+\tunsigned long sz;\n+\tenum object_type type;\n+\tvoid *data;\n+\tstruct index_state *istate = &the_index;\n+\n+\tlen = strlen(path);\n+\tpos = index_name_pos(istate, path, len);\n+\tif (pos < 0)\n+\t\treturn NULL;\n+\tif (!ce_skip_worktree(istate->cache[pos]))\n+\t\treturn NULL;\n+\tdata = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);\n+\tif (!data || type != OBJ_BLOB) {\n+\t\tfree(data);\n+\t\treturn NULL;\n+\t}\n+\t*size = xsize_t(sz);\n+\treturn data;\n+}\n+\n static int add_excludes_from_file_1(const char *fname,\n \t\t\t\t    const char *base,\n \t\t\t\t    int baselen,\n \t\t\t\t    char **buf_p,\n-\t\t\t\t    struct exclude_list *which)\n+\t\t\t\t    struct exclude_list *which,\n+\t\t\t\t    int check_index)\n {\n \tstruct stat st;\n \tint fd, i;\n@@ -212,20 +236,26 @@ static int add_excludes_from_file_1(const char *fname,\n \tchar *buf, *entry;\n \n \tfd = open(fname, O_RDONLY);\n-\tif (fd < 0 || fstat(fd, &st) < 0)\n-\t\tgoto err;\n-\tsize = xsize_t(st.st_size);\n-\tif (size == 0) {\n-\t\tclose(fd);\n-\t\treturn 0;\n+\tif (fd < 0 || fstat(fd, &st) < 0) {\n+\t\tif (0 <= fd)\n+\t\t\tclose(fd);\n+\t\tif (!check_index ||\n+\t\t    (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)\n+\t\t\treturn -1;\n \t}\n-\tbuf = xmalloc(size+1);\n-\tif (read_in_full(fd, buf, size) != size)\n-\t{\n-\t\tfree(buf);\n-\t\tgoto err;\n+\telse {\n+\t\tsize = xsize_t(st.st_size);\n+\t\tif (size == 0) {\n+\t\t\tclose(fd);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tbuf = xmalloc(size);\n+\t\tif (read_in_full(fd, buf, size) != size) {\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tclose(fd);\n \t}\n-\tclose(fd);\n \n \tif (buf_p)\n \t\t*buf_p = buf;\n@@ -240,17 +270,12 @@ static int add_excludes_from_file_1(const char *fname,\n \t\t}\n \t}\n \treturn 0;\n-\n- err:\n-\tif (0 <= fd)\n-\t\tclose(fd);\n-\treturn -1;\n }\n \n void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n {\n \tif (add_excludes_from_file_1(fname, \"\", 0, NULL,\n-\t\t\t\t     &dir->exclude_list[EXC_FILE]) < 0)\n+\t\t\t\t     &dir->exclude_list[EXC_FILE], 0) < 0)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n@@ -301,7 +326,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n \t\tadd_excludes_from_file_1(dir->basebuf,\n \t\t\t\t\t dir->basebuf, stk->baselen,\n-\t\t\t\t\t &stk->filebuf, el);\n+\t\t\t\t\t &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\n \t\tcurrent = stk->baselen;\n \t}\ndiff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\nindex c65bca8..132c476 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -64,6 +64,8 @@ two/*.4\n echo '!*.2\n !*.8' >one/two/.gitignore\n \n+allignores='.gitignore one/.gitignore one/two/.gitignore'\n+\n test_expect_success \\\n     'git ls-files --others with various exclude options.' \\\n     'git ls-files --others \\\n@@ -85,6 +87,26 @@ test_expect_success \\\n        >output &&\n      test_cmp expect output'\n \n+test_expect_success 'setup skip-worktree gitignore' '\n+\tgit add $allignores &&\n+\tgit update-index --skip-worktree $allignores &&\n+\trm $allignores\n+'\n+\n+test_expect_success \\\n+    'git ls-files --others with various exclude options.' \\\n+    'git ls-files --others \\\n+       --exclude=\\*.6 \\\n+       --exclude-per-directory=.gitignore \\\n+       --exclude-from=.git/ignore \\\n+       >output &&\n+     test_cmp expect output'\n+\n+test_expect_success 'restore gitignore' '\n+\tgit checkout $allignores &&\n+\trm .git/index\n+'\n+\n cat > excludes-file <<\\EOF\n *.[1-8]\n e*\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 118c6eb..7d8ed68 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -22,6 +22,25 @@ test_expect_success 'setup' '\n \n '\n \n+test_expect_success 'git clean with skip-worktree .gitignore' '\n+\tgit update-index --skip-worktree .gitignore &&\n+\trm .gitignore &&\n+\tmkdir -p build docs &&\n+\ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n+\tgit clean &&\n+\ttest -f Makefile &&\n+\ttest -f README &&\n+\ttest -f src/part1.c &&\n+\ttest -f src/part2.c &&\n+\ttest ! -f a.out &&\n+\ttest ! -f src/part3.c &&\n+\ttest -f docs/manual.txt &&\n+\ttest -f obj.o &&\n+\ttest -f build/lib.so &&\n+\tgit update-index --no-skip-worktree .gitignore &&\n+\tgit checkout .gitignore\n+'\n+\n test_expect_success 'git clean' '\n \n \tmkdir -p build docs &&\n-- \n1.6.3.GIT\n"},{"id":"121336","messageId":"1250776033-12395-9-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-8-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 08/19] unpack-trees(): carry skip-worktree bit over in merged_entry()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:02Z","receivedAt":"2009-08-20T13:47:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In this code path, we would remove \"old\" and replace it with \"merge\".\n\"old\" may have skip-worktree bit, so re-add it to \"merge\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n unpack-trees.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 3eda263..dc6d74a 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -680,6 +680,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\t} else {\n \t\t\tif (verify_uptodate(old, o))\n \t\t\t\treturn -1;\n+\t\t\tif (ce_skip_worktree(old))\n+\t\t\t\tupdate |= CE_SKIP_WORKTREE;\n \t\t\tinvalidate_ce_path(old, o);\n \t\t}\n \t}\n-- \n1.6.3.GIT\n"},{"id":"121338","messageId":"1250776033-12395-10-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-9-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 09/19] excluded_1(): support exclude files in index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:03Z","receivedAt":"2009-08-20T13:47:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Index does not really have \"directories\", attempts to match \"foo/\"\nagainst index will fail unless someone tries to reconstruct directories\nfrom a list of file.\n\nObserving that dtype in this function can never be NULL (otherwise\nit would segfault), dtype NULL will be used to say \"hey.. you are\nmatching against index\" and behave properly.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c |    6 ++++++\n 1 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex cda2556..a4a91d5 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -349,6 +349,12 @@ static int excluded_1(const char *pathname,\n \t\t\tint to_exclude = x->to_exclude;\n \n \t\t\tif (x->flags & EXC_FLAG_MUSTBEDIR) {\n+\t\t\t\tif (!dtype) {\n+\t\t\t\t\tif (!prefixcmp(pathname, exclude))\n+\t\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t}\n \t\t\t\tif (*dtype == DT_UNKNOWN)\n \t\t\t\t\t*dtype = get_dtype(NULL, pathname, pathlen);\n \t\t\t\tif (*dtype != DT_DIR)\n-- \n1.6.3.GIT\n"},{"id":"121337","messageId":"1250776033-12395-11-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-10-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 10/19] dir.c: export excluded_1() and add_excludes_from_file_1()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:04Z","receivedAt":"2009-08-20T13:47:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These functions are used to handle .gitignore. They are now exported\nso that sparse checkout can reuse.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n dir.c |   32 ++++++++++++++++----------------\n dir.h |    4 ++++\n 2 files changed, 20 insertions(+), 16 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex a4a91d5..3a8d3e6 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -223,12 +223,12 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size)\n \treturn data;\n }\n \n-static int add_excludes_from_file_1(const char *fname,\n-\t\t\t\t    const char *base,\n-\t\t\t\t    int baselen,\n-\t\t\t\t    char **buf_p,\n-\t\t\t\t    struct exclude_list *which,\n-\t\t\t\t    int check_index)\n+int add_excludes_from_file_to_list(const char *fname,\n+\t\t\t\t   const char *base,\n+\t\t\t\t   int baselen,\n+\t\t\t\t   char **buf_p,\n+\t\t\t\t   struct exclude_list *which,\n+\t\t\t\t   int check_index)\n {\n \tstruct stat st;\n \tint fd, i;\n@@ -274,8 +274,8 @@ static int add_excludes_from_file_1(const char *fname,\n \n void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n {\n-\tif (add_excludes_from_file_1(fname, \"\", 0, NULL,\n-\t\t\t\t     &dir->exclude_list[EXC_FILE], 0) < 0)\n+\tif (add_excludes_from_file_to_list(fname, \"\", 0, NULL,\n+\t\t\t\t\t   &dir->exclude_list[EXC_FILE], 0) < 0)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n@@ -324,9 +324,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n \t\tmemcpy(dir->basebuf + current, base + current,\n \t\t       stk->baselen - current);\n \t\tstrcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);\n-\t\tadd_excludes_from_file_1(dir->basebuf,\n-\t\t\t\t\t dir->basebuf, stk->baselen,\n-\t\t\t\t\t &stk->filebuf, el, 1);\n+\t\tadd_excludes_from_file_to_list(dir->basebuf,\n+\t\t\t\t\t       dir->basebuf, stk->baselen,\n+\t\t\t\t\t       &stk->filebuf, el, 1);\n \t\tdir->exclude_stack = stk;\n \t\tcurrent = stk->baselen;\n \t}\n@@ -336,9 +336,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)\n /* Scan the list and let the last match determine the fate.\n  * Return 1 for exclude, 0 for include and -1 for undecided.\n  */\n-static int excluded_1(const char *pathname,\n-\t\t      int pathlen, const char *basename, int *dtype,\n-\t\t      struct exclude_list *el)\n+int excluded_from_list(const char *pathname,\n+\t\t       int pathlen, const char *basename, int *dtype,\n+\t\t       struct exclude_list *el)\n {\n \tint i;\n \n@@ -412,8 +412,8 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)\n \n \tprep_exclude(dir, pathname, basename-pathname);\n \tfor (st = EXC_CMDL; st <= EXC_FILE; st++) {\n-\t\tswitch (excluded_1(pathname, pathlen, basename,\n-\t\t\t\t   dtype_p, &dir->exclude_list[st])) {\n+\t\tswitch (excluded_from_list(pathname, pathlen, basename,\n+\t\t\t\t\t   dtype_p, &dir->exclude_list[st])) {\n \t\tcase 0:\n \t\t\treturn 0;\n \t\tcase 1:\ndiff --git a/dir.h b/dir.h\nindex 320b6a2..3bead5f 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -69,7 +69,11 @@ extern int match_pathspec(const char **pathspec, const char *name, int namelen,\n extern int fill_directory(struct dir_struct *dir, const char **pathspec);\n extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);\n \n+extern int excluded_from_list(const char *pathname, int pathlen, const char *basename,\n+\t\t\t      int *dtype, struct exclude_list *el);\n extern int excluded(struct dir_struct *, const char *, int *);\n+extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,\n+\t\t\t\t\t  char **buf_p, struct exclude_list *which, int check_index);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\n \t\t\tint baselen, struct exclude_list *which);\n-- \n1.6.3.GIT\n"},{"id":"121339","messageId":"1250776033-12395-12-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-11-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 11/19] Introduce \"sparse checkout\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:05Z","receivedAt":"2009-08-20T13:47:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With skip-worktree bit, you can manually set it to unwanted files,\nthen remove them: you would have the so-called sparse checkout. The\ndisadvantages are:\n\n - Porcelain tools are not aware of this. Everytime you do an\n   operation that may update working directory, skip-worktree may be\n   cleared out. You have to set them again.\n\n - You still have to remove skip-worktree'd files manually, which is\n   boring and ineffective.\n\nThese will be addressed in the following patches. This patch gives an\nidea what is \"sparse checkout\" in Documentation/git-read-tree.txt.\nThis file is chosen instead of git-checkout.txt because it is quite\ntechnical and user-unfriendly. I'd expect git-checkout.txt to have\nsomething when Porcelain support is done.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-read-tree.txt |   44 +++++++++++++++++++++++++++++++++++++++\n 1 files changed, 44 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt\nindex 4a932b0..8b39716 100644\n--- a/Documentation/git-read-tree.txt\n+++ b/Documentation/git-read-tree.txt\n@@ -360,6 +360,50 @@ middle of doing, and when your working tree is ready (i.e. you\n have finished your work-in-progress), attempt the merge again.\n \n \n+Sparse checkout\n+---------------\n+\n+\"Sparse checkout\" allows to sparsely populate working directory.\n+It uses skip-worktree bit (see linkgit:git-update-index[1]) to tell\n+Git whether a file on working directory is worth looking at.\n+\n+\"git read-tree\" and other merge-based commands (\"git merge\", \"git\n+checkout\"...) can help maintaining skip-worktree bitmap and working\n+directory update. `$GIT_DIR/info/sparse-checkout` is used to\n+define the skip-worktree reference bitmap. When \"git read-tree\" needs\n+to update working directory, it will reset skip-worktree bit in index\n+based on this file, which uses the same syntax as .gitignore files.\n+If an entry matches a pattern in this file, skip-worktree will be\n+set on that entry. Otherwise, skip-worktree will be unset.\n+\n+Then it compares the new skip-worktree value with the previous one. If\n+skip-worktree turns from unset to set, it will add the corresponding\n+file back. If it turns from set to unset, that file will be removed.\n+\n+While `$GIT_DIR/info/sparse-checkout` is usually used to specify what\n+files are in. You can also specify what files are _not_ in, using\n+negate patterns. For example, to remove file \"unwanted\":\n+\n+----------------\n+*\n+!unwanted\n+----------------\n+\n+Another tricky thing is fully repopulating working directory when you\n+no longer want sparse checkout. You cannot just disable \"sparse\n+checkout\" because skip-worktree are still in the index and you working\n+directory is still sparsely populated. You should re-populate working\n+directory with the `$GIT_DIR/info/sparse-checkout` file content as\n+follows:\n+\n+----------------\n+*\n+----------------\n+\n+Then you can disable sparse checkout. Sparse checkout support in \"git\n+read-tree\" and similar commands is disabled by default.\n+\n+\n SEE ALSO\n --------\n linkgit:git-write-tree[1]; linkgit:git-ls-files[1];\n-- \n1.6.3.GIT\n"},{"id":"121340","messageId":"1250776033-12395-13-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-12-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 12/19] unpack-trees(): add CE_WT_REMOVE to remove on worktree alone","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:06Z","receivedAt":"2009-08-20T13:47:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"CE_REMOVE now removes both worktree and index versions. Sparse\ncheckout must be able to remove worktree version while keep the\nindex intact when checkout area is narrowed.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h        |    3 +++\n unpack-trees.c |    9 ++++++++-\n 2 files changed, 11 insertions(+), 1 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 3b0e5fc..0e912a9 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -177,6 +177,9 @@ struct cache_entry {\n #define CE_HASHED    (0x100000)\n #define CE_UNHASHED  (0x200000)\n \n+/* Only remove in work directory, not index */\n+#define CE_WT_REMOVE (0x400000)\n+\n /*\n  * Extended on-disk flags\n  */\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex dc6d74a..6a51a69 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -78,7 +78,7 @@ static int check_updates(struct unpack_trees_options *o)\n \tif (o->update && o->verbose_update) {\n \t\tfor (total = cnt = 0; cnt < index->cache_nr; cnt++) {\n \t\t\tstruct cache_entry *ce = index->cache[cnt];\n-\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE))\n+\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))\n \t\t\t\ttotal++;\n \t\t}\n \n@@ -92,6 +92,13 @@ static int check_updates(struct unpack_trees_options *o)\n \tfor (i = 0; i < index->cache_nr; i++) {\n \t\tstruct cache_entry *ce = index->cache[i];\n \n+\t\tif (ce->ce_flags & CE_WT_REMOVE) {\n+\t\t\tdisplay_progress(progress, ++cnt);\n+\t\t\tif (o->update)\n+\t\t\t\tunlink_entry(ce);\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (ce->ce_flags & CE_REMOVE) {\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tif (o->update)\n-- \n1.6.3.GIT\n"},{"id":"121342","messageId":"1250776033-12395-14-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-13-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 13/19] unpack-trees.c: generalize verify_* functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:07Z","receivedAt":"2009-08-20T13:47:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n unpack-trees.c |   23 ++++++++++++++++++-----\n 1 files changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 6a51a69..8eb4b70 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -452,8 +452,9 @@ static int same(struct cache_entry *a, struct cache_entry *b)\n  * When a CE gets turned into an unmerged entry, we\n  * want it to be up-to-date\n  */\n-static int verify_uptodate(struct cache_entry *ce,\n-\t\tstruct unpack_trees_options *o)\n+static int verify_uptodate_1(struct cache_entry *ce,\n+\t\t\t\t   struct unpack_trees_options *o,\n+\t\t\t\t   const char *error_msg)\n {\n \tstruct stat st;\n \n@@ -478,7 +479,13 @@ static int verify_uptodate(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(ERRORMSG(o, not_uptodate_file), ce->name);\n+\t\terror(error_msg, ce->name);\n+}\n+\n+static int verify_uptodate(struct cache_entry *ce,\n+\t\t\t   struct unpack_trees_options *o)\n+{\n+\treturn verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));\n }\n \n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n@@ -586,8 +593,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,\n  * We do not want to remove or overwrite a working tree file that\n  * is not tracked, unless it is ignored.\n  */\n-static int verify_absent(struct cache_entry *ce, const char *action,\n-\t\t\t struct unpack_trees_options *o)\n+static int verify_absent_1(struct cache_entry *ce, const char *action,\n+\t\t\t\t struct unpack_trees_options *o,\n+\t\t\t\t const char *error_msg)\n {\n \tstruct stat st;\n \n@@ -667,6 +675,11 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \t}\n \treturn 0;\n }\n+static int verify_absent(struct cache_entry *ce, const char *action,\n+\t\t\t struct unpack_trees_options *o)\n+{\n+\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));\n+}\n \n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\tstruct unpack_trees_options *o)\n-- \n1.6.3.GIT\n"},{"id":"121345","messageId":"1250776033-12395-15-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-14-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 14/19] unpack-trees(): \"enable\" sparse checkout and load $GIT_DIR/info/sparse-checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:08Z","receivedAt":"2009-08-20T13:47:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This patch introduces core.sparseCheckout, which will control whether\nsparse checkout support is enabled in unpack_trees()\n\nIt also loads sparse-checkout file that will be used in the next patch.\nI split it out so the next patch will be shorter, easier to read.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt        |    4 ++++\n Documentation/git-read-tree.txt |    4 +++-\n cache.h                         |    1 +\n config.c                        |    5 +++++\n environment.c                   |    1 +\n unpack-trees.c                  |   36 ++++++++++++++++++++++++++++++------\n unpack-trees.h                  |    4 ++++\n 7 files changed, 48 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5256c7f..bfaa78d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -439,6 +439,10 @@ On some file system/operating system combinations, this is unreliable.\n Set this config setting to 'rename' there; However, This will remove the\n check that makes sure that existing object files will not get overwritten.\n \n+core.sparseCheckout::\n+\tEnable \"sparse checkout\" feature. See section \"Sparse checkout\" in\n+\tlinkgit:git-read-tree[1] for more information.\n+\n add.ignore-errors::\n \tTells 'git-add' to continue adding files when some files cannot be\n \tadded due to indexing errors. Equivalent to the '--ignore-errors'\ndiff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt\nindex 8b39716..fc3f08b 100644\n--- a/Documentation/git-read-tree.txt\n+++ b/Documentation/git-read-tree.txt\n@@ -401,7 +401,9 @@ follows:\n ----------------\n \n Then you can disable sparse checkout. Sparse checkout support in \"git\n-read-tree\" and similar commands is disabled by default.\n+read-tree\" and similar commands is disabled by default. You need to\n+turn `core.sparseCheckout` on in order to have sparse checkout\n+support.\n \n \n SEE ALSO\ndiff --git a/cache.h b/cache.h\nindex 0e912a9..a401daf 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -528,6 +528,7 @@ extern int auto_crlf;\n extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n+extern int core_apply_sparse_checkout;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/config.c b/config.c\nindex e87edea..abd762e 100644\n--- a/config.c\n+++ b/config.c\n@@ -503,6 +503,11 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.sparsecheckout\")) {\n+\t\tcore_apply_sparse_checkout = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 5de6837..f9ea207 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -50,6 +50,7 @@ enum push_default_type push_default = PUSH_DEFAULT_MATCHING;\n #endif\n enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;\n int grafts_replace_parents = 1;\n+int core_apply_sparse_checkout;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 8eb4b70..44f8fdf 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -378,6 +378,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n {\n \tint ret;\n \tstatic struct cache_entry *dfc;\n+\tstruct exclude_list el;\n \n \tif (len > MAX_UNPACK_TREES)\n \t\tdie(\"unpack_trees takes at most %d trees\", MAX_UNPACK_TREES);\n@@ -387,6 +388,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tstate.quiet = 1;\n \tstate.refresh_cache = 1;\n \n+\tmemset(&el, 0, sizeof(el));\n+\tif (!core_apply_sparse_checkout || !o->update)\n+\t\to->skip_sparse_checkout = 1;\n+\tif (!o->skip_sparse_checkout) {\n+\t\tif (add_excludes_from_file_to_list(git_path(\"info/sparse-checkout\"), \"\", 0, NULL, &el, 0) < 0)\n+\t\t\to->skip_sparse_checkout = 1;\n+\t\telse\n+\t\t\to->el = &el;\n+\t}\n+\n \tmemset(&o->result, 0, sizeof(o->result));\n \to->result.initialized = 1;\n \tif (o->src_index) {\n@@ -407,26 +418,39 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\tinfo.fn = unpack_callback;\n \t\tinfo.data = o;\n \n-\t\tif (traverse_trees(len, t, &info) < 0)\n-\t\t\treturn unpack_failed(o, NULL);\n+\t\tif (traverse_trees(len, t, &info) < 0) {\n+\t\t\tret = unpack_failed(o, NULL);\n+\t\t\tgoto done;\n+\t\t}\n \t}\n \n \t/* Any left-over entries in the index? */\n \tif (o->merge) {\n \t\twhile (o->pos < o->src_index->cache_nr) {\n \t\t\tstruct cache_entry *ce = o->src_index->cache[o->pos];\n-\t\t\tif (unpack_index_entry(ce, o) < 0)\n-\t\t\t\treturn unpack_failed(o, NULL);\n+\t\t\tif (unpack_index_entry(ce, o) < 0) {\n+\t\t\t\tret = unpack_failed(o, NULL);\n+\t\t\t\tgoto done;\n+\t\t\t}\n \t\t}\n \t}\n \n-\tif (o->trivial_merges_only && o->nontrivial_merge)\n-\t\treturn unpack_failed(o, \"Merge requires file-level merging\");\n+\tif (o->trivial_merges_only && o->nontrivial_merge) {\n+\t\tret = unpack_failed(o, \"Merge requires file-level merging\");\n+\t\tgoto done;\n+\t}\n \n \to->src_index = NULL;\n \tret = check_updates(o) ? (-2) : 0;\n \tif (o->dst_index)\n \t\t*o->dst_index = o->result;\n+\n+done:\n+\tfor (i = 0;i < el.nr;i++)\n+\t\tfree(el.excludes[i]);\n+\tif (el.excludes)\n+\t\tfree(el.excludes);\n+\n \treturn ret;\n }\n \ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex d19df44..5c9e98a 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -4,6 +4,7 @@\n #define MAX_UNPACK_TREES 8\n \n struct unpack_trees_options;\n+struct exclude_list;\n \n typedef int (*merge_fn_t)(struct cache_entry **src,\n \t\tstruct unpack_trees_options *options);\n@@ -28,6 +29,7 @@ struct unpack_trees_options {\n \t\t     skip_unmerged,\n \t\t     initial_checkout,\n \t\t     diff_index_cached,\n+\t\t     skip_sparse_checkout,\n \t\t     gently;\n \tconst char *prefix;\n \tint pos;\n@@ -44,6 +46,8 @@ struct unpack_trees_options {\n \tstruct index_state *dst_index;\n \tstruct index_state *src_index;\n \tstruct index_state result;\n+\n+\tstruct exclude_list *el; /* for internal use */\n };\n \n extern int unpack_trees(unsigned n, struct tree_desc *t,\n-- \n1.6.3.GIT\n"},{"id":"121341","messageId":"1250776033-12395-16-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-15-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 15/19] unpack_trees(): apply $GIT_DIR/info/sparse-checkout to the final index","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:09Z","receivedAt":"2009-08-20T13:47:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n unpack-trees.c |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n unpack-trees.h |    2 +\n 2 files changed, 83 insertions(+), 1 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 44f8fdf..2d8ecb7 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -32,6 +32,12 @@ static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n \n \t/* bind_overlap */\n \t\"Entry '%s' overlaps with '%s'.  Cannot bind.\",\n+\n+\t/* sparse_not_uptodate_file */\n+\t\"Entry '%s' not uptodate. Cannot update sparse checkout.\",\n+\n+\t/* would_lose_orphaned */\n+\t\"Working tree file '%s' would be %s by sparse checkout update.\",\n };\n \n #define ERRORMSG(o,fld) \\\n@@ -125,6 +131,57 @@ static int check_updates(struct unpack_trees_options *o)\n \treturn errs != 0;\n }\n \n+static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);\n+static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);\n+\n+static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)\n+{\n+\tconst char *basename;\n+\n+\tif (ce_stage(ce))\n+\t\treturn 0;\n+\n+\tbasename = strrchr(ce->name, '/');\n+\tbasename = basename ? basename+1 : ce->name;\n+\treturn excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;\n+}\n+\n+static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)\n+{\n+\tint was_skip_worktree = ce_skip_worktree(ce);\n+\n+\tif (will_have_skip_worktree(ce, o))\n+\t\tce->ce_flags |= CE_SKIP_WORKTREE;\n+\telse\n+\t\tce->ce_flags &= ~CE_SKIP_WORKTREE;\n+\n+\t/*\n+\t * We only care about files getting into the checkout area\n+\t * If merge strategies want to remove some, go ahead, this\n+\t * flag will be removed eventually in unpack_trees() if it's\n+\t * outside checkout area.\n+\t */\n+\tif (ce->ce_flags & CE_REMOVE)\n+\t\treturn 0;\n+\n+\tif (!was_skip_worktree && ce_skip_worktree(ce)) {\n+\t\t/*\n+\t\t * If CE_UPDATE is set, verify_uptodate() must be called already\n+\t\t * also stat info may have lost after merged_entry() so calling\n+\t\t * verify_uptodate() again may fail\n+\t\t */\n+\t\tif (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))\n+\t\t\treturn -1;\n+\t\tce->ce_flags |= CE_WT_REMOVE;\n+\t}\n+\tif (was_skip_worktree && !ce_skip_worktree(ce)) {\n+\t\tif (verify_absent_sparse(ce, \"overwritten\", o))\n+\t\t\treturn -1;\n+\t\tce->ce_flags |= CE_UPDATE;\n+\t}\n+\treturn 0;\n+}\n+\n static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)\n {\n \tint ret = o->fn(src, o);\n@@ -376,7 +433,7 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)\n  */\n int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)\n {\n-\tint ret;\n+\tint i, ret;\n \tstatic struct cache_entry *dfc;\n \tstruct exclude_list el;\n \n@@ -440,6 +497,17 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\tgoto done;\n \t}\n \n+\tif (!o->skip_sparse_checkout) {\n+\t\tfor (i = 0;i < o->result.cache_nr;i++) {\n+\t\t\tstruct cache_entry *ce = o->result.cache[i];\n+\n+\t\t\tif (apply_sparse_checkout(ce, o)) {\n+\t\t\t\tret = -1;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \to->src_index = NULL;\n \tret = check_updates(o) ? (-2) : 0;\n \tif (o->dst_index)\n@@ -512,6 +580,12 @@ static int verify_uptodate(struct cache_entry *ce,\n \treturn verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));\n }\n \n+static int verify_uptodate_sparse(struct cache_entry *ce,\n+\t\t\t\t  struct unpack_trees_options *o)\n+{\n+\treturn verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));\n+}\n+\n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n {\n \tif (ce)\n@@ -705,6 +779,12 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));\n }\n \n+static int verify_absent_sparse(struct cache_entry *ce, const char *action,\n+\t\t\t struct unpack_trees_options *o)\n+{\n+\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));\n+}\n+\n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\tstruct unpack_trees_options *o)\n {\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 5c9e98a..95ff36c 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -15,6 +15,8 @@ struct unpack_trees_error_msgs {\n \tconst char *not_uptodate_dir;\n \tconst char *would_lose_untracked;\n \tconst char *bind_overlap;\n+\tconst char *sparse_not_uptodate_file;\n+\tconst char *would_lose_orphaned;\n };\n \n struct unpack_trees_options {\n-- \n1.6.3.GIT\n"},{"id":"121343","messageId":"1250776033-12395-17-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-16-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 16/19] unpack-trees(): ignore worktree check outside checkout area","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:10Z","receivedAt":"2009-08-20T13:47:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"verify_absent() and verify_uptodate() are used to ensure worktree\nis safe to be updated, then CE_REMOVE or CE_UPDATE will be set.\nFinally check_updates() bases on CE_REMOVE, CE_UPDATE and the\nrecently added CE_WT_REMOVE to update working directory accordingly.\n\nThe entries that are checked may eventually be left out of checkout\narea (done later in apply_sparse_checkout()). We don't want to update\noutside checkout area. This patch teaches Git to assume \"good\",\nskip these checks when it's sure those entries will be outside checkout\narea, and clear CE_REMOVE|CE_UPDATE that could be set due to this\nassumption.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n unpack-trees.c |   12 ++++++++++++\n 1 files changed, 12 insertions(+), 0 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 2d8ecb7..72743b3 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -505,6 +505,14 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\t\t\tret = -1;\n \t\t\t\tgoto done;\n \t\t\t}\n+\t\t\t/*\n+\t\t\t * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout\n+\t\t\t * area as a result of ce_skip_worktree() shortcuts in\n+\t\t\t * verify_absent() and verify_uptodate(). Clear them.\n+\t\t\t */\n+\t\t\tif (ce_skip_worktree(ce))\n+\t\t\t\tce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);\n+\n \t\t}\n \t}\n \n@@ -577,6 +585,8 @@ static int verify_uptodate_1(struct cache_entry *ce,\n static int verify_uptodate(struct cache_entry *ce,\n \t\t\t   struct unpack_trees_options *o)\n {\n+\tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n+\t\treturn 0;\n \treturn verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));\n }\n \n@@ -776,6 +786,8 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n static int verify_absent(struct cache_entry *ce, const char *action,\n \t\t\t struct unpack_trees_options *o)\n {\n+\tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n+\t\treturn 0;\n \treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));\n }\n \n-- \n1.6.3.GIT\n"},{"id":"121347","messageId":"1250776033-12395-18-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-17-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 17/19] read-tree: add --no-sparse-checkout to disable sparse checkout support","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:11Z","receivedAt":"2009-08-20T13:47:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-read-tree.txt |    6 +++++-\n builtin-read-tree.c             |    4 +++-\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt\nindex fc3f08b..ea7b0b2 100644\n--- a/Documentation/git-read-tree.txt\n+++ b/Documentation/git-read-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]\n \t\t[-u [--exclude-per-directory=<gitignore>] | -i]]\n-\t\t[--index-output=<file>]\n+\t\t[--index-output=<file>] [--no-sparse-checkout]\n \t\t<tree-ish1> [<tree-ish2> [<tree-ish3>]]\n \n \n@@ -110,6 +110,10 @@ OPTIONS\n \tdirectories the index file and index output file are\n \tlocated in.\n \n+--no-sparse-checkout::\n+\tDisable sparse checkout support even if `core.sparseCheckout`\n+\tis true.\n+\n <tree-ish#>::\n \tThe id of the tree object(s) to be read/merged.\n \ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 9c2d634..f5acb1a 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -31,7 +31,7 @@ static int list_tree(unsigned char *sha1)\n }\n \n static const char * const read_tree_usage[] = {\n-\t\"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]]  [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]\",\n+\t\"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]\",\n \tNULL\n };\n \n@@ -98,6 +98,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n \t\t  PARSE_OPT_NONEG, exclude_per_directory_cb },\n \t\tOPT_SET_INT('i', NULL, &opts.index_only,\n \t\t\t    \"don't check the working tree after merging\", 1),\n+\t\tOPT_SET_INT(0, \"no-sparse-checkout\", &opts.skip_sparse_checkout,\n+\t\t\t    \"skip applying sparse checkout filter\", 1),\n \t\tOPT_END()\n \t};\n \n-- \n1.6.3.GIT\n"},{"id":"121346","messageId":"1250776033-12395-19-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-18-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 18/19] Add tests for sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:12Z","receivedAt":"2009-08-20T13:47:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1009-read-tree-sparse-checkout.sh |  154 ++++++++++++++++++++++++++++++++++\n 1 files changed, 154 insertions(+), 0 deletions(-)\n create mode 100755 t/t1009-read-tree-sparse-checkout.sh\n\ndiff --git a/t/t1009-read-tree-sparse-checkout.sh b/t/t1009-read-tree-sparse-checkout.sh\nnew file mode 100755\nindex 0000000..2192f5a\n--- /dev/null\n+++ b/t/t1009-read-tree-sparse-checkout.sh\n@@ -0,0 +1,154 @@\n+#!/bin/sh\n+\n+test_description='sparse checkout tests'\n+\n+. ./test-lib.sh\n+\n+cat >expected <<EOF\n+100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0\tinit.t\n+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0\tsub/added\n+EOF\n+test_expect_success 'setup' '\n+\ttest_commit init &&\n+\techo modified >> init.t &&\n+\tmkdir sub &&\n+\ttouch sub/added &&\n+\tgit add init.t sub/added &&\n+\tgit commit -m \"modified and added\" &&\n+\tgit tag top &&\n+\tgit rm sub/added &&\n+\tgit commit -m removed &&\n+\tgit tag removed &&\n+\tgit checkout top &&\n+\tgit ls-files --stage > result &&\n+\ttest_cmp expected result\n+'\n+\n+cat >expected.swt <<EOF\n+H init.t\n+H sub/added\n+EOF\n+test_expect_success 'read-tree without .git/info/sparse-checkout' '\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files --stage > result &&\n+\ttest_cmp expected result &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result\n+'\n+\n+test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '\n+\techo > .git/info/sparse-checkout\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest -f init.t &&\n+\ttest -f sub/added\n+'\n+\n+test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '\n+\tgit config core.sparsecheckout true &&\n+\techo > .git/info/sparse-checkout &&\n+\tgit read-tree --no-sparse-checkout -m -u HEAD &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest -f init.t &&\n+\ttest -f sub/added\n+'\n+\n+cat >expected.swt <<EOF\n+S init.t\n+S sub/added\n+EOF\n+test_expect_success 'read-tree with empty .git/info/sparse-checkout' '\n+\tgit config core.sparsecheckout true &&\n+\techo > .git/info/sparse-checkout &&\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files --stage > result &&\n+\ttest_cmp expected result &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest ! -f init.t &&\n+\ttest ! -f sub/added\n+'\n+\n+cat >expected.swt <<EOF\n+S init.t\n+H sub/added\n+EOF\n+test_expect_success 'match directories with trailing slash' '\n+\techo sub/ > .git/info/sparse-checkout &&\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest ! -f init.t &&\n+\ttest -f sub/added\n+'\n+\n+cat >expected.swt <<EOF\n+H init.t\n+H sub/added\n+EOF\n+test_expect_failure 'match directories without trailing slash' '\n+\techo init.t > .git/info/sparse-checkout &&\n+\techo sub >> .git/info/sparse-checkout &&\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest ! -f init.t &&\n+\ttest -f sub/added\n+'\n+\n+cat >expected.swt <<EOF\n+H init.t\n+S sub/added\n+EOF\n+test_expect_success 'checkout area changes' '\n+\techo init.t > .git/info/sparse-checkout &&\n+\tgit read-tree -m -u HEAD &&\n+\tgit ls-files -t > result &&\n+\ttest_cmp expected.swt result &&\n+\ttest -f init.t &&\n+\ttest ! -f sub/added\n+'\n+\n+test_expect_success 'read-tree updates worktree, absent case' '\n+\techo sub/added > .git/info/sparse-checkout &&\n+\tgit checkout -f top &&\n+\tgit read-tree -m -u HEAD^ &&\n+\ttest ! -f init.t\n+'\n+\n+test_expect_success 'read-tree updates worktree, dirty case' '\n+\techo sub/added > .git/info/sparse-checkout &&\n+\tgit checkout -f top &&\n+\techo dirty > init.t &&\n+\tgit read-tree -m -u HEAD^ &&\n+\tgrep -q dirty init.t &&\n+\trm init.t\n+'\n+\n+test_expect_success 'read-tree removes worktree, dirty case' '\n+\techo init.t > .git/info/sparse-checkout &&\n+\tgit checkout -f top &&\n+\techo dirty > added &&\n+\tgit read-tree -m -u HEAD^ &&\n+\tgrep -q dirty added\n+'\n+\n+test_expect_success 'read-tree adds to worktree, absent case' '\n+\techo init.t > .git/info/sparse-checkout &&\n+\tgit checkout -f removed &&\n+\tgit read-tree -u -m HEAD^ &&\n+\ttest ! -f sub/added\n+'\n+\n+test_expect_success 'read-tree adds to worktree, dirty case' '\n+\techo init.t > .git/info/sparse-checkout &&\n+\tgit checkout -f removed &&\n+\tmkdir sub &&\n+\techo dirty > sub/added &&\n+\tgit read-tree -u -m HEAD^ &&\n+\tgrep -q dirty sub/added\n+'\n+\n+test_done\n-- \n1.6.3.GIT\n"},{"id":"121344","messageId":"1250776033-12395-20-git-send-email-pclouds@gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-19-git-send-email-pclouds@gmail.com","subject":"[RFC PATCH v4 19/19] sparse checkout: inhibit empty worktree","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-20T13:47:13Z","receivedAt":"2009-08-20T13:47:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The way sparse checkout works, users may empty their worktree\ncompletely, because of non-matching sparse-checkout spec, or empty\nspec. I believe this is not desired. This patch makes Git refuse to\nproduce such worktree.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t1009-read-tree-sparse-checkout.sh |   10 +++-------\n unpack-trees.c                       |    7 +++++++\n 2 files changed, 10 insertions(+), 7 deletions(-)\n\ndiff --git a/t/t1009-read-tree-sparse-checkout.sh b/t/t1009-read-tree-sparse-checkout.sh\nindex 2192f5a..62246db 100755\n--- a/t/t1009-read-tree-sparse-checkout.sh\n+++ b/t/t1009-read-tree-sparse-checkout.sh\n@@ -55,20 +55,16 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-\n \ttest -f sub/added\n '\n \n-cat >expected.swt <<EOF\n-S init.t\n-S sub/added\n-EOF\n test_expect_success 'read-tree with empty .git/info/sparse-checkout' '\n \tgit config core.sparsecheckout true &&\n \techo > .git/info/sparse-checkout &&\n-\tgit read-tree -m -u HEAD &&\n+\ttest_must_fail git read-tree -m -u HEAD &&\n \tgit ls-files --stage > result &&\n \ttest_cmp expected result &&\n \tgit ls-files -t > result &&\n \ttest_cmp expected.swt result &&\n-\ttest ! -f init.t &&\n-\ttest ! -f sub/added\n+\ttest -f init.t &&\n+\ttest -f sub/added\n '\n \n cat >expected.swt <<EOF\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 72743b3..80ae2a0 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -498,6 +498,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t}\n \n \tif (!o->skip_sparse_checkout) {\n+\t\tint empty_worktree = 1;\n \t\tfor (i = 0;i < o->result.cache_nr;i++) {\n \t\t\tstruct cache_entry *ce = o->result.cache[i];\n \n@@ -512,8 +513,14 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\t\t */\n \t\t\tif (ce_skip_worktree(ce))\n \t\t\t\tce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);\n+\t\t\telse\n+\t\t\t\tempty_worktree = 0;\n \n \t\t}\n+\t\tif (o->result.cache_nr && empty_worktree) {\n+\t\t\tret = unpack_failed(o, \"Sparse checkout leaves no entry on working directory\");\n+\t\t\tgoto done;\n+\t\t}\n \t}\n \n \to->src_index = NULL;\n-- \n1.6.3.GIT\n"},{"id":"121352","messageId":"m3ocqamrz6.fsf@localhost.localdomain","threadId":"20673","inReplyTo":"1250776033-12395-1-git-send-email-pclouds@gmail.com","subject":"Re: [RFC PATCH v4 00/19] Sparse checkout","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-08-20T15:21:03Z","receivedAt":"2009-08-20T15:21:03Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"乧畹﻿ễ渠周﻿ਾ⁗敬捯浥⁴漠瑨攠景畲瑨⁲潵湤⁯映獰慲獥⁣桥捫潵琠瑨楳⁹敡爬⁤畢扥搠≔桥ਾ⁭畭浹幗幗幗幗幗湯ⵣ桥捫潵琠牥瑵牮猢Ⱐ慬瑨潵杨⁴桥⁢楴⁮潷⁣潭敳⁵湤敲ਾ⁡⁮敷⁮慭攠≳歩瀭睯牫瑲敥∠嬱崮⁔桩猠獥物敳⁨慳⁴睯⁰慲瑳㨠獰慲獥ਾ⁷潲歴牥攠慮搠獰慲獥⁣桥捫潵琮⁄整慩汳⁷楬氠扥⁧楶敮⁢敬潷⸊㸠ਾ⁆潲⁢牡癥⁵獥牳⁯映瑨楳⁳敲楥猺⁉⁨慶攠灵獨敤⁩琠瑯⁭礠獰慲獥ⵣ桥捫潵琊㸠扲慮捨⁛㉝⁳漠祯甠捡渠橵獴⁣汯湥⁩琠慮搠瑥獴⁡睡礮⁖楳楢汥⁣桡湧敳㨊㸠ਾ†ⴠ瑨攠獰散⁩猠湯眠⹧楴⽩湦漯獰慲獥ⵣ桥捫潵琊㸠‭⁴桥⁳灥挠楳⁰潳楴楶攠浡瑣桩湧Ⱐ瑨慴⁩猠楴⁭慴捨敳⁷桡琠彩獟⁩渊㸠†⁷潲歴牥攬⁮潴⁷桡琠楳⁯畴⁯映睯牫瑲敥⁬楫攠瑨攠污獴⁳敲楥猊㸠‭⁹潵⁮敥搠瑯⁳整⁣潲攮獰慲獥捨散歯畴⁮漠慤摩瑩潮慬⁣潭浡湤⁡牧畭敮琊㸠†⁩猠湥敤敤⸊嬮⸮崊ਾ⁛ㅝ⁔桥牥⁡牥⁡⁦敷⁲敡獯湳⁢敨楮搠瑨楳⁲敮慭攺ਾ†ⴠ瑨敲攠楳⁮潴桩湧⁡扯畴•捨散歯畴∠楮⁴桥⁦楲獴⁰慲琬⁩琧猠慢潵琊㸠†•獰慲獥⁷潲歴牥攢ਾ†ⴠ瑨攠摯畢汥⁮敧慴楯渠楳獵攠睩瑨•湯ⴢ⁰慲琠潦•湯ⵣ桥捫潵琢ਾ†ⴠ湥眠湡浥⁭慫敳⁣慣桥⹨⁤楦映慬楧渠扥慵瑩晵汬礊੗敬氬⁯渠潮攠桡湤⁳楤攠楴⁩猠浯牥⁣汥慲⁷桡琠楳⁣桥捫敤⁯畴Ⱐ੡湤⁰敲桡灳⁭潲攠捯浭潮⁣慳攠潦⁣桥捫楮朠潵琠潮汹⁳敬散瑥搠晩汥猊楳⁳業灬敲⸊੏渠瑨攠潴桥爠桡湤⁩映楴⁷慳‮杩琯楮景⽮漭捨散歯畴⁯爠捨散歯畴ⵥ硣汵摥Ⰺ潲⁷潲歴牥攭數捬畤攬⁥浰瑹⁦楬攠潲⁬慣欠潦⁳畣栠晩汥⁷潵汤⁭敡渠晵汬੣桥捫潵琮†周敮⁣潲攮獰慲獥捨散歯畴 潲⁣潲攮獰慲獥晩汥⤠睯畬搠扥੮敥摥搠潮汹⁦潲⁰敲景牭慮捥⁲敡獯湳 湯⁮敥搠瑯⁥硡浩湥੣桥捫潵琭數捬畤攠晩汥⤠敶敮⁩映睥⁤散楤攠瑯⁳桩瀠數慭灬攠晩汥⁷楴栊捯浭敮瑳⁤敳捲楢楮朠楴猠獹湴慸Ⱐ楮⁴敭灬慴敳⽩湦漭ⵣ桥捫潵琭數捬畤攊ੂ畴⁴桡琠楳⁪畳琠扩步獨敤摩湧Ⱐ摯渧琠汥琠楴⁤楳瑲慣琠祯甠晲潭⁴桥⁲敡氊楳獵攠潦⁴桩猠獥物敳⸮⸊ਭⴠ੊慫畢⁎慲﻿ę扳歩ਊ䥮⁲敬慴敤⁮敷猺⁴桥牥⁩猠牥慬汹⁮漠煵敳瑩潮⁴桡琠扩步⁳桥摳⁳桯畬搠扥ੰ慩湴敤⁲敤⸠剥慬汹⸠ ⡊潨慮湥猠卣桩湤敬楮Ⱐ潮⁧楴⁭慩汩湧⁬楳琩�"},{"id":"121353","messageId":"vpqy6pey013.fsf@bauges.imag.fr","threadId":"20673","inReplyTo":"m3ocqamrz6.fsf@localhost.localdomain","subject":"Re: [RFC PATCH v4 00/19] Sparse checkout","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2009-08-20T15:31:36Z","receivedAt":"2009-08-20T15:31:36Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Content-Type: text/plain; charset=utf-16be\n                                    ^^^^^^^^\n\nI don't know what it should have been, but not UTF-16 ;-)\n\n(readable message below)\n\n> Nguyen Thai\n>> Welcome to the fourth round of sparse checkout this year, dubbed \"The\n>> mummy^W^W^W^W^Wno-checkout returns\", although the bit now comes under\n>> a new name \"skip-worktree\" [1]. This series has two parts: sparse\n>> worktree and sparse checkout. Details will be given below.\n>> \n>> For brave users of this series: I have pushed it to my sparse-checkout\n>> branch [2] so you can just clone it and test away. Visible changes:\n>> \n>>  - the spec is now .git/info/sparse-checkout\n>>  - the spec is positive matching, that is it matches what _is_ in\n>>    worktree, not what is out of worktree like the last series\n>>  - you need to set core.sparsecheckout no additional command argument\n>>    is needed.\n> [...]\n>\n>> [1] There are a few reasons behind this rename:\n>>  - there is nothing about \"checkout\" in the first part, it's about\n>>    \"sparse worktree\"\n>>  - the double negation issue with \"no-\" part of \"no-checkout\"\n>>  - new name makes cache.h diff align beautifully\n>\n> Well, on one hand side it is more clear what is checked out, \n> and perhaps more common case of checking out only selected files\n> is simpler.\n>\n> On the other hand if it was .git/info/no-checkout or checkout-exclude,\n> or worktree-exclude, empty file or lack of such file would mean full\n> checkout.  Then core.sparsecheckout (or core.sparsefile) would be\n> needed only for performance reasons (no need to examine\n> checkout-exclude file) even if we decide to ship example file with\n> comments describing its syntax, in templates/info--checkout-exclude\n>\n> But that is just bikeshedding, don't let it distract you from the real\n> issue of this series...\n>\n> -- \n> Jakub Narebski\n>\n> In related news: there is really no question that bike sheds should be\n> painted red. Really.  \n> (Johannes Schindelin, on git mailing list)\n\n-- \nMatthieu\n"},{"id":"121398","messageId":"7vprapip1v.fsf@alter.siamese.dyndns.org","threadId":"20673","inReplyTo":"1250776033-12395-1-git-send-email-pclouds@gmail.com","subject":"Re: [RFC PATCH v4 00/19] Sparse checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-08-21T07:50:04Z","receivedAt":"2009-08-21T07:50:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The recent assume-unchanged \"breakage\" that lets Git overwrite\n> assume-unchanged files worried me. I sat back, checked and wrote tests\n\nYeah, it worries me, too.  Does the fix to make sure the sparse stuff\nwon't be broken apply equally to assume-unchanged?  Does the series have\nsuch fixes to assume-unchanged bit as well?\n\nBy the way, I think the first patch in the earlier series, 540e694\n(Prevent diff machinery from examining assume-unchanged entries on\nworktree, 2009-08-11), is a good change regardless of the sparse\nimplementation, and I'm inclined to say that we should merge that part\n(and I suspect there will be similar fixes to really ignore differences to\nCE_VALID entries) first and then queue this new series on top.\n"},{"id":"121405","messageId":"fcaeb9bf0908210215t5f694e3fp20c34e9c327f515b@mail.gmail.com","threadId":"20673","inReplyTo":"7vprapip1v.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC PATCH v4 00/19] Sparse checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-21T09:15:28Z","receivedAt":"2009-08-21T09:15:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2009/8/21 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> The recent assume-unchanged \"breakage\" that lets Git overwrite\n>> assume-unchanged files worried me. I sat back, checked and wrote tests\n>\n> Yeah, it worries me, too.  Does the fix to make sure the sparse stuff\n> won't be broken apply equally to assume-unchanged?  Does the series have\n> such fixes to assume-unchanged bit as well?\n\nThis series does not fix assume-unchanged bit. I'd like to focus on\nskip-worktree bit now. I still need to write a few more tests for\ngit-apply, git-checkout... but I think they are safe. It's up to you\nto see if changes apply to assume-unchanged bit, in patches 4/19 and\n5/19. I don't know if I understand assume-unchanged semantics\ncorrectly anymore :-)\n\nAnyway I think we could put a big fat warning above ce_uptodate()\nmacro definition, saying that this bit/macro could be faked by\nassume-unchanged or skip-worktree bit, so don't rely on that macro\nwhen it comes to writing (at least for skip-worktree part).\n\nHmm.. _or_ we could make it clear whether it is truly uptodate, or\nfaked uptodate. Some code path will be updated to only trust \"truly\nuptodate\" bit, which is clearer and easier to grasp than messy logics\nlike \"if (ce_uptodate(ce) && !ce_skip_worktree(ce))\". Something like\nthis as a starting point (for demonstration only, I don't think it\ncompiles)\n\ndiff --git a/cache.h b/cache.h\nindex a401daf..05fb746 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -179,6 +179,7 @@ struct cache_entry {\n\n /* Only remove in work directory, not index */\n #define CE_WT_REMOVE (0x400000)\n+#define CE_ASSUME_UPTODATE  (0x800000)\n\n /*\n  * Extended on-disk flags\n@@ -236,9 +237,11 @@ static inline size_t ce_namelen(const struct\ncache_entry *ce)\n \t\t\t    ondisk_cache_entry_extended_size(ce_namelen(ce)) : \\\n \t\t\t    ondisk_cache_entry_size(ce_namelen(ce)))\n #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)\n-#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)\n+#define ce_truely_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)\n+#define ce_uptodate(ce) ((ce)->ce_flags & (CE_UPTODATE | CE_ASSUME_UPTODATE))\n #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)\n #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)\n+#define ce_mark_assume_uptodate(ce) ((ce)->ce_flags |= CE_ASSUME_UPTODATE)\n\n #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)\n static inline unsigned int create_ce_mode(unsigned int mode)\ndiff --git a/read-cache.c b/read-cache.c\nindex 5ee7d9d..c022955 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1005,7 +1005,7 @@ static struct cache_entry\n*refresh_cache_ent(struct index_state *istate,\n \t\treturn ce;\n\n \tif (!ignore_valid && ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))) {\n-\t\tce_mark_uptodate(ce);\n+\t\tce_mark_assume_uptodate(ce);\n \t\treturn ce;\n \t}\n\nStill thinking of it. Seems like a good change...\n\n> By the way, I think the first patch in the earlier series, 540e694\n> (Prevent diff machinery from examining assume-unchanged entries on\n> worktree, 2009-08-11), is a good change regardless of the sparse\n> implementation, and I'm inclined to say that we should merge that part\n> (and I suspect there will be similar fixes to really ignore differences to\n> CE_VALID entries) first and then queue this new series on top.\n\nI have no problem with that.\n-- \nDuy\n"},{"id":"121406","messageId":"fcaeb9bf0908210219h1c2a4702i81661ced00f39232@mail.gmail.com","threadId":"20673","inReplyTo":"1250776033-12395-5-git-send-email-pclouds@gmail.com","subject":"Re: [RFC PATCH v4 04/19] Teach Git to respect skip-worktree bit (reading part)","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-21T09:19:01Z","receivedAt":"2009-08-21T09:19:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2009/8/20 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> grep: turn on --cached for files that is marked skip-worktree\n> ls-files: do not check for deleted file that is marked skip-worktree\n> update-index: ignore update request if it's skip-worktree, while still allows removing\n> diff*: skip worktree version\n\nI kept reminding myself and still managed to miss it. skip-worktree\nbit is not completely independent from assume-unchanged. It shares\nCE_MATCH_IGNORE_VALID flag in ie_match_stat(). Should have mentioned\nthat in commit log.\n-- \nDuy\n"},{"id":"121442","messageId":"7vd46pf4yw.fsf@alter.siamese.dyndns.org","threadId":"20673","inReplyTo":"fcaeb9bf0908210219h1c2a4702i81661ced00f39232@mail.gmail.com","subject":"Re: [RFC PATCH v4 04/19] Teach Git to respect skip-worktree bit (reading part)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-08-21T17:32:07Z","receivedAt":"2009-08-21T17:32:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> 2009/8/20 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>> grep: turn on --cached for files that is marked skip-worktree\n>> ls-files: do not check for deleted file that is marked skip-worktree\n>> update-index: ignore update request if it's skip-worktree, while still allows removing\n>> diff*: skip worktree version\n>\n> I kept reminding myself and still managed to miss it. skip-worktree\n> bit is not completely independent from assume-unchanged. It shares\n> CE_MATCH_IGNORE_VALID flag in ie_match_stat(). Should have mentioned\n> that in commit log.\n\nYeah, that is why I feel uneasy reading your response to my other message:\n\n  This series does not fix assume-unchanged bit. I'd like to focus on\n  skip-worktree bit now. ... I don't know if I understand assume-unchanged\n  semantics correctly anymore :-)\n"},{"id":"121515","messageId":"fcaeb9bf0908220456q7adc5c38h9567ad1f8ca16deb@mail.gmail.com","threadId":"20673","inReplyTo":"7vd46pf4yw.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC PATCH v4 04/19] Teach Git to respect skip-worktree bit (reading part)","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-08-22T11:56:32Z","receivedAt":"2009-08-22T11:56:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Aug 22, 2009 at 12:32 AM, Junio C Hamano<gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> 2009/8/20 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>>> grep: turn on --cached for files that is marked skip-worktree\n>>> ls-files: do not check for deleted file that is marked skip-worktree\n>>> update-index: ignore update request if it's skip-worktree, while still allows removing\n>>> diff*: skip worktree version\n>>\n>> I kept reminding myself and still managed to miss it. skip-worktree\n>> bit is not completely independent from assume-unchanged. It shares\n>> CE_MATCH_IGNORE_VALID flag in ie_match_stat(). Should have mentioned\n>> that in commit log.\n\nI should have just renamed it to CE_MATCH_PARANOID. That would fit\nboth assume-unchanged and skip-worktree bits.\n\n> Yeah, that is why I feel uneasy reading your response to my other message:\n>\n>  This series does not fix assume-unchanged bit. I'd like to focus on\n>  skip-worktree bit now. ... I don't know if I understand assume-unchanged\n>  semantics correctly anymore :-)\n\nThe thing is there are grey areas where assume-unchanged is used. If\nworktree version is there and really uptodate, nothing to worry about.\nWe seemed to care of local changes in assume-unchanged files, but\nthat's not written in stone. So I started off with a new bit, clearer\nsemantics. Perhaps new changes could be applied back to\nassume-unchanged bit, but I'd stay away from that for now.\n-- \nDuy\n"}]}