{"thread":{"id":"15736","subject":"[PATCH/resent 0/9] Sparse checkout (first half)","startedAt":"2008-10-01T04:04:00Z","lastAt":"2008-11-20T22:16:58Z","messageCount":23,"participants":["Nguyễn Thái Ngọc Duy","Santi Béjar","Nguyen Thai Ngoc Duy","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"92018","messageId":"1222833849-22129-1-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":null,"subject":"[PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:00Z","receivedAt":"2008-10-01T04:04:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is the first half of the series, making git ready for sparse\ncheckout. The only difference from the last (first half) sent\nseries is safeguard bitmask fix in 1/9\n\nNguyá»n ThÃ¡i Ngá»c Duy (9):\n  Extend index to save more flags\n  Introduce CE_NO_CHECKOUT bit\n  ls-files: add options to support sparse checkout\n  update-index: refactor mark_valid() in preparation for new options\n  update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT\n    bit\n  ls-files: Add tests for --sparse and friends\n  Prevent diff machinery from examining worktree outside sparse\n    checkout\n  checkout_entry(): CE_NO_CHECKOUT on checked out entries.\n  grep: skip files outside sparse checkout area\n\n .gitignore                            |    1 +\n Documentation/git-checkout.txt        |   34 +++++++++++++++++\n Documentation/git-grep.txt            |    4 +-\n Documentation/git-ls-files.txt        |   24 +++++++++++-\n Documentation/git-update-index.txt    |   13 ++++++\n Makefile                              |    2 +-\n builtin-grep.c                        |    7 +++-\n builtin-ls-files.c                    |   41 ++++++++++++++++++--\n builtin-update-index.c                |   40 ++++++++++++-------\n cache.h                               |   66 +++++++++++++++++++++++++++++++--\n diff-lib.c                            |    5 +-\n diff.c                                |    4 +-\n entry.c                               |    1 +\n read-cache.c                          |   57 ++++++++++++++++++++++------\n t/t2104-update-index-no-checkout.sh   |   36 ++++++++++++++++++\n t/t3004-ls-files-sparse.sh            |   40 ++++++++++++++++++++\n t/t3004/cached.expected               |    5 ++\n t/t3004/deleted.expected              |    1 +\n t/t3004/everything.expected           |   10 +++++\n t/t3004/modified.expected             |    2 +\n t/t3004/no-checkout.expected          |    2 +\n t/t3004/orphaned-no-checkout.expected |    3 +\n t/t3004/orphaned.expected             |    1 +\n t/t3004/others.expected               |    2 +\n t/t3004/sparse-cached.expected        |    3 +\n t/t3004/sparse-everything.expected    |   11 +++++\n test-index-version.c                  |   14 +++++++\n 27 files changed, 385 insertions(+), 44 deletions(-)\n create mode 100755 t/t2104-update-index-no-checkout.sh\n create mode 100755 t/t3004-ls-files-sparse.sh\n create mode 100644 t/t3004/cached.expected\n create mode 100644 t/t3004/deleted.expected\n create mode 100644 t/t3004/everything.expected\n create mode 100644 t/t3004/modified.expected\n create mode 100644 t/t3004/no-checkout.expected\n create mode 100644 t/t3004/orphaned-no-checkout.expected\n create mode 100644 t/t3004/orphaned.expected\n create mode 100644 t/t3004/others.expected\n create mode 100644 t/t3004/sparse-cached.expected\n create mode 100644 t/t3004/sparse-everything.expected\n create mode 100644 test-index-version.c\n"},{"id":"92019","messageId":"1222833849-22129-2-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/9] Extend index to save more flags","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:01Z","receivedAt":"2008-10-01T04:04:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The on-disk format of index only saves 16 bit flags, nearly all have\nbeen used. The last bit (CE_EXTENDED) is used to for future extension.\n\nThis patch extends index entry format to save more flags in future.\nThe new entry format will be used when CE_EXTENDED bit is 1.\n\nBecause older implementation may not understand CE_EXTENDED bit and\nmisread the new format, if there is any extended entry in index, index\nheader version will turn 3, which makes it incompatible for older git.\nIf there is none, header version will return to 2 again.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h      |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n read-cache.c |   51 +++++++++++++++++++++++++++++++++++++++++----------\n 2 files changed, 95 insertions(+), 14 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex f4b8ddf..5180879 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -109,6 +109,26 @@ struct ondisk_cache_entry {\n \tchar name[FLEX_ARRAY]; /* more */\n };\n \n+/*\n+ * This struct is used when CE_EXTENDED bit is 1\n+ * The struct must match ondisk_cache_entry exactly from\n+ * ctime till flags\n+ */\n+struct ondisk_cache_entry_extended {\n+\tstruct cache_time ctime;\n+\tstruct cache_time mtime;\n+\tunsigned int dev;\n+\tunsigned int ino;\n+\tunsigned int mode;\n+\tunsigned int uid;\n+\tunsigned int gid;\n+\tunsigned int size;\n+\tunsigned char sha1[20];\n+\tunsigned short flags;\n+\tunsigned short flags2;\n+\tchar name[FLEX_ARRAY]; /* more */\n+};\n+\n struct cache_entry {\n \tunsigned int ce_ctime;\n \tunsigned int ce_mtime;\n@@ -130,7 +150,15 @@ struct cache_entry {\n #define CE_VALID     (0x8000)\n #define CE_STAGESHIFT 12\n \n-/* In-memory only */\n+/*\n+ * Range 0xFFFF0000 in ce_flags is divided into\n+ * two parts: in-memory flags and on-disk ones.\n+ * Flags in CE_EXTENDED_FLAGS will get saved on-disk\n+ * if you want to save a new flag, add it in\n+ * CE_EXTENDED_FLAGS\n+ *\n+ * In-memory only flags\n+ */\n #define CE_UPDATE    (0x10000)\n #define CE_REMOVE    (0x20000)\n #define CE_UPTODATE  (0x40000)\n@@ -140,6 +168,24 @@ struct cache_entry {\n #define CE_UNHASHED  (0x200000)\n \n /*\n+ * Extended on-disk flags\n+ */\n+/* CE_EXTENDED2 is for future extension */\n+#define CE_EXTENDED2 0x80000000\n+\n+#define CE_EXTENDED_FLAGS (0)\n+\n+/*\n+ * Safeguard to avoid saving wrong flags:\n+ *  - CE_EXTENDED2 won't get saved until its semantic is known\n+ *  - Bits in 0x0000FFFF have been saved in ce_flags already\n+ *  - Bits in 0x003F0000 are currently in-memory flags\n+ */\n+#if CE_EXTENDED_FLAGS & 0x803FFFFF\n+#error \"CE_EXTENDED_FLAGS out of range\"\n+#endif\n+\n+/*\n  * Copy the sha1 and stat state of a cache entry from one to\n  * another. But we never change the name, or the hash state!\n  */\n@@ -171,7 +217,9 @@ static inline size_t ce_namelen(const struct cache_entry *ce)\n }\n \n #define ce_size(ce) cache_entry_size(ce_namelen(ce))\n-#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))\n+#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \\\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_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)\n@@ -214,8 +262,10 @@ static inline int ce_to_dtype(const struct cache_entry *ce)\n \t(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \\\n \tS_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)\n \n-#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)\n-#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)\n+#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)\n+#define cache_entry_size(len) flexible_size(cache_entry,len)\n+#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)\n+#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)\n \n struct index_state {\n \tstruct cache_entry **cache;\ndiff --git a/read-cache.c b/read-cache.c\nindex c5a8659..667c36b 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1096,7 +1096,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)\n \n \tif (hdr->hdr_signature != htonl(CACHE_SIGNATURE))\n \t\treturn error(\"bad signature\");\n-\tif (hdr->hdr_version != htonl(2))\n+\tif (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))\n \t\treturn error(\"bad index version\");\n \tSHA1_Init(&c);\n \tSHA1_Update(&c, hdr, size - 20);\n@@ -1131,6 +1131,7 @@ int read_index(struct index_state *istate)\n static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)\n {\n \tsize_t len;\n+\tconst char *name;\n \n \tce->ce_ctime = ntohl(ondisk->ctime.sec);\n \tce->ce_mtime = ntohl(ondisk->mtime.sec);\n@@ -1143,19 +1144,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en\n \t/* On-disk flags are just 16 bits */\n \tce->ce_flags = ntohs(ondisk->flags);\n \n-\t/* For future extension: we do not understand this entry yet */\n-\tif (ce->ce_flags & CE_EXTENDED)\n-\t\tdie(\"Unknown index entry format\");\n \thashcpy(ce->sha1, ondisk->sha1);\n \n \tlen = ce->ce_flags & CE_NAMEMASK;\n+\n+\tif (ce->ce_flags & CE_EXTENDED) {\n+\t\tstruct ondisk_cache_entry_extended *ondisk2;\n+\t\tint extended_flags;\n+\t\tondisk2 = (struct ondisk_cache_entry_extended *)ondisk;\n+\t\textended_flags = ntohs(ondisk2->flags2) << 16;\n+\t\t/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */\n+\t\tif (extended_flags & ~CE_EXTENDED_FLAGS)\n+\t\t\tdie(\"Unknown index entry format %08x\", extended_flags);\n+\t\tce->ce_flags |= extended_flags;\n+\t\tname = ondisk2->name;\n+\t}\n+\telse\n+\t\tname = ondisk->name;\n+\n \tif (len == CE_NAMEMASK)\n-\t\tlen = strlen(ondisk->name);\n+\t\tlen = strlen(name);\n \t/*\n \t * NEEDSWORK: If the original index is crafted, this copy could\n \t * go unchecked.\n \t */\n-\tmemcpy(ce->name, ondisk->name, len + 1);\n+\tmemcpy(ce->name, name, len + 1);\n }\n \n static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)\n@@ -1415,6 +1428,7 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)\n {\n \tint size = ondisk_ce_size(ce);\n \tstruct ondisk_cache_entry *ondisk = xcalloc(1, size);\n+\tchar *name;\n \n \tondisk->ctime.sec = htonl(ce->ce_ctime);\n \tondisk->ctime.nsec = 0;\n@@ -1428,7 +1442,15 @@ static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)\n \tondisk->size = htonl(ce->ce_size);\n \thashcpy(ondisk->sha1, ce->sha1);\n \tondisk->flags = htons(ce->ce_flags);\n-\tmemcpy(ondisk->name, ce->name, ce_namelen(ce));\n+\tif (ce->ce_flags & CE_EXTENDED) {\n+\t\tstruct ondisk_cache_entry_extended *ondisk2;\n+\t\tondisk2 = (struct ondisk_cache_entry_extended *)ondisk;\n+\t\tondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);\n+\t\tname = ondisk2->name;\n+\t}\n+\telse\n+\t\tname = ondisk->name;\n+\tmemcpy(name, ce->name, ce_namelen(ce));\n \n \treturn ce_write(c, fd, ondisk, size);\n }\n@@ -1437,16 +1459,25 @@ int write_index(const struct index_state *istate, int newfd)\n {\n \tSHA_CTX c;\n \tstruct cache_header hdr;\n-\tint i, err, removed;\n+\tint i, err, removed, extended;\n \tstruct cache_entry **cache = istate->cache;\n \tint entries = istate->cache_nr;\n \n-\tfor (i = removed = 0; i < entries; i++)\n+\tfor (i = removed = extended = 0; i < entries; i++) {\n \t\tif (cache[i]->ce_flags & CE_REMOVE)\n \t\t\tremoved++;\n \n+\t\t/* reduce extended entries if possible */\n+\t\tcache[i]->ce_flags &= ~CE_EXTENDED;\n+\t\tif (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {\n+\t\t\textended++;\n+\t\t\tcache[i]->ce_flags |= CE_EXTENDED;\n+\t\t}\n+\t}\n+\n \thdr.hdr_signature = htonl(CACHE_SIGNATURE);\n-\thdr.hdr_version = htonl(2);\n+\t/* for extended format, increase version so older git won't try to read it */\n+\thdr.hdr_version = htonl(extended ? 3 : 2);\n \thdr.hdr_entries = htonl(entries - removed);\n \n \tSHA1_Init(&c);\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92020","messageId":"1222833849-22129-3-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-2-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/9] Introduce CE_NO_CHECKOUT bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:02Z","receivedAt":"2008-10-01T04:04:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This bit is the basis of sparse checkout. If this bit is on, the entry\nis outside sparse checkout and therefore should be ignored (similar\nto CE_VALID)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt |   33 +++++++++++++++++++++++++++++++++\n cache.h                        |   10 +++++++++-\n read-cache.c                   |    6 +++---\n 3 files changed, 45 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 82e154d..4bd9eba 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -171,6 +171,39 @@ the reflog for HEAD where you were, e.g.\n $ git log -g -2 HEAD\n ------------\n \n+Sparse checkout\n+---------------\n+\n+Normally when you checkout a branch, your working directory\n+will be fully populated. In some situations, you just need to\n+work on certain files, no full checkout is needed. Sparse\n+checkout is a mode that limits the checkout area according to your\n+needs. With sparse checkout, you can work on a single file, a\n+collection of files, a subdirectory or a collection of separated\n+subdirectories.\n+\n+Because sparse checkout uses a new index format, it will be\n+incompatible with git prior to 1.6.0 regarding worktree operations.\n+Operations that only need access to the repository itself, such as\n+clone, push, or pull/fetch from another (normal) repository... should\n+not be affected by sparse checkout.\n+\n+In sparse checkout mode, checkout status of every files in your\n+working directory will be recorded in index. If a file is marked\n+\"no-checkout\", it means that file is not needed to be present in\n+working directory by user or any git command. When a new file is added\n+to index, it will be marked \"checkout\" unless sparse patterns are\n+applied.  Unmerged files are always \"checkout\". When you checkout new\n+files using \"git checkout <file>\" they will be automatically marked\n+\"checkout\". Other commands such as \"git apply\" can also checkout new\n+files if they are needed.\n+\n+\"No-checkout\" status is very similar to \"assume-unchanged bit\"\n+(see linkgit:git-update-index[1]). The main difference between them\n+is \"assume unchanged\" bit just ignores corresponding files in working\n+directory while sparse checkout goes a bit farther, remove those files\n+when it is safe to do so.\n+\n \n EXAMPLES\n --------\ndiff --git a/cache.h b/cache.h\nindex 5180879..f13ddbc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -170,10 +170,11 @@ struct cache_entry {\n /*\n  * Extended on-disk flags\n  */\n+#define CE_NO_CHECKOUT 0x40000000\n /* CE_EXTENDED2 is for future extension */\n #define CE_EXTENDED2 0x80000000\n \n-#define CE_EXTENDED_FLAGS (0)\n+#define CE_EXTENDED_FLAGS (CE_NO_CHECKOUT)\n \n /*\n  * Safeguard to avoid saving wrong flags:\n@@ -185,6 +186,9 @@ struct cache_entry {\n #error \"CE_EXTENDED_FLAGS out of range\"\n #endif\n \n+/* \"Assume unchanged\" mask */\n+#define CE_VALID_MASK (CE_VALID | CE_NO_CHECKOUT)\n+\n /*\n  * Copy the sha1 and stat state of a cache entry from one to\n  * another. But we never change the name, or the hash state!\n@@ -222,6 +226,10 @@ 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_no_checkout(ce) ((ce)->ce_flags & CE_NO_CHECKOUT)\n+#define ce_checkout(ce) (!ce_no_checkout(ce))\n+#define ce_mark_no_checkout(ce) ((ce)->ce_flags |= CE_NO_CHECKOUT)\n+#define ce_mark_checkout(ce) ((ce)->ce_flags &= ~CE_NO_CHECKOUT)\n #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)\n \n #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)\ndiff --git a/read-cache.c b/read-cache.c\nindex 667c36b..e965a4c 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -254,7 +254,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_MASK))\n \t\treturn 0;\n \n \tchanged = ce_match_stat_basic(ce, st);\n@@ -962,10 +962,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \t\treturn ce;\n \n \t/*\n-\t * CE_VALID means the user promised us that the change to\n+\t * CE_VALID_MASK 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_MASK)) {\n \t\tce_mark_uptodate(ce);\n \t\treturn ce;\n \t}\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92022","messageId":"1222833849-22129-4-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-3-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/9] ls-files: add options to support sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:03Z","receivedAt":"2008-10-01T04:04:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The first option to be introduced is --sparse, which puts ls-files\nin \"sparse mode\". In this mode, cached entries are divided into\n - checkout entries: shown by --cached (new behavior with --sparse)\n - no-checkout entries: show by --no-checkout (new option)\n - orphaned entries: shown by --orphaned (new option)\n\nOrphaned entries are themselves no-checkout ones but for some reasons\nstill be present in working directory.\n\nWhile at it, fix \"--deleted\" running out of checkout area.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-ls-files.txt |   24 +++++++++++++++++++++-\n builtin-ls-files.c             |   41 ++++++++++++++++++++++++++++++++++++---\n 2 files changed, 59 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 9f85d60..1de68e2 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -10,8 +10,9 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-files' [-z] [-t] [-v]\n-\t\t(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])\\*\n+\t\t(--[cached|deleted|others|ignored|stage|unmerged|killed|modified|orphaned|no-checkout])\\*\n \t\t(-[c|d|o|i|s|u|k|m])\\*\n+\t\t[--sparse]\n \t\t[-x <pattern>|--exclude=<pattern>]\n \t\t[-X <file>|--exclude-from=<file>]\n \t\t[--exclude-per-directory=<file>]\n@@ -32,7 +33,9 @@ OPTIONS\n -------\n -c::\n --cached::\n-\tShow cached files in the output (default)\n+\tShow cached files in the output (default). When used with --sparse,\n+\tshow only cached files that are marked \"checkout\", no-checkout\n+\tentries will be excluded.\n \n -d::\n --deleted::\n@@ -72,6 +75,21 @@ OPTIONS\n \tto file/directory conflicts for checkout-index to\n \tsucceed.\n \n+--no-checkout::\n+\tShow no-checkout entries. This option implies --sparse.\n+\n+--orphaned::\n+\tShow orphaned entries. Orphaned entries are no-checkout\n+\tentries that are present in working directory. This option\n+\timplies --sparse.\n+\n+--sparse::\n+\tWhen --sparse is passed, cached files will be divided into two\n+\tparts: checkout entries and no-checkout entries.\n+\t--cached will only show checkout entries.\n+\tNo-checkout entries can be shown using --orphaned or\n+\t--no-checkout (or both).\n+\n -z::\n \t\\0 line termination on output.\n \n@@ -107,6 +125,8 @@ 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+\t-::\tno-checkout entries\n+\tO::\torphaned entries\n \tM::\tunmerged\n \tR::\tremoved/deleted\n \tC::\tmodified/changed\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex 068f424..873de15 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -20,6 +20,9 @@ static int show_unmerged;\n static int show_modified;\n static int show_killed;\n static int show_valid_bit;\n+static int show_orphaned;\n+static int show_no_checkout;\n+static int sparse_checkout;\n static int line_terminator = '\\n';\n \n static int prefix_len;\n@@ -35,6 +38,8 @@ 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_orphaned = \"\";\n+static const char *tag_no_checkout = \"\";\n \n \n /*\n@@ -235,7 +240,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\tif (show_killed)\n \t\t\tshow_killed_files(dir);\n \t}\n-\tif (show_cached | show_stage) {\n+\tif (show_cached | show_stage | show_orphaned | show_no_checkout) {\n \t\tfor (i = 0; i < active_nr; i++) {\n \t\t\tstruct cache_entry *ce = active_cache[i];\n \t\t\tint dtype = ce_to_dtype(ce);\n@@ -245,6 +250,16 @@ 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 (sparse_checkout && ce_no_checkout(ce)) {\n+\t\t\t\tstruct stat st;\n+\t\t\t\tif (show_no_checkout)\n+\t\t\t\t\tshow_ce_entry(tag_no_checkout, ce);\n+\t\t\t\tif (show_orphaned && !lstat(ce->name, &st))\n+\t\t\t\t\tshow_ce_entry(tag_orphaned, ce);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!(show_cached | show_stage))\n+\t\t\t\tcontinue;\n \t\t\tshow_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);\n \t\t}\n \t}\n@@ -257,7 +272,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\tif (excluded(dir, ce->name, &dtype) != dir->show_ignored)\n \t\t\t\tcontinue;\n \t\t\terr = lstat(ce->name, &st);\n-\t\t\tif (show_deleted && err)\n+\t\t\tif (show_deleted && err && ce_checkout(ce))\n \t\t\t\tshow_ce_entry(tag_removed, ce);\n \t\t\tif (show_modified && ce_modified(ce, &st, 0))\n \t\t\t\tshow_ce_entry(tag_modified, ce);\n@@ -423,7 +438,8 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_\n }\n \n static const char ls_files_usage[] =\n-\t\"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* \"\n+\t\"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified|orphaned|no-checkout])* \"\n+\t\"[ --sparse ] \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n \t\"[ --exclude-per-directory=<filename> ] [--exclude-standard] \"\n \t\"[--full-name] [--abbrev] [--] [<file>]*\";\n@@ -457,6 +473,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\ttag_modified = \"C \";\n \t\t\ttag_other = \"? \";\n \t\t\ttag_killed = \"K \";\n+\t\t\ttag_orphaned = \"O \";\n+\t\t\ttag_no_checkout = \"- \";\n \t\t\tif (arg[1] == 'v')\n \t\t\t\tshow_valid_bit = 1;\n \t\t\tcontinue;\n@@ -465,6 +483,21 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\tshow_cached = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--sparse\")) {\n+\t\t\tsparse_checkout = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--orphaned\")) {\n+\t\t\tshow_orphaned = 1;\n+\t\t\tsparse_checkout = 1;\n+\t\t\trequire_work_tree = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--no-checkout\")) {\n+\t\t\tshow_no_checkout = 1;\n+\t\t\tsparse_checkout = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-d\") || !strcmp(arg, \"--deleted\")) {\n \t\t\tshow_deleted = 1;\n \t\t\tcontinue;\n@@ -593,7 +626,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \n \t/* With no flags, we default to showing the cached files */\n \tif (!(show_stage | show_deleted | show_others | show_unmerged |\n-\t      show_killed | show_modified))\n+\t      show_killed | show_modified | show_orphaned | show_no_checkout))\n \t\tshow_cached = 1;\n \n \tread_cache();\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92021","messageId":"1222833849-22129-5-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-4-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/9] update-index: refactor mark_valid() in preparation for new options","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:04Z","receivedAt":"2008-10-01T04:04:04Z","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 417f972..ae94739 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@@ -649,11 +645,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.0.2.488.gf604a\n"},{"id":"92023","messageId":"1222833849-22129-6-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-5-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:05Z","receivedAt":"2008-10-01T04:04:05Z","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 .gitignore                          |    1 +\n Documentation/git-checkout.txt      |    3 +-\n Documentation/git-update-index.txt  |   13 ++++++++++++\n Makefile                            |    2 +-\n builtin-update-index.c              |   16 ++++++++++++++-\n t/t2104-update-index-no-checkout.sh |   36 +++++++++++++++++++++++++++++++++++\n test-index-version.c                |   14 +++++++++++++\n 7 files changed, 82 insertions(+), 3 deletions(-)\n create mode 100755 t/t2104-update-index-no-checkout.sh\n create mode 100644 test-index-version.c\n\ndiff --git a/.gitignore b/.gitignore\nindex bbaf9de..0c35577 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -147,6 +147,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/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 4bd9eba..2b344e1 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -196,7 +196,8 @@ to index, it will be marked \"checkout\" unless sparse patterns are\n applied.  Unmerged files are always \"checkout\". When you checkout new\n files using \"git checkout <file>\" they will be automatically marked\n \"checkout\". Other commands such as \"git apply\" can also checkout new\n-files if they are needed.\n+files if they are needed. linkgit:git-update-index[1] can be used to\n+update \"checkout/no-checkout\" status in index.\n \n \"No-checkout\" status is very similar to \"assume-unchanged bit\"\n (see linkgit:git-update-index[1]). The main difference between them\ndiff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt\nindex 1d9d81a..ec03e05 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     [--checkout | --no-checkout]\n \t     [--ignore-submodules]\n \t     [--really-refresh] [--unresolve] [--again | -g]\n \t     [--info-only] [--index-info]\n@@ -99,6 +100,18 @@ 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+--checkout::\n+--no-checkout::\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 \"no-checkout\" bit for the paths. This\n+\tbit is used for marking files for sparse checkout. If\n+\ta path is marked \"no-checkout\", then it should not be\n+\tchecked out unless requested by user or needed for a git\n+\tcommand to function.\n+\tSee linkgit:git-checkout[1] for more information about\n+\tsparse checkout.\n+\n -g::\n --again::\n \tRuns 'git-update-index' itself on the paths whose index\ndiff --git a/Makefile b/Makefile\nindex e0c03c3..edb33cb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1327,7 +1327,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X test-index-version$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex ae94739..7514aff 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_no_checkout_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_no_checkout_only) {\n+\t\tif (mark_ce_flags(p, CE_NO_CHECKOUT, mark_no_checkout_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@@ -386,7 +392,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] [--checkout|--no-checkout] [--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@@ -652,6 +658,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, \"--checkout\")) {\n+\t\t\t\tmark_no_checkout_only = UNMARK_FLAG;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(path, \"--no-checkout\")) {\n+\t\t\t\tmark_no_checkout_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/t/t2104-update-index-no-checkout.sh b/t/t2104-update-index-no-checkout.sh\nnew file mode 100755\nindex 0000000..be9f913\n--- /dev/null\n+++ b/t/t2104-update-index-no-checkout.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy\n+#\n+\n+test_description='git update-index no-checkout bits (a.k.a sparse checkout)'\n+\n+. ./test-lib.sh\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+'\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 --no-checkout' '\n+\tgit update-index --no-checkout 1 sub/1 &&\n+\ttest -z \"$(git ls-files --sparse|grep 1)\"'\n+\n+test_expect_success 'index is at version 3 after having some no-checkout entries' '\n+\ttest \"$(test-index-version < .git/index)\" = 3\n+'\n+\n+test_expect_success 'update-index --checkout' '\n+\tgit update-index --checkout 1 sub/1 &&\n+\ttest \"$(git ls-files)\" = \"$(git ls-files --sparse)\"'\n+\n+test_expect_success 'index version is back to 2 when there is no no-checkout entry' '\n+\ttest \"$(test-index-version < .git/index)\" = 2\n+'\n+\n+test_done\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.0.2.488.gf604a\n"},{"id":"92024","messageId":"1222833849-22129-7-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-6-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/9] ls-files: Add tests for --sparse and friends","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:06Z","receivedAt":"2008-10-01T04:04:06Z","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/t3004-ls-files-sparse.sh            |   40 +++++++++++++++++++++++++++++++++\n t/t3004/cached.expected               |    5 ++++\n t/t3004/deleted.expected              |    1 +\n t/t3004/everything.expected           |   10 ++++++++\n t/t3004/modified.expected             |    2 +\n t/t3004/no-checkout.expected          |    2 +\n t/t3004/orphaned-no-checkout.expected |    3 ++\n t/t3004/orphaned.expected             |    1 +\n t/t3004/others.expected               |    2 +\n t/t3004/sparse-cached.expected        |    3 ++\n t/t3004/sparse-everything.expected    |   11 +++++++++\n 11 files changed, 80 insertions(+), 0 deletions(-)\n create mode 100755 t/t3004-ls-files-sparse.sh\n create mode 100644 t/t3004/cached.expected\n create mode 100644 t/t3004/deleted.expected\n create mode 100644 t/t3004/everything.expected\n create mode 100644 t/t3004/modified.expected\n create mode 100644 t/t3004/no-checkout.expected\n create mode 100644 t/t3004/orphaned-no-checkout.expected\n create mode 100644 t/t3004/orphaned.expected\n create mode 100644 t/t3004/others.expected\n create mode 100644 t/t3004/sparse-cached.expected\n create mode 100644 t/t3004/sparse-everything.expected\n\ndiff --git a/t/t3004-ls-files-sparse.sh b/t/t3004-ls-files-sparse.sh\nnew file mode 100755\nindex 0000000..ec2c869\n--- /dev/null\n+++ b/t/t3004-ls-files-sparse.sh\n@@ -0,0 +1,40 @@\n+#!/bin/sh\n+\n+test_description=\"test ls-files in --sparse mode\"\n+\n+. test-lib.sh\n+\n+test_ls_files() {\n+\tT=../t3004/$1.expected\n+\tshift\n+\ttest_expect_success \"ls-files $*\" \"git ls-files $* > result && test_cmp $T result\"\n+}\n+\n+test_expect_success 'setup' '\n+\ttouch other orphaned no-checkout cached modified deleted &&\n+\tgit add orphaned no-checkout cached modified deleted &&\n+\tgit update-index --no-checkout orphaned no-checkout &&\n+\techo modified >> modified &&\n+\trm no-checkout deleted\n+'\n+\n+test_ls_files cached\n+test_ls_files cached --cached\n+test_ls_files sparse-cached --sparse\n+test_ls_files sparse-cached --sparse --cached\n+test_ls_files no-checkout --no-checkout\n+test_ls_files no-checkout --sparse --no-checkout\n+test_ls_files orphaned --orphaned\n+test_ls_files orphaned --sparse --orphaned\n+test_ls_files orphaned-no-checkout -v --no-checkout --orphaned\n+test_ls_files orphaned-no-checkout -v --sparse --no-checkout --orphaned\n+test_ls_files deleted --deleted\n+test_ls_files deleted --sparse --deleted\n+test_ls_files modified --modified\n+test_ls_files modified --sparse --modified\n+test_ls_files others --others\n+test_ls_files others --sparse --others\n+test_ls_files everything -v --cached --deleted --modified --others\n+test_ls_files sparse-everything -v --cached --no-checkout --orphaned --deleted --modified --others\n+\n+test_done\n\\ No newline at end of file\ndiff --git a/t/t3004/cached.expected b/t/t3004/cached.expected\nnew file mode 100644\nindex 0000000..6fd0c78\n--- /dev/null\n+++ b/t/t3004/cached.expected\n@@ -0,0 +1,5 @@\n+cached\n+deleted\n+modified\n+no-checkout\n+orphaned\ndiff --git a/t/t3004/deleted.expected b/t/t3004/deleted.expected\nnew file mode 100644\nindex 0000000..71779d2\n--- /dev/null\n+++ b/t/t3004/deleted.expected\n@@ -0,0 +1 @@\n+deleted\ndiff --git a/t/t3004/everything.expected b/t/t3004/everything.expected\nnew file mode 100644\nindex 0000000..6000328\n--- /dev/null\n+++ b/t/t3004/everything.expected\n@@ -0,0 +1,10 @@\n+? other\n+? result\n+H cached\n+H deleted\n+H modified\n+H no-checkout\n+H orphaned\n+R deleted\n+C deleted\n+C modified\ndiff --git a/t/t3004/modified.expected b/t/t3004/modified.expected\nnew file mode 100644\nindex 0000000..644a96e\n--- /dev/null\n+++ b/t/t3004/modified.expected\n@@ -0,0 +1,2 @@\n+deleted\n+modified\ndiff --git a/t/t3004/no-checkout.expected b/t/t3004/no-checkout.expected\nnew file mode 100644\nindex 0000000..b2a429f\n--- /dev/null\n+++ b/t/t3004/no-checkout.expected\n@@ -0,0 +1,2 @@\n+no-checkout\n+orphaned\ndiff --git a/t/t3004/orphaned-no-checkout.expected b/t/t3004/orphaned-no-checkout.expected\nnew file mode 100644\nindex 0000000..d687ef0\n--- /dev/null\n+++ b/t/t3004/orphaned-no-checkout.expected\n@@ -0,0 +1,3 @@\n+- no-checkout\n+- orphaned\n+O orphaned\ndiff --git a/t/t3004/orphaned.expected b/t/t3004/orphaned.expected\nnew file mode 100644\nindex 0000000..571b267\n--- /dev/null\n+++ b/t/t3004/orphaned.expected\n@@ -0,0 +1 @@\n+orphaned\ndiff --git a/t/t3004/others.expected b/t/t3004/others.expected\nnew file mode 100644\nindex 0000000..bf5bf2b\n--- /dev/null\n+++ b/t/t3004/others.expected\n@@ -0,0 +1,2 @@\n+other\n+result\ndiff --git a/t/t3004/sparse-cached.expected b/t/t3004/sparse-cached.expected\nnew file mode 100644\nindex 0000000..3453483\n--- /dev/null\n+++ b/t/t3004/sparse-cached.expected\n@@ -0,0 +1,3 @@\n+cached\n+deleted\n+modified\ndiff --git a/t/t3004/sparse-everything.expected b/t/t3004/sparse-everything.expected\nnew file mode 100644\nindex 0000000..5df0599\n--- /dev/null\n+++ b/t/t3004/sparse-everything.expected\n@@ -0,0 +1,11 @@\n+? other\n+? result\n+H cached\n+H deleted\n+H modified\n+- no-checkout\n+- orphaned\n+O orphaned\n+R deleted\n+C deleted\n+C modified\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92025","messageId":"1222833849-22129-8-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-7-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/9] Prevent diff machinery from examining worktree outside sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:07Z","receivedAt":"2008-10-01T04:04: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 diff-lib.c |    5 +++--\n diff.c     |    4 +++-\n 2 files changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ae96c64..992280b 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -161,7 +161,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_no_checkout(ce))\n \t\t\tcontinue;\n \n \t\tchanged = check_removed(ce, &st);\n@@ -348,6 +348,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,\n \tstruct rev_info *revs = cbdata->revs;\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_no_checkout(idx));\n \t/*\n \t * Backward compatibility wart - \"diff-index -m\" does\n \t * not mean \"do not ignore merges\", but \"match_missing\".\n@@ -355,7 +357,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 b001d7b..c001e2c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1798,8 +1798,10 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int\n \n \t/*\n \t * If ce matches the file in the work tree, we can reuse it.\n+\t * For sparse checkout case, ce_uptodate() may be true although\n+\t * the file may or may not exist in the work tree.\n \t */\n-\tif (ce_uptodate(ce) ||\n+\tif ((ce_uptodate(ce) && ce_checkout(ce)) ||\n \t    (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))\n \t\treturn 1;\n \n-- \n1.6.0.2.488.gf604a\n"},{"id":"92026","messageId":"1222833849-22129-9-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-8-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/9] checkout_entry(): CE_NO_CHECKOUT on checked out entries.","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:08Z","receivedAt":"2008-10-01T04:04:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With this you can just do \"git checkout some-files\" to\nwiden your checkout. One caveat though: caller must save\nthe index.\n\nFor all of its callers (unpack_trees(), checkout-index, checkout\nand apply), only \"git apply\" does not write index back.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n entry.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/entry.c b/entry.c\nindex aa2ee46..305f8d3 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -230,5 +230,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t\n \t} else if (state->not_new)\n \t\treturn 0;\n \tcreate_directories(path, state);\n+\tce_mark_checkout(ce);\n \treturn write_entry(ce, path, state, 0);\n }\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92027","messageId":"1222833849-22129-10-git-send-email-pclouds@gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-9-git-send-email-pclouds@gmail.com","subject":"[PATCH 9/9] grep: skip files outside sparse checkout area","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T04:04:09Z","receivedAt":"2008-10-01T04:04: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 Documentation/git-grep.txt |    4 +++-\n builtin-grep.c             |    7 ++++++-\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex fa4d133..ee359c9 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -24,7 +24,9 @@ SYNOPSIS\n DESCRIPTION\n -----------\n Look for specified patterns in the working tree files, blobs\n-registered in the index file, or given tree objects.\n+registered in the index file, or given tree objects. By default\n+it will search in the working tree files. When in sparse checkout\n+mode, it only searches checked-out files.\n \n \n OPTIONS\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 3a51662..d5507d7 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -343,6 +343,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)\n \t\t\tcontinue;\n \t\tif (!pathspec_matches(paths, ce->name))\n \t\t\tcontinue;\n+\t\tif (ce_no_checkout(ce))\n+\t\t\tcontinue;\n \t\tname = ce->name;\n \t\tif (name[0] == '-') {\n \t\t\tint len = ce_namelen(ce);\n@@ -404,8 +406,11 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)\n \t\t\t\tcontinue;\n \t\t\thit |= grep_sha1(opt, ce->sha1, ce->name, 0);\n \t\t}\n-\t\telse\n+\t\telse {\n+\t\t\tif (ce_no_checkout(ce))\n+\t\t\t\tcontinue;\n \t\t\thit |= grep_file(opt, ce->name);\n+\t\t}\n \t\tif (ce_stage(ce)) {\n \t\t\tdo {\n \t\t\t\tnr++;\n-- \n1.6.0.2.488.gf604a\n"},{"id":"92042","messageId":"adf1fd3d0810010320v340230f0jb443bafc1ef05d6f@mail.gmail.com","threadId":"15736","inReplyTo":"1222833849-22129-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2008-10-01T10:20:25Z","receivedAt":"2008-10-01T10:20:25Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"Not in this half but I've seen that you added a --default-sparse flag\nto \"git clone\". I think it is useless as mostly always the one given\nin -sparse-checkout should be used.\n\nThanks\n\nSanti\n\nOn Wed, Oct 1, 2008 at 6:04 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This is the first half of the series, making git ready for sparse\n> checkout. The only difference from the last (first half) sent\n> series is safeguard bitmask fix in 1/9\n>\n> Nguyễn Thái Ngọc Duy (9):\n>  Extend index to save more flags\n>  Introduce CE_NO_CHECKOUT bit\n>  ls-files: add options to support sparse checkout\n>  update-index: refactor mark_valid() in preparation for new options\n>  update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT\n>    bit\n>  ls-files: Add tests for --sparse and friends\n>  Prevent diff machinery from examining worktree outside sparse\n>    checkout\n>  checkout_entry(): CE_NO_CHECKOUT on checked out entries.\n>  grep: skip files outside sparse checkout area\n>\n>  .gitignore                            |    1 +\n>  Documentation/git-checkout.txt        |   34 +++++++++++++++++\n>  Documentation/git-grep.txt            |    4 +-\n>  Documentation/git-ls-files.txt        |   24 +++++++++++-\n>  Documentation/git-update-index.txt    |   13 ++++++\n>  Makefile                              |    2 +-\n>  builtin-grep.c                        |    7 +++-\n>  builtin-ls-files.c                    |   41 ++++++++++++++++++--\n>  builtin-update-index.c                |   40 ++++++++++++-------\n>  cache.h                               |   66 +++++++++++++++++++++++++++++++--\n>  diff-lib.c                            |    5 +-\n>  diff.c                                |    4 +-\n>  entry.c                               |    1 +\n>  read-cache.c                          |   57 ++++++++++++++++++++++------\n>  t/t2104-update-index-no-checkout.sh   |   36 ++++++++++++++++++\n>  t/t3004-ls-files-sparse.sh            |   40 ++++++++++++++++++++\n>  t/t3004/cached.expected               |    5 ++\n>  t/t3004/deleted.expected              |    1 +\n>  t/t3004/everything.expected           |   10 +++++\n>  t/t3004/modified.expected             |    2 +\n>  t/t3004/no-checkout.expected          |    2 +\n>  t/t3004/orphaned-no-checkout.expected |    3 +\n>  t/t3004/orphaned.expected             |    1 +\n>  t/t3004/others.expected               |    2 +\n>  t/t3004/sparse-cached.expected        |    3 +\n>  t/t3004/sparse-everything.expected    |   11 +++++\n>  test-index-version.c                  |   14 +++++++\n>  27 files changed, 385 insertions(+), 44 deletions(-)\n>  create mode 100755 t/t2104-update-index-no-checkout.sh\n>  create mode 100755 t/t3004-ls-files-sparse.sh\n>  create mode 100644 t/t3004/cached.expected\n>  create mode 100644 t/t3004/deleted.expected\n>  create mode 100644 t/t3004/everything.expected\n>  create mode 100644 t/t3004/modified.expected\n>  create mode 100644 t/t3004/no-checkout.expected\n>  create mode 100644 t/t3004/orphaned-no-checkout.expected\n>  create mode 100644 t/t3004/orphaned.expected\n>  create mode 100644 t/t3004/others.expected\n>  create mode 100644 t/t3004/sparse-cached.expected\n>  create mode 100644 t/t3004/sparse-everything.expected\n>  create mode 100644 test-index-version.c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n"},{"id":"92044","messageId":"adf1fd3d0810010425r145396b0t52cf883576da8bbd@mail.gmail.com","threadId":"15736","inReplyTo":"adf1fd3d0810010320v340230f0jb443bafc1ef05d6f@mail.gmail.com","subject":"Re: [PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2008-10-01T11:25:23Z","receivedAt":"2008-10-01T11:25:23Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar <santi@agolina.net> wrote:\n> Not in this half but I've seen that you added a --default-sparse flag\n> to \"git clone\". I think it is useless as mostly always the one given\n> in -sparse-checkout should be used.\n\nTo be more precise, whenever you do \"git clone --sparse-checkout\" you\nwill want it to be the default sparse pattern.\n\nBest regards,\n\nSanti\n"},{"id":"92046","messageId":"fcaeb9bf0810010509m4d2045e4qcc2a887c180a787@mail.gmail.com","threadId":"15736","inReplyTo":"adf1fd3d0810010425r145396b0t52cf883576da8bbd@mail.gmail.com","subject":"Re: [PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-10-01T12:09:04Z","receivedAt":"2008-10-01T12:09:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On 10/1/08, Santi Béjar <santi@agolina.net> wrote:\n> On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar <santi@agolina.net> wrote:\n>  > Not in this half but I've seen that you added a --default-sparse flag\n>  > to \"git clone\". I think it is useless as mostly always the one given\n>  > in -sparse-checkout should be used.\n>\n>\n> To be more precise, whenever you do \"git clone --sparse-checkout\" you\n>  will want it to be the default sparse pattern.\n\nYes, if only the default pattern gets updated properly when you update\nyour checkout area. If \"git clone --sparse-checkout\" makes the default\npattern, then \"git checkout --reset-sparse\" should reset the default\npattern as well. The hard part is how to update default pattern with\n--include-sparse and --exclude-sparse. Also, people can use \"git\nupdate-index\" to update checkout area, which should not touch default\npattern at all. Maybe just throw a warning when default pattern no\nlonger matches the checkout area, then let them decide.\n-- \nDuy\n"},{"id":"92052","messageId":"adf1fd3d0810010738j21a6432foef3758fc7ed1cb1f@mail.gmail.com","threadId":"15736","inReplyTo":"fcaeb9bf0810010509m4d2045e4qcc2a887c180a787@mail.gmail.com","subject":"Re: [PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2008-10-01T14:38:56Z","receivedAt":"2008-10-01T14:38:56Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"On Wed, Oct 1, 2008 at 2:09 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> On 10/1/08, Santi Béjar <santi@agolina.net> wrote:\n>> On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar <santi@agolina.net> wrote:\n>>  > Not in this half but I've seen that you added a --default-sparse flag\n>>  > to \"git clone\". I think it is useless as mostly always the one given\n>>  > in -sparse-checkout should be used.\n>>\n>>\n>> To be more precise, whenever you do \"git clone --sparse-checkout\" you\n>>  will want it to be the default sparse pattern.\n>\n> Yes, if only the default pattern gets updated properly when you update\n> your checkout area.\n\nI don't parse this sentence, but I cannot see the connection between\nthe default sparse pattern for _clone_ and updating it or the working\narea.\n\n> If \"git clone --sparse-checkout\" makes the default\n> pattern, then \"git checkout --reset-sparse\" should reset the default\n> pattern as well.\n\nMakes sense.\n\n> The hard part is how to update default pattern with\n> --include-sparse and --exclude-sparse.\n\nMaybe you could let the core.defaultsparse be defined multiple times.\n\n[core]\n        defaultsparse = Documentation/*\n        defaultsparse = t/*\n        defaultsparse = !t/*svn*\n\nequivalent to\n\n[core]\n        defaultsparse = Documentation/*:t/*:!t/*svn*\n\nbut it moves the complexity to the parsing of the config.\n\n> Also, people can use \"git\n> update-index\" to update checkout area, which should not touch default\n> pattern at all.\n\nI would prefer having a --not-update-sparse-pattern.\n\nThe most common workflow should be the most straightforward. The most\ncommon use-case would be somebody working _only_ in some subdirectory\n(say Documentation), then what s/he normally does is:\n\n1) cloning and default sparse chechout:\n  $ git clone --sparse-checkout=Documentation/* ...\n  or within an existing wd\n  $ git checkout --reset-sparse=Documentation/* ...\n  (maybe just --sparse?)\n\n2) Work normally inside the Documentation directory...\n\n3) Abort a merge with conflicts outside the sparse area:\n  $ git reset --hard ORIG_HEAD)\n  <Now there are files outside the default sparse area>\n  $ git checkout --reset-sparse # with --sparse to set the sparse pattern\n\nIncluding/excluding more paths in the default sparse checkout is not\nsomething you would do normally, but I think it makes sense to add\nthem to the default pattern.\n\nPlease correct me if you think there are other use-cases, or more\nthings in my use-case.\n\n> Maybe just throw a warning when default pattern no\n> longer matches the checkout area, then let them decide.\n\nSure, and inform in the \"git status\" that you are in a sparse checkout.\n\nBest regards,\nSanti\n"},{"id":"92139","messageId":"adf1fd3d0810020414t127b25b0x4b74b9823d93bc20@mail.gmail.com","threadId":"15736","inReplyTo":"48e45071174c3_22373fefab26908c6423@app01.zenbe.com.tmail","subject":"Re: [PATCH/resent 0/9] Sparse checkout (first half)","fromName":"Santi Béjar","fromEmail":"santi@agolina.net","sentAt":"2008-10-02T11:14:01Z","receivedAt":"2008-10-02T11:14:01Z","isPatch":true,"sender":{"key":"santi@agolina.net","avatar":null},"body":"On Thu, Oct 2, 2008 at 6:39 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On 10/01/2008 \"Santi Béjar\" <santi@agolina.net> wrote:\n>>On Wed, Oct 1, 2008 at 2:09 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>> > On 10/1/08, Santi Béjar <santi@agolina.net> wrote:\n>> >> On Wed, Oct 1, 2008 at 12:20 PM, Santi Béjar <santi@agolina.net> wrote:\n>> >>  > Not in this half but I've seen that you added a --default-sparse flag\n>> >>  > to \"git clone\". I think it is useless as mostly always the one given\n>> >>  > in -sparse-checkout should be used.\n>> >>\n>> >>\n>> >> To be more precise, whenever you do \"git clone --sparse-checkout\" you\n>> >>  will want it to be the default sparse pattern.\n>> >\n>> > Yes, if only the default pattern gets updated properly when you update\n>> > your checkout area.\n>>\n>> I don't parse this sentence, but I cannot see the connection between\n>> the default sparse pattern for _clone_ and updating it or the working\n>> area.\n>\n> After clone with sparse \"Documentation/\" you may want to extend the checkout area to \"t/\" also. \"git pull\" after that should allow new files in Documentation/ as well as t/. So the default sparse pattern should be updated to \"Documentation/:t/\"\n\nSure.\n\n[...]\n\n>> > The hard part is how to update default pattern with\n>> > --include-sparse and --exclude-sparse.\n>>\n>> Maybe you could let the core.defaultsparse be defined multiple times.\n>>\n>> [core]\n>>         defaultsparse = Documentation/*\n>>         defaultsparse = t/*\n>>         defaultsparse = !t/*svn*\n>>\n>> equivalent to\n>>\n>> [core]\n>>         defaultsparse = Documentation/*:t/*:!t/*svn*\n>>\n>> but it moves the complexity to the parsing of the config.\n>\n> There are two issues regarding --{in,ex}clude-sparse:\n>  1. we need to save the prefix somehow. \"git checkout --include-sparse='*.c'\" in compat/ and the same command at top-level workdir give different result.\n\nthen, core.defaultsparse=\"compat//*.c\" and core.defaultsparse=\"*.c\", isn't it?\n\n>  If we don't save prefix, we get wrong result with defaultsparse. This one is easy although it may make sparse patterns and .git{ignore,attributes} look different.\n\nPossible bug, currently in your sparse-checkout branch the prefix is not saved:\n\n$ cd git\n$ cd Documentation\n$ git checkout --sparse=RelNotes*\n$ git config -l | grep sparse\ncore.defaultsparse=RelNotes-1.*\n\n>  2. cruft gets accumulated in core.defaultsparse. Consider this (assume we cloned with sparse \"Documentation/\")\n>\n> for i in 1 2 3;do\n> git checkout --include-sparse=\"t/\"\n> git checkout --exclude-sparse=\"t/\" # oops, I did not mean to do that\n> done\n>\n> The default sparse pattern would be \"Documentation/:t/:!t/:t/:!t/:t/:!t/\" while it should be just \"Documentation/\". Another example is\n>\n> git checkout --include-sparse=t/test-lib.sh\n> git checkout --include-sparse=t/\n>\n> The defaultsparse would be \"Documentation/:t/test-lib.sh:t/\" while a better defaultsparse should be \"Documentation/:t/\"\n>\n\nBut these are the steps to reproduce what the user asked for, so I\ndon't see a problem.\n\n> But maybe I just overlook this. Maybe just show the defaultsparse to people and let them clean it with \"--reset-sparse\".\n\nSure.\n\n>\n>> > Also, people can use \"git\n>> > update-index\" to update checkout area, which should not touch default\n>> > pattern at all.\n>>\n>> I would prefer having a --not-update-sparse-pattern.\n>\n> \"git update-index\" does not understand \"sparse patterns\" at all. It operates at file level. But it is plumbling anyway so those who use it should take care of default sparse pattern themselves.\n\nWell, \"git checkout <paths>\" does the trick.\n\nSanti\n"},{"id":"96082","messageId":"20081118113316.GA18610@segfault.peff.net","threadId":"15736","inReplyTo":"1222833849-22129-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-11-18T11:33:16Z","receivedAt":"2008-11-18T11:33:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 01, 2008 at 11:04:05AM +0700, Nguyễn Thái Ngọc Duy wrote:\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\nMind-boggling, but this manages to break on Solaris. Fix is\nbelow.\n\n-- >8 --\nt2104: touch portability fix\n\nSome versions of touch support the syntax:\n\n  touch [MMDDhhmm[yy]] file...\n\nwhich makes the first argument an optional time\nspecification. They can get confused by\n\n  touch 1 2 foo bar\n\nas they assume that '1' is a bogus time specification. This\nis broken, for example, with /bin/touch on Solaris 8.\n\nTo fix it, we'll just reverse the order of arguments so that\nan unambiguous argument is in the slot for the optional time\nspecification.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nNote that this has implications for 'touch \"$FOO\" \"$BAR\"'\nused in scripts if FOO might be entirely numeric. However, a\nquick grep shows we usually touch one file at a time.\n\n t/t2104-update-index-no-checkout.sh |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t2104-update-index-no-checkout.sh b/t/t2104-update-index-no-checkout.sh\nindex be9f913..37a6861 100755\n--- a/t/t2104-update-index-no-checkout.sh\n+++ b/t/t2104-update-index-no-checkout.sh\n@@ -9,7 +9,7 @@ test_description='git update-index no-checkout bits (a.k.a sparse checkout)'\n \n test_expect_success 'setup' '\n \tmkdir sub &&\n-\ttouch 1 2 sub/1 sub/2 &&\n+\ttouch sub/1 sub/2 1 2 &&\n \tgit add 1 2 sub/1 sub/2\n '\n \n-- \n1.6.0.4.984.gbd0b7\n"},{"id":"96091","messageId":"7vk5b0vp19.fsf@gitster.siamese.dyndns.org","threadId":"15736","inReplyTo":"20081118113316.GA18610@segfault.peff.net","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-11-18T18:59:14Z","receivedAt":"2008-11-18T18:59:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Oct 01, 2008 at 11:04:05AM +0700, Nguyễn Thái Ngọc Duy wrote:\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>\n> Mind-boggling, but this manages to break on Solaris. Fix is\n> below.\n\nI tend to avoid \"touch\", not specifically for this reason, but surely this\nis another reason why \">sub/1\" is much better way to create a throw-away\nfile ;-)\n\n> Note that this has implications for 'touch \"$FOO\" \"$BAR\"'\n> used in scripts if FOO might be entirely numeric. However, a\n> quick grep shows we usually touch one file at a time.\n\nYou can always do:\n\n\t: >>\"$FOO\"\n"},{"id":"96120","messageId":"7v4p24tq59.fsf@gitster.siamese.dyndns.org","threadId":"15736","inReplyTo":"7vk5b0vp19.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-11-19T02:18:10Z","receivedAt":"2008-11-19T02:18:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Jeff King <peff@peff.net> writes:\n>\n>> On Wed, Oct 01, 2008 at 11:04:05AM +0700, Nguyễn Thái Ngọc Duy wrote:\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>>\n>> Mind-boggling, but this manages to break on Solaris. Fix is\n>> below.\n>\n> I tend to avoid \"touch\", not specifically for this reason, but surely this\n> is another reason why \">sub/1\" is much better way to create a throw-away\n> file ;-)\n>\n>> Note that this has implications for 'touch \"$FOO\" \"$BAR\"'\n>> used in scripts if FOO might be entirely numeric. However, a\n>> quick grep shows we usually touch one file at a time.\n>\n> You can always do:\n>\n> \t: >>\"$FOO\"\n\nHaving said all that, I wouldn't suggest redoing the patch using >>\nredirection.  But change from \"touch 1 nondigit\" to \"touch nondigit 1\"\nis a bit too subtle to my taste.  Let's write it this way instead:\n\ndiff --git a/t/t2104-update-index-no-checkout.sh b/t/t2104-update-index-no-checkout.sh\nindex be9f913..37a6861 100755\n--- a/t/t2104-update-index-no-checkout.sh\n+++ b/t/t2104-update-index-no-checkout.sh\n@@ -9,7 +9,7 @@ test_description='git update-index no-checkout bits (a.k.a sparse checkout)'\n \n test_expect_success 'setup' '\n \tmkdir sub &&\n-\ttouch 1 2 sub/1 sub/2 &&\n+\ttouch ./1 ./2 sub/1 sub/2 &&\n \tgit add 1 2 sub/1 sub/2\n '\n \n"},{"id":"96149","messageId":"20081119130223.GA27893@segfault.peff.net","threadId":"15736","inReplyTo":"7v4p24tq59.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-11-19T13:02:23Z","receivedAt":"2008-11-19T13:02:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Nov 18, 2008 at 06:18:10PM -0800, Junio C Hamano wrote:\n\n> Having said all that, I wouldn't suggest redoing the patch using >>\n> redirection.  But change from \"touch 1 nondigit\" to \"touch nondigit 1\"\n> is a bit too subtle to my taste.  Let's write it this way instead:\n\nYes, I also dislike the subtlety, but my \"obvious\" idea was something\nlike:\n\n  for i in 1 2 sub/1 sub/2; do\n    touch $i\n  done\n\nwhich just seemed clunky. But:\n\n> -\ttouch 1 2 sub/1 sub/2 &&\n> +\ttouch ./1 ./2 sub/1 sub/2 &&\n\nthis is less clunky, and I have confirmed that it solves the problem. I\njust wasn't clever enough to think of it in the first place. ;)\n\n-Peff\n"},{"id":"96264","messageId":"fcaeb9bf0811200726x1f2956c6k6f2ca16543a0fbc@mail.gmail.com","threadId":"15736","inReplyTo":"20081119130223.GA27893@segfault.peff.net","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-20T15:26:48Z","receivedAt":"2008-11-20T15:26:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On 11/19/08, Jeff King <peff@peff.net> wrote:\n> On Tue, Nov 18, 2008 at 06:18:10PM -0800, Junio C Hamano wrote:\n>\n>  > Having said all that, I wouldn't suggest redoing the patch using >>\n>  > redirection.  But change from \"touch 1 nondigit\" to \"touch nondigit 1\"\n>  > is a bit too subtle to my taste.  Let's write it this way instead:\n>\n>\n> Yes, I also dislike the subtlety, but my \"obvious\" idea was something\n>  like:\n>\n>   for i in 1 2 sub/1 sub/2; do\n>     touch $i\n>   done\n>\n>  which just seemed clunky. But:\n>\n>\n>  > -     touch 1 2 sub/1 sub/2 &&\n>  > +     touch ./1 ./2 sub/1 sub/2 &&\n>\n>\n> this is less clunky, and I have confirmed that it solves the problem. I\n>  just wasn't clever enough to think of it in the first place. ;)\n\nThanks for catching. The last half also has the same problem. Another\nway is maybe just stay away for those numbers, naming the files by\nalphabet. Just wonder if we could have some ways to automatically\ncatch this kind of bug in the future.\n\n-- \nDuy\n"},{"id":"96265","messageId":"20081120153252.GA7374@coredump.intra.peff.net","threadId":"15736","inReplyTo":"fcaeb9bf0811200726x1f2956c6k6f2ca16543a0fbc@mail.gmail.com","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-11-20T15:32:52Z","receivedAt":"2008-11-20T15:32:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 20, 2008 at 10:26:48PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> Thanks for catching. The last half also has the same problem. Another\n\nI'm not sure what you mean by \"the last half also has the same problem\";\nwith the patch I posted (or Junio's patch) the test works fine for me.\n\n> way is maybe just stay away for those numbers, naming the files by\n> alphabet. Just wonder if we could have some ways to automatically\n> catch this kind of bug in the future.\n\nDscho suggested something similar. I would be happy if somebody wrote a\nportability lint that found problems in shell constructs and calling\nconventions of tools. In practice, though, I think it ends up being\nquite hard to catalog all of the quirks of every platform (and\ncertainly, I would never have thought that this would break -- as it\nwas, after it _did_ break I had to sit scratching my head wondering how\nthat piece of code could be wrong).\n\nSo I think a simpler approach makes sense: write tests that make sure\nthe system is doing what you want, and then run those tests periodically\nin the environments that you care about checking. When it breaks, you\nknow there is a problem. :) And that is exactly how this bug was caught.\n\n-Peff\n"},{"id":"96268","messageId":"fcaeb9bf0811200754o69551068h44856ad3971cab48@mail.gmail.com","threadId":"15736","inReplyTo":"20081120153252.GA7374@coredump.intra.peff.net","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-20T15:54:29Z","receivedAt":"2008-11-20T15:54:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On 11/20/08, Jeff King <peff@peff.net> wrote:\n> On Thu, Nov 20, 2008 at 10:26:48PM +0700, Nguyen Thai Ngoc Duy wrote:\n>\n>  > Thanks for catching. The last half also has the same problem. Another\n>\n>\n> I'm not sure what you mean by \"the last half also has the same problem\";\n>  with the patch I posted (or Junio's patch) the test works fine for me.\n\nSorry for the confusion. I meant the second half of the series,\nimplementing UI interface for sparce checkout, which is not in pu yet.\n-- \nDuy\n"},{"id":"96286","messageId":"7vod0aqbz9.fsf@gitster.siamese.dyndns.org","threadId":"15736","inReplyTo":"fcaeb9bf0811200726x1f2956c6k6f2ca16543a0fbc@mail.gmail.com","subject":"Re: [PATCH 5/9] update-index: add --checkout/--no-checkout to update CE_NO_CHECKOUT bit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-11-20T22:16:58Z","receivedAt":"2008-11-20T22:16:58Z","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>> Yes, I also dislike the subtlety, but my \"obvious\" idea was something\n>>  like:\n>>\n>>   for i in 1 2 sub/1 sub/2; do\n>>     touch $i\n>>   done\n>>\n>>  which just seemed clunky. But:\n>>\n>>\n>>  > -     touch 1 2 sub/1 sub/2 &&\n>>  > +     touch ./1 ./2 sub/1 sub/2 &&\n>>\n>>\n>> this is less clunky, and I have confirmed that it solves the problem. I\n>>  just wasn't clever enough to think of it in the first place. ;)\n>\n> Thanks for catching. The last half also has the same problem. Another\n> way is maybe just stay away for those numbers, naming the files by\n> ... Just wonder if we could have some ways to automatically\n> catch this kind of bug in the future.\n\nA tool to do so essentially amounts to coming up with a set of POSIX\ncommand line tools that know and flag all the known bugs broken platform\ntools have, at the same time producing a reasonable end result so that it\ncan continue running and detecting more portability issues in the scripts.\n\nUsing dash as the shell helps to catch use of constructs outside POSIX,\nrunning various GNU tools with POSIXLY_CORRECT=YesPlease may have similar\neffects, but quirks specific to particular platforms like the above is\nfundamentally hard to check.  Running autobuilder farms like Jeff does is\nprobably the best thing we can do.\n"}]}