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:
parent
4392da4d5d
commit
163b959194
75
convert.c
75
convert.c
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user