{"thread":{"id":"23763","subject":"[PATCH v3 1/5] sha1_file: Added index_blob().","startedAt":"2010-05-10T09:51:45Z","lastAt":"2010-05-10T09:51:50Z","messageCount":6,"participants":["Henrik Grubbström (Grubba)"],"isPatch":true,"patchVersion":3,"patchTotal":5},"messages":[{"id":"141369","messageId":"cover.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":null,"subject":"[PATCH v3 0/5] Patches to avoid reporting conversion changes.","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:45Z","receivedAt":"2010-05-10T09:51:45Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"This is the third go at having the git index keep track of\nconversion mode changes for files.\n\nChanges since last time:\n\n  o The normalized sha1 is gone and replaced with a single bit\n    (CONV_NORM_NEEDED).\n\n  o The index extension \"NORM\" has been replaced with \"CONV\",\n    since the only data to be saved is the conversion flags,\n    and the format has been changed incompatibly.\n\n  o The cache entry is now smudged on conversion mode change\n    when relevant.\n\nHenrik Grubbström (Grubba) (5):\n  sha1_file: Added index_blob().\n  strbuf: Added strbuf_add_uint32().\n  cache: Keep track of conversion mode changes.\n  cache: Added index extension \"CONV\".\n  t/t0021: Test that conversion changes are detected.\n\n cache.h               |   11 +++++\n convert.c             |   31 +++++++++++++++\n read-cache.c          |  102 ++++++++++++++++++++++++++++++++++++++++++++----\n sha1_file.c           |   19 +++++++++\n strbuf.h              |    4 ++\n t/t0021-conversion.sh |   54 ++++++++++++++++++++++++++\n 6 files changed, 212 insertions(+), 9 deletions(-)\n"},{"id":"141368","messageId":"73348f7cf4878413eaf97d8d9a88fce8be7c24df.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":"cover.1273482409.git.grubba@grubba.org","subject":"[PATCH v3 1/5] sha1_file: Added index_blob().","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:46Z","receivedAt":"2010-05-10T09:51:46Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"When conversion attributes have changed, it is useful\nto be able to easily reconvert an existing blob.\n\nSigned-off-by: Henrik Grubbström <grubba@grubba.org>\n---\nNo changes since v1.\n\n cache.h     |    1 +\n sha1_file.c |   19 +++++++++++++++++++\n 2 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 5eb0573..1fe2d7d 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -494,6 +494,7 @@ extern int ie_match_stat(const struct index_state *, struct cache_entry *, struc\n extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);\n \n extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);\n+extern int index_blob(unsigned char *dst_sha1, const unsigned char *src_sha1, int write_object, const char *path);\n extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);\n extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);\n extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);\ndiff --git a/sha1_file.c b/sha1_file.c\nindex ff65328..c162321 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -2434,6 +2434,25 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,\n \n #define SMALL_FILE_SIZE (32*1024)\n \n+int index_blob(unsigned char *dst_sha1, const unsigned char *src_sha1,\n+\t       int write_object, const char *path)\n+{\n+\tvoid *buf;\n+\tunsigned long buflen = 0;\n+\tint ret;\n+\n+\tmemcpy(dst_sha1, src_sha1, 20);\n+\tbuf = read_object_with_reference(src_sha1, typename(OBJ_BLOB),\n+\t\t\t\t\t &buflen, dst_sha1);\n+\tif (!buf)\n+\t\treturn 0;\n+\n+\tret = index_mem(dst_sha1, buf, buflen, write_object, OBJ_BLOB, path);\n+\tfree(buf);\n+\n+\treturn ret;\n+}\n+\n int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,\n \t     enum object_type type, const char *path)\n {\n-- \n1.7.0.4.369.g81e89\n"},{"id":"141371","messageId":"127d7018774ac06757bcffb2c691b4ab7b866021.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":"cover.1273482409.git.grubba@grubba.org","subject":"[PATCH v3 2/5] strbuf: Added strbuf_add_uint32().","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:47Z","receivedAt":"2010-05-10T09:51:47Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"Added convenience function for adding an unsigned 32bit\ninteger in network byte-order to a strbuf.\n\nSigned-off-by: Henrik Grubbström <grubba@grubba.org>\n---\nNew trivial function.\n\n strbuf.h |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/strbuf.h b/strbuf.h\nindex fac2dbc..52cd71e 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -107,6 +107,10 @@ static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {\n \tstrbuf_grow(sb, sb2->len);\n \tstrbuf_add(sb, sb2->buf, sb2->len);\n }\n+static inline void strbuf_add_uint32(struct strbuf *sb, uint32_t val) {\n+\tval = htonl(val);\n+\tstrbuf_add(sb, &val, sizeof(val));\n+}\n extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);\n \n typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);\n-- \n1.7.0.4.369.g81e89\n"},{"id":"141370","messageId":"864c8c0ba90b462b72071b5afff7e639391f7fe4.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":"cover.1273482409.git.grubba@grubba.org","subject":"[PATCH v3 3/5] cache: Keep track of conversion mode changes.","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:48Z","receivedAt":"2010-05-10T09:51:48Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"The index now keeps track of the conversion mode that was active\nwhen the entry was created. This can be used to detect the most\ncommon cases of when the conversion mode has changed.\n\nSigned-off-by: Henrik Grubbström <grubba@grubba.org>\n---\nNo longer keeps track of the normalized sha1. Instead\nCONV_NORM_NEEDED is set when the conversion mode affects\nthe normalized sha1, and the normalized sha1 (re-)generated\non demand.\n\nThe cache_entry is now smudged when CONV_NORM_NEEDED is set\nor reset, since we don't know the size of the corresponding\nworking tree file in such cases.\n\nRemoved the prefix NORM_ from the conversion mode flags.\n\n cache.h      |   10 ++++++++++\n convert.c    |   31 +++++++++++++++++++++++++++++++\n read-cache.c |   37 ++++++++++++++++++++++++++++++++++---\n 3 files changed, 75 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 1fe2d7d..e5f54f2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -151,10 +151,19 @@ struct cache_entry {\n \tunsigned int ce_size;\n \tunsigned int ce_flags;\n \tunsigned char sha1[20];\n+\tunsigned int ce_conv_flags;\n \tstruct cache_entry *next;\n \tchar name[FLEX_ARRAY]; /* more */\n };\n \n+#define CONV_CRLF_GIT\t\t0x0001\n+#define CONV_CRLF_WT\t\t0x0002\n+#define CONV_CRLF_GUESS\t\t0x0004\n+#define CONV_IDENT\t\t0x0008\n+#define CONV_FILT\t\t0x0010\n+#define CONV_MASK\t\t0x001f\n+#define CONV_NORM_NEEDED\t0x010000\n+\n #define CE_NAMEMASK  (0x0fff)\n #define CE_STAGEMASK (0x3000)\n #define CE_EXTENDED  (0x4000)\n@@ -1014,6 +1023,7 @@ extern void trace_argv_printf(const char **argv, const char *format, ...);\n \n /* convert.c */\n /* returns 1 if *dst was used */\n+extern unsigned int git_conv_flags(const char *path);\n extern int convert_to_git(const char *path, const char *src, size_t len,\n                           struct strbuf *dst, enum safe_crlf checksafe);\n extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);\ndiff --git a/convert.c b/convert.c\nindex 4f8fcb7..9c063c8 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -568,6 +568,37 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)\n \treturn !!ATTR_TRUE(value);\n }\n \n+unsigned int git_conv_flags(const char *path)\n+{\n+\tstruct git_attr_check check[3];\n+\tint crlf = CRLF_GUESS;\n+\tint ident = 0;\n+\tunsigned ret = 0;\n+\tstruct convert_driver *drv = NULL;\n+\n+\tsetup_convert_check(check);\n+\tif (!git_checkattr(path, ARRAY_SIZE(check), check)) {\n+\t\tcrlf = git_path_check_crlf(path, check + 0);\n+\t\tident = git_path_check_ident(path, check + 1);\n+\t\tdrv = git_path_check_convert(path, check + 2);\n+\t}\n+\n+\tif (auto_crlf && (crlf != CRLF_BINARY)) {\n+\t\tret |= CONV_CRLF_GIT;\n+\t\tif (crlf != CRLF_INPUT && auto_crlf > 0)\n+\t\t\tret |= CONV_CRLF_WT;\n+\t\tif (crlf == CRLF_GUESS)\n+\t\t\tret |= CONV_CRLF_GUESS;\n+\t}\n+\tif (ident) {\n+\t\tret |= CONV_IDENT;\n+\t}\n+\tif (drv) {\n+\t\tret |= CONV_FILT;\n+\t}\n+\treturn ret;\n+}\n+\n int convert_to_git(const char *path, const char *src, size_t len,\n                    struct strbuf *dst, enum safe_crlf checksafe)\n {\ndiff --git a/read-cache.c b/read-cache.c\nindex f1f789b..eeda928 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -88,12 +88,38 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)\n static int ce_compare_data(struct cache_entry *ce, struct stat *st)\n {\n \tint match = -1;\n-\tint fd = open(ce->name, O_RDONLY);\n-\n+\tint fd;\n+\tunsigned char norm_sha1[20];\n+\tunsigned int conv_flags = git_conv_flags(ce->name);\n+\tconst unsigned char *cmp_sha1 = ce->sha1;\n+\n+\tif ((conv_flags ^ ce->ce_conv_flags) & CONV_MASK) {\n+\t\tif (ce->ce_conv_flags & CONV_NORM_NEEDED) {\n+\t\t\t/* Smudge the entry since it was only correct\n+\t\t\t * for the old conversion mode. */\n+\t\t\tce->ce_size = 0;\n+\t\t}\n+\t\tce->ce_conv_flags = conv_flags;\n+\t} else\n+\t\tconv_flags = ce->ce_conv_flags & CONV_NORM_NEEDED;\n+\n+\tif (conv_flags) {\n+\t\tindex_blob(norm_sha1, ce->sha1, 0, ce->name);\n+\t\tif (!(conv_flags & CONV_NORM_NEEDED) &&\n+\t\t    hashcmp(norm_sha1, ce->sha1)) {\n+\t\t\tce->ce_conv_flags = conv_flags | CONV_NORM_NEEDED;\n+\t\t\t/* Smudge the entry since we don't know\n+\t\t\t * the correct value. */\n+\t\t\tce->ce_size = 0;\n+\t\t}\n+\t\tcmp_sha1 = norm_sha1;\n+\t}\n+\t\n+\tfd = open(ce->name, O_RDONLY);\n \tif (fd >= 0) {\n \t\tunsigned char sha1[20];\n \t\tif (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))\n-\t\t\tmatch = hashcmp(sha1, ce->sha1);\n+\t\t\tmatch = hashcmp(sha1, cmp_sha1);\n \t\t/* index_fd() closed the file descriptor already */\n \t}\n \treturn match;\n@@ -227,6 +253,11 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)\n \t\tchanged |= INODE_CHANGED;\n #endif\n \n+\t/* ce_size can not be trusted if the conversion mode has changed. */\n+\tif ((ce->ce_mode & S_IFMT) == S_IFREG &&\n+\t    ((ce->ce_conv_flags ^ git_conv_flags(ce->name)) & CONV_MASK))\n+\t\treturn changed;\n+\n \tif (ce->ce_size != (unsigned int) st->st_size)\n \t\tchanged |= DATA_CHANGED;\n \n-- \n1.7.0.4.369.g81e89\n"},{"id":"141372","messageId":"7e9487a1e7fcfaebc4f50d5866bc065baf5494f7.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":"cover.1273482409.git.grubba@grubba.org","subject":"[PATCH v3 4/5] cache: Added index extension \"CONV\".","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:49Z","receivedAt":"2010-05-10T09:51:49Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"The index can now store and retrieve the ce_conv_flags data.\n\nSigned-off-by: Henrik Grubbström <grubba@grubba.org>\n---\nThe extension name has been changed from \"NORM\" to \"CONV\",\nsince only conversion flags are stored.\n\n read-cache.c |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 files changed, 59 insertions(+), 6 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex eeda928..630e001 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -27,6 +27,7 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall\n #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )\n #define CACHE_EXT_TREE 0x54524545\t/* \"TREE\" */\n #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* \"REUC\" */\n+#define CACHE_EXT_CONV 0x434f4e56\t/* \"CONV\" */\n \n struct index_state the_index;\n \n@@ -1209,6 +1210,34 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)\n \treturn 0;\n }\n \n+/* The on disk format is the default conversion flags followed\n+ * by alternating cache entry numbers and corresponding flags.\n+ */\n+static int conv_read(struct cache_entry **cache, unsigned int entries,\n+\t\t     const unsigned int *data, unsigned long sz)\n+{\n+\tunsigned int entry_no;\n+\tunsigned int default_conv_flags;\n+\tif (sz < sizeof(*data))\n+\t\treturn 0;\n+\tdefault_conv_flags = ntohl(*data);\n+\tdata++;\n+\tsz -= sizeof(*data);\n+\tif (default_conv_flags) {\n+\t\tfor (entry_no = 0; entry_no < entries; entry_no++)\n+\t\t\tcache[entry_no]->ce_conv_flags = default_conv_flags;\n+\t}\n+\twhile (sz >= 2*sizeof(*data)) {\n+\t\tentry_no = ntohl(*data);\n+\t\tdata++;\n+\t\tif (entry_no >= entries) break;\n+\t\tcache[entry_no]->ce_conv_flags = ntohl(*data);\n+\t\tdata++;\n+\t\tsz -= 2*sizeof(*data);\n+\t}\n+\treturn 0;\n+}\n+\n static int read_index_extension(struct index_state *istate,\n \t\t\t\tconst char *ext, void *data, unsigned long sz)\n {\n@@ -1219,6 +1248,9 @@ static int read_index_extension(struct index_state *istate,\n \tcase CACHE_EXT_RESOLVE_UNDO:\n \t\tistate->resolve_undo = resolve_undo_read(data, sz);\n \t\tbreak;\n+\tcase CACHE_EXT_CONV:\n+\t\treturn conv_read(istate->cache, istate->cache_nr, data, sz);\n+\t\tbreak;\n \tdefault:\n \t\tif (*ext < 'A' || 'Z' < *ext)\n \t\t\treturn error(\"index uses %.4s extension, which we do not understand\",\n@@ -1542,6 +1574,15 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)\n \t}\n }\n \n+static void conv_write(struct strbuf *sb, const struct cache_entry *ce,\n+\t\t       int entry_no)\n+{\n+\tunsigned int entry[2];\n+\tentry[0] = htonl(entry_no);\n+\tentry[1] = htonl(ce->ce_conv_flags);\n+\tstrbuf_add(sb, &entry, sizeof(entry));\n+}\n+\n static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)\n {\n \tint size = ondisk_ce_size(ce);\n@@ -1577,10 +1618,12 @@ int write_index(struct index_state *istate, int newfd)\n {\n \tgit_SHA_CTX c;\n \tstruct cache_header hdr;\n-\tint i, err, removed, extended;\n+\tint i, j, err, removed, extended;\n \tstruct cache_entry **cache = istate->cache;\n \tint entries = istate->cache_nr;\n \tstruct stat st;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tunsigned int default_conv_flags;\n \n \tfor (i = removed = extended = 0; i < entries; i++) {\n \t\tif (cache[i]->ce_flags & CE_REMOVE)\n@@ -1603,7 +1646,10 @@ int write_index(struct index_state *istate, int newfd)\n \tif (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)\n \t\treturn -1;\n \n-\tfor (i = 0; i < entries; i++) {\n+\tdefault_conv_flags = git_conv_flags(\"\");\n+\tstrbuf_add_uint32(&sb, default_conv_flags);\n+\n+\tfor (i = j = 0; i < entries; i++) {\n \t\tstruct cache_entry *ce = cache[i];\n \t\tif (ce->ce_flags & CE_REMOVE)\n \t\t\tcontinue;\n@@ -1611,12 +1657,21 @@ int write_index(struct index_state *istate, int newfd)\n \t\t\tce_smudge_racily_clean_entry(ce);\n \t\tif (ce_write_entry(&c, newfd, ce) < 0)\n \t\t\treturn -1;\n+\t\tif (ce->ce_conv_flags != default_conv_flags)\n+\t\t\tconv_write(&sb, ce, j);\n+\t\tj++;\n \t}\n \n \t/* Write extension data here */\n+\tif (default_conv_flags || sb.len > sizeof(default_conv_flags)) {\n+\t\terr = write_index_ext_header(&c, newfd, CACHE_EXT_CONV, sb.len) < 0\n+\t\t\t|| ce_write(&c, newfd, sb.buf, sb.len) < 0;\n+\t\tstrbuf_release(&sb);\n+\t\tif (err)\n+\t\t\treturn -1;\n+\t} else\n+\t\tstrbuf_release(&sb);\n \tif (istate->cache_tree) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n \t\tcache_tree_write(&sb, istate->cache_tree);\n \t\terr = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0\n \t\t\t|| ce_write(&c, newfd, sb.buf, sb.len) < 0;\n@@ -1625,8 +1680,6 @@ int write_index(struct index_state *istate, int newfd)\n \t\t\treturn -1;\n \t}\n \tif (istate->resolve_undo) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n \t\tresolve_undo_write(&sb, istate->resolve_undo);\n \t\terr = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,\n \t\t\t\t\t     sb.len) < 0\n-- \n1.7.0.4.369.g81e89\n"},{"id":"141373","messageId":"d994f040fede700200cbc450a67be2a425efb5b4.1273482409.git.grubba@grubba.org","threadId":"23763","inReplyTo":"cover.1273482409.git.grubba@grubba.org","subject":"[PATCH v3 5/5] t/t0021: Test that conversion changes are detected.","fromName":"Henrik Grubbström (Grubba)","fromEmail":"grubba@grubba.org","sentAt":"2010-05-10T09:51:50Z","receivedAt":"2010-05-10T09:51:50Z","isPatch":true,"sender":{"key":"grubba@grubba.org","avatar":"https://avatars.githubusercontent.com/u/1169458?v=4"},"body":"Signed-off-by Henrik Grubbström <grubba@grubba.org>\n---\nA subshell has been added to the CRLF conversion mode\ntests to avoid the changed cwd leaking.\n\n t/t0021-conversion.sh |   54 +++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 54 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex 6cb8d60..6d41ee7 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -89,4 +89,58 @@ test_expect_success expanded_in_repo '\n \tcmp expanded-keywords expected-output\n '\n \n+# Check that files that have had their canonical representation\n+# changed since being checked in aren't reported as modified\n+# directly after being checked out.\n+test_expect_success keywords_not_modified '\n+\t{\n+\t\techo \"File with foreign keywords\"\n+\t\techo \"\\$Id\\$\"\n+\t\techo \"\\$Id: NoTerminatingSymbol\"\n+\t\techo \"\\$Id: Foreign Commit With Spaces \\$\"\n+\t\techo \"\\$Id: GitCommitId \\$\"\n+\t\techo \"\\$Id: NoTerminatingSymbolAtEOF\"\n+\t} > expanded-keywords2 &&\n+\n+\tgit add expanded-keywords2 &&\n+\tgit commit -m \"File with keywords expanded\" &&\n+\n+\techo \"expanded-keywords2 ident\" >> .gitattributes &&\n+\n+\trm -f expanded-keywords2 &&\n+\tgit checkout -- expanded-keywords2 &&\n+\n+\ttest \"x`git status --porcelain -- expanded-keywords2`\" = x\n+'\n+\n+# Test detection of CRLF conversion changes CRLF ==> LF.\n+test_expect_success crlf_conversion_change_crlf_to_lf '\n+(\n+\t# step 0. a blob with CRLF\n+\tgit init one && cd one &&\n+\techo -e \"a quick brown fox\\015\" >kuzu &&\n+\tgit add kuzu && git commit -m kuzu &&\n+\t# step 1. you want CRLF in work area, LF in repository\n+\tgit config core.autocrlf true &&\n+\t# step 2. user edit and revert.\n+\ttouch kuzu &&\n+\tgit update-index --refresh\n+)\n+'\n+\n+# Test detection of CRLF conversion changes LF ==> CRLF.\n+test_expect_success crlf_conversion_change_lf_to_crlf '\n+(\n+\t# step 0 & 1. a project with LF ending\n+\tgit init two && cd two &&\n+\techo a quick brown fox >kuzu &&\n+\tgit add kuzu && git commit -m kuzu &&\n+\t# step 2. you want CRLF in your work area\n+\techo -e \"a quick brown fox\\015\" >kuzu &&\n+\tgit config core.autocrlf true &&\n+\t# step 3. oops, refresh\n+\tgit update-index --refresh\n+)\n+'\n+\n test_done\n-- \n1.7.0.4.369.g81e89\n"}]}