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 <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2007-04-19 22:37:19 -07:00
parent 4392da4d5d
commit 163b959194
2 changed files with 89 additions and 60 deletions

View File

@ -10,6 +10,11 @@
* translation when the "auto_crlf" option is set. * 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 { struct text_stat {
/* CR, LF and CRLF counts */ /* CR, LF and CRLF counts */
unsigned cr, lf, crlf; unsigned cr, lf, crlf;
@ -74,13 +79,13 @@ static int is_binary(unsigned long size, struct text_stat *stats)
return 0; 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; char *buffer, *nbuf;
unsigned long size, nsize; unsigned long size, nsize;
struct text_stat stats; struct text_stat stats;
if (guess && !auto_crlf) if ((action == CRLF_BINARY) || (action == CRLF_GUESS && !auto_crlf))
return 0; return 0;
size = *sizep; size = *sizep;
@ -94,7 +99,7 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int
if (!stats.cr) if (!stats.cr)
return 0; return 0;
if (guess) { if (action == CRLF_GUESS) {
/* /*
* We're currently not going to even try to convert stuff * We're currently not going to even try to convert stuff
* that has bare CR characters. Does anybody do that crazy * 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; *bufp = nbuf;
*sizep = nsize; *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 { do {
unsigned char c = *buffer++; unsigned char c = *buffer++;
if (c != '\r') if (c != '\r')
@ -136,24 +146,15 @@ static int crlf_to_git(const char *path, char **bufp, unsigned long *sizep, int
return 1; return 1;
} }
static int autocrlf_to_git(const char *path, char **bufp, unsigned long *sizep) static int crlf_to_worktree(const char *path, char **bufp, unsigned long *sizep, int action)
{
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)
{ {
char *buffer, *nbuf; char *buffer, *nbuf;
unsigned long size, nsize; unsigned long size, nsize;
struct text_stat stats; struct text_stat stats;
unsigned char last; unsigned char last;
if (guess && auto_crlf <= 0) if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
(action == CRLF_GUESS && auto_crlf <= 0))
return 0; return 0;
size = *sizep; 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) if (stats.lf == stats.crlf)
return 0; return 0;
if (guess) { if (action == CRLF_GUESS) {
/* If we have any bare CR characters, we're not going to touch it */ /* If we have any bare CR characters, we're not going to touch it */
if (stats.cr != stats.crlf) if (stats.cr != stats.crlf)
return 0; return 0;
@ -200,16 +201,6 @@ static int crlf_to_working_tree(const char *path, char **bufp, unsigned long *si
return 1; 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 void setup_crlf_check(struct git_attr_check *check)
{ {
static struct git_attr *attr_crlf; 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)) { if (!git_checkattr(path, 1, &attr_crlf_check)) {
const char *value = attr_crlf_check.value; const char *value = attr_crlf_check.value;
if (ATTR_TRUE(value)) if (ATTR_TRUE(value))
return 1; return CRLF_TEXT;
else if (ATTR_FALSE(value)) else if (ATTR_FALSE(value))
return 0; return CRLF_BINARY;
else if (ATTR_UNSET(value)) else if (ATTR_UNSET(value))
; ;
else else if (!strcmp(value, "input"))
die("unknown value %s given to 'crlf' attribute", return CRLF_INPUT;
(char *)value); /* fallthru */
} }
return -1; return CRLF_GUESS;
} }
int convert_to_git(const char *path, char **bufp, unsigned long *sizep) int convert_to_git(const char *path, char **bufp, unsigned long *sizep)
{ {
switch (git_path_check_crlf(path)) { return crlf_to_git(path, bufp, sizep, 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);
}
} }
int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep) int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep)
{ {
switch (git_path_check_crlf(path)) { return crlf_to_worktree(path, bufp, sizep, 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);
}
} }

View File

@ -4,6 +4,10 @@ test_description='CRLF conversion'
. ./test-lib.sh . ./test-lib.sh
q_to_nul () {
tr Q '\0'
}
append_cr () { append_cr () {
sed -e 's/$/Q/' | tr Q '\015' 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 && for w in Hello world how are you; do echo $w; done >one &&
mkdir dir && mkdir dir &&
for w in I am very very fine thank you; do echo $w; done >dir/two && 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 add . &&
git commit -m initial && git commit -m initial &&
@ -27,6 +32,7 @@ test_expect_success setup '
one=`git rev-parse HEAD:one` && one=`git rev-parse HEAD:one` &&
dir=`git rev-parse HEAD:dir` && dir=`git rev-parse HEAD:dir` &&
two=`git rev-parse HEAD:dir/two` && 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 && for w in Some extra lines here; do echo $w; done >>one &&
git diff >patch.file && git diff >patch.file &&
@ -38,7 +44,7 @@ test_expect_success setup '
test_expect_success 'update with autocrlf=input' ' 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 read-tree --reset -u HEAD &&
git repo-config core.autocrlf input && git repo-config core.autocrlf input &&
@ -62,7 +68,7 @@ test_expect_success 'update with autocrlf=input' '
test_expect_success 'update with autocrlf=true' ' 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 read-tree --reset -u HEAD &&
git repo-config core.autocrlf true && git repo-config core.autocrlf true &&
@ -86,7 +92,7 @@ test_expect_success 'update with autocrlf=true' '
test_expect_success 'checkout 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 repo-config core.autocrlf true &&
git read-tree --reset -u HEAD && git read-tree --reset -u HEAD &&
@ -110,7 +116,7 @@ test_expect_success 'checkout with autocrlf=true' '
test_expect_success 'checkout with autocrlf=input' ' 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 repo-config core.autocrlf input &&
git read-tree --reset -u HEAD && git read-tree --reset -u HEAD &&
@ -136,7 +142,7 @@ test_expect_success 'checkout with autocrlf=input' '
test_expect_success 'apply patch (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 repo-config core.autocrlf input &&
git read-tree --reset -u HEAD && 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)' ' 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 repo-config core.autocrlf input &&
git read-tree --reset -u HEAD && 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)' ' 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 repo-config core.autocrlf input &&
git read-tree --reset -u HEAD && 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)' ' 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 repo-config core.autocrlf true &&
git read-tree --reset -u HEAD && 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)' ' 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 repo-config core.autocrlf true &&
git read-tree --reset -u HEAD && 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)' ' 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 repo-config core.autocrlf true &&
git read-tree --reset -u HEAD && 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' ' test_expect_success '.gitattributes says two is binary' '
rm -f tmp one dir/two three &&
echo "two -crlf" >.gitattributes && echo "two -crlf" >.gitattributes &&
rm -f tmp one dir/two &&
git repo-config core.autocrlf true && git repo-config core.autocrlf true &&
git read-tree --reset -u HEAD && git read-tree --reset -u HEAD &&
@ -230,6 +236,52 @@ test_expect_success '.gitattributes says two is binary' '
fi && fi &&
if remove_cr one >/dev/null if remove_cr one >/dev/null
then
: happy
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 then
: happy : happy
else else