Skip to content

Commit

Permalink
Update 'crlf' attribute semantics.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Junio C Hamano committed Apr 20, 2007
1 parent 4392da4 commit 163b959
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 60 deletions.
75 changes: 26 additions & 49 deletions convert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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')
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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));
}
74 changes: 63 additions & 11 deletions t/t0020-crlf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand All @@ -20,13 +24,15 @@ 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 &&
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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -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 &&
Expand All @@ -230,6 +236,52 @@ test_expect_success '.gitattributes says two is binary' '
fi &&
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
: happy
else
Expand Down

0 comments on commit 163b959

Please sign in to comment.