From 163b95919428cd7d782af91296e0b886683f2daa Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 19 Apr 2007 22:37:19 -0700 Subject: [PATCH] Update 'crlf' attribute semantics. This updates the semantics of 'crlf' so that .gitattributes file can say "this is text, even though it may look funny". Setting the `crlf` attribute on a path is meant to mark the path as a "text" file. 'core.autocrlf' conversion takes place without guessing the content type by inspection. Unsetting the `crlf` attribute on a path is meant to mark the path as a "binary" file. The path never goes through line endings conversion upon checkin/checkout. Unspecified `crlf` attribute tells git to apply the `core.autocrlf` conversion when the file content looks like text. Setting the `crlf` attribut to string value "input" is similar to setting the attribute to `true`, but also forces git to act as if `core.autocrlf` is set to `input` for the path. Signed-off-by: Junio C Hamano --- convert.c | 75 ++++++++++++++++++++------------------------------------- t/t0020-crlf.sh | 74 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 60 deletions(-) diff --git a/convert.c b/convert.c index a5f60c7c6b..da64253a16 100644 --- a/convert.c +++ b/convert.c @@ -10,6 +10,11 @@ * translation when the "auto_crlf" option is set. */ +#define CRLF_GUESS (-1) +#define CRLF_BINARY 0 +#define CRLF_TEXT 1 +#define CRLF_INPUT 2 + struct text_stat { /* CR, LF and CRLF counts */ unsigned cr, lf, crlf; @@ -74,13 +79,13 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } -static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int guess) +static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int action) { char *buffer, *nbuf; unsigned long size, nsize; struct text_stat stats; - if (guess && !auto_crlf) + if ((action == CRLF_BINARY) || (action == CRLF_GUESS && !auto_crlf)) return 0; size = *sizep; @@ -94,7 +99,7 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int if (!stats.cr) return 0; - if (guess) { + if (action == CRLF_GUESS) { /* * We're currently not going to even try to convert stuff * that has bare CR characters. Does anybody do that crazy @@ -119,7 +124,12 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int *bufp = nbuf; *sizep = nsize; - if (guess) { + if (action == CRLF_GUESS) { + /* + * If we guessed, we already know we rejected a file with + * lone CR, and we can strip a CR without looking at what + * follow it. + */ do { unsigned char c = *buffer++; if (c != '\r') @@ -136,24 +146,15 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int return 1; } -static int autocrlf_to_git(const char *path, char **bufp, unsigned long *sizep) -{ - return crlf_to_git(path, bufp, sizep, 1); -} - -static int forcecrlf_to_git(const char *path, char **bufp, unsigned long *sizep) -{ - return crlf_to_git(path, bufp, sizep, 0); -} - -static int crlf_to_working_tree(const char *path, char **bufp, unsigned long *sizep, int guess) +static int crlf_to_worktree(const char *path, char **bufp, unsigned long *sizep, int action) { char *buffer, *nbuf; unsigned long size, nsize; struct text_stat stats; unsigned char last; - if (guess && auto_crlf <= 0) + if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || + (action == CRLF_GUESS && auto_crlf <= 0)) return 0; size = *sizep; @@ -171,7 +172,7 @@ static int crlf_to_working_tree(const char *path, char **bufp, unsigned long *si if (stats.lf == stats.crlf) return 0; - if (guess) { + if (action == CRLF_GUESS) { /* If we have any bare CR characters, we're not going to touch it */ if (stats.cr != stats.crlf) return 0; @@ -200,16 +201,6 @@ static int crlf_to_working_tree(const char *path, char **bufp, unsigned long *si return 1; } -static int autocrlf_to_working_tree(const char *path, char **bufp, unsigned long *sizep) -{ - return crlf_to_working_tree(path, bufp, sizep, 1); -} - -static int forcecrlf_to_working_tree(const char *path, char **bufp, unsigned long *sizep) -{ - return crlf_to_working_tree(path, bufp, sizep, 0); -} - static void setup_crlf_check(struct git_attr_check *check) { static struct git_attr *attr_crlf; @@ -228,38 +219,24 @@ static int git_path_check_crlf(const char *path) if (!git_checkattr(path, 1, &attr_crlf_check)) { const char *value = attr_crlf_check.value; if (ATTR_TRUE(value)) - return 1; + return CRLF_TEXT; else if (ATTR_FALSE(value)) - return 0; + return CRLF_BINARY; else if (ATTR_UNSET(value)) ; - else - die("unknown value %s given to 'crlf' attribute", - (char *)value); + else if (!strcmp(value, "input")) + return CRLF_INPUT; + /* fallthru */ } - return -1; + return CRLF_GUESS; } int convert_to_git(const char *path, char **bufp, unsigned long *sizep) { - switch (git_path_check_crlf(path)) { - case 0: - return 0; - case 1: - return forcecrlf_to_git(path, bufp, sizep); - default: - return autocrlf_to_git(path, bufp, sizep); - } + return crlf_to_git(path, bufp, sizep, git_path_check_crlf(path)); } int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep) { - switch (git_path_check_crlf(path)) { - case 0: - return 0; - case 1: - return forcecrlf_to_working_tree(path, bufp, sizep); - default: - return autocrlf_to_working_tree(path, bufp, sizep); - } + return crlf_to_worktree(path, bufp, sizep, git_path_check_crlf(path)); } diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index cf84f0a1ab..fe1dfd08a0 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -4,6 +4,10 @@ test_description='CRLF conversion' . ./test-lib.sh +q_to_nul () { + tr Q '\0' +} + append_cr () { sed -e 's/$/Q/' | tr Q '\015' } @@ -20,6 +24,7 @@ test_expect_success setup ' for w in Hello world how are you; do echo $w; done >one && mkdir dir && for w in I am very very fine thank you; do echo $w; done >dir/two && + for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three && git add . && git commit -m initial && @@ -27,6 +32,7 @@ test_expect_success setup ' one=`git rev-parse HEAD:one` && dir=`git rev-parse HEAD:dir` && two=`git rev-parse HEAD:dir/two` && + three=`git rev-parse HEAD:three` && for w in Some extra lines here; do echo $w; done >>one && git diff >patch.file && @@ -38,7 +44,7 @@ test_expect_success setup ' test_expect_success 'update with autocrlf=input' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git read-tree --reset -u HEAD && git repo-config core.autocrlf input && @@ -62,7 +68,7 @@ test_expect_success 'update with autocrlf=input' ' test_expect_success 'update with autocrlf=true' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git read-tree --reset -u HEAD && git repo-config core.autocrlf true && @@ -86,7 +92,7 @@ test_expect_success 'update with autocrlf=true' ' test_expect_success 'checkout with autocrlf=true' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf true && git read-tree --reset -u HEAD && @@ -110,7 +116,7 @@ test_expect_success 'checkout with autocrlf=true' ' test_expect_success 'checkout with autocrlf=input' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf input && git read-tree --reset -u HEAD && @@ -136,7 +142,7 @@ test_expect_success 'checkout with autocrlf=input' ' test_expect_success 'apply patch (autocrlf=input)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf input && git read-tree --reset -u HEAD && @@ -149,7 +155,7 @@ test_expect_success 'apply patch (autocrlf=input)' ' test_expect_success 'apply patch --cached (autocrlf=input)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf input && git read-tree --reset -u HEAD && @@ -162,7 +168,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' ' test_expect_success 'apply patch --index (autocrlf=input)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf input && git read-tree --reset -u HEAD && @@ -176,7 +182,7 @@ test_expect_success 'apply patch --index (autocrlf=input)' ' test_expect_success 'apply patch (autocrlf=true)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf true && git read-tree --reset -u HEAD && @@ -189,7 +195,7 @@ test_expect_success 'apply patch (autocrlf=true)' ' test_expect_success 'apply patch --cached (autocrlf=true)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf true && git read-tree --reset -u HEAD && @@ -202,7 +208,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' ' test_expect_success 'apply patch --index (autocrlf=true)' ' - rm -f tmp one dir/two && + rm -f tmp one dir/two three && git repo-config core.autocrlf true && git read-tree --reset -u HEAD && @@ -216,8 +222,8 @@ test_expect_success 'apply patch --index (autocrlf=true)' ' test_expect_success '.gitattributes says two is binary' ' + rm -f tmp one dir/two three && echo "two -crlf" >.gitattributes && - rm -f tmp one dir/two && git repo-config core.autocrlf true && git read-tree --reset -u HEAD && @@ -235,6 +241,52 @@ test_expect_success '.gitattributes says two is binary' ' else echo "Huh?" false + fi && + + if remove_cr three >/dev/null + then + echo "Huh?" + false + else + : happy + fi +' + +test_expect_success '.gitattributes says two is input' ' + + rm -f tmp one dir/two three && + echo "two crlf=input" >.gitattributes && + git read-tree --reset -u HEAD && + + if remove_cr dir/two >/dev/null + then + echo "Huh?" + false + else + : happy + fi +' + +test_expect_success '.gitattributes says two and three are text' ' + + rm -f tmp one dir/two three && + echo "t* crlf" >.gitattributes && + git read-tree --reset -u HEAD && + + if remove_cr dir/two >/dev/null + then + : happy + else + echo "Huh?" + false + fi && + + if remove_cr three >/dev/null + then + : happy + else + echo "Huh?" + false fi ' -- 2.11.4.GIT