{"thread":{"id":"23821","subject":"[PATCH v2] Add \"core.eol\" config variable","startedAt":"2010-05-16T19:12:39Z","lastAt":"2010-05-16T19:12:39Z","messageCount":1,"participants":["Eyvind Bernhardsen"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"141780","messageId":"1274037159-85389-1-git-send-email-eyvind.bernhardsen@gmail.com","threadId":"23821","inReplyTo":null,"subject":"[PATCH v2] Add \"core.eol\" config variable","fromName":"Eyvind Bernhardsen","fromEmail":"eyvind.bernhardsen@gmail.com","sentAt":"2010-05-16T19:12:39Z","receivedAt":"2010-05-16T19:12:39Z","isPatch":true,"sender":{"key":"eyvind.bernhardsen@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106762?v=4"},"body":"Introduce a new configuration variable, \"core.eol\", that allows the user\nto set which line endings to use for end-of-line-normalized files in the\nworking directory.  It defaults to \"native\", which means CRLF on Windows\nand LF everywhere else.\n\nFor backwards compatibility, \"core.autocrlf\" will override core.eol if\ncore.eol is left unset.  This means that\n\n[core]\n\tautocrlf = true\n\nwill give CRLFs in the working directory even on platforms with LF as\ntheir native line ending.\n\nIf core.eol is set explicitly (including setting it to \"native\"), it\nwill override core.autocrlf so that\n\n[core]\n        autocrlf = true\n        eol = lf\n\nnormalizes all files that look like text, but does not put CRLFs in the\nworking directory.\n\nSigned-off-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>\n---\n\nQuick fix for the \"core.eol\" patch; I'd forgotten to update the\nsafecrlf test.  Also made git complain if core.autocrlf is \"input\" and\ncore.eol is explicitly set to \"crlf\".\n-- \nEyvind\n\n\n Documentation/config.txt        |    8 ++++\n Documentation/gitattributes.txt |    6 ++-\n Makefile                        |    3 +\n cache.h                         |   13 ++++++\n config.c                        |   16 +++++++\n convert.c                       |   56 +++++++++++++++++---------\n environment.c                   |    1 +\n t/t0026-eol-config.sh           |   83 +++++++++++++++++++++++++++++++++++++++\n 8 files changed, 166 insertions(+), 20 deletions(-)\n create mode 100755 t/t0026-eol-config.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 207351b..7cc15a4 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -207,6 +207,14 @@ core.autocrlf::\n \tthe file's `text` attribute, or if `text` is unspecified,\n \tbased on the file's contents.  See linkgit:gitattributes[5].\n \n+core.eol::\n+\tSets the line ending type to use in the working directory for\n+\tfiles that have the `text` property set.  Alternatives are\n+\t'lf', 'crlf' and 'native', which uses the platform's native\n+\tline ending.  The default value is `native`.  See\n+\tlinkgit:gitattributes[5] for more information on end-of-line\n+\tconversion.\n+\n core.safecrlf::\n \tIf true, makes git check if converting `CRLF` is reversible when\n \tend-of-line conversion is active.  Git will verify if a command\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex f091f08..be1f160 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -207,7 +207,11 @@ attribute to \"auto\" for _all_ files.\n ------------------------\n \n This ensures that all files that git considers to be text will have\n-normalized (LF) line endings in the repository.\n+normalized (LF) line endings in the repository.  The `core.eol`\n+configuration variable controls which line endings git will use for\n+normalized files in your working directory; the default is to use the\n+native line ending for your platform, or CRLF if `core.autocrlf` is\n+set.\n \n NOTE: When `text=auto` normalization is enabled in an existing\n repository, any text files containing CRLFs should be normalized.  If\ndiff --git a/Makefile b/Makefile\nindex 910f471..419532e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -224,6 +224,8 @@ all::\n #\n # Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded\n # dependency rules.\n+#\n+# Define NATIVE_CRLF if your platform uses CRLF for line endings.\n \n GIT-VERSION-FILE: FORCE\n \t@$(SHELL_PATH) ./GIT-VERSION-GEN\n@@ -989,6 +991,7 @@ ifeq ($(uname_S),Windows)\n \tNO_CURL = YesPlease\n \tNO_PYTHON = YesPlease\n \tBLK_SHA1 = YesPlease\n+\tNATIVE_CRLF = YesPlease\n \n \tCC = compat/vcbuild/scripts/clink.pl\n \tAR = compat/vcbuild/scripts/lib.pl\ndiff --git a/cache.h b/cache.h\nindex d1f669e..ac6bfbd 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -568,6 +568,19 @@ enum auto_crlf {\n \n extern enum auto_crlf auto_crlf;\n \n+enum eol {\n+\tEOL_UNSET,\n+\tEOL_CRLF,\n+\tEOL_LF,\n+#ifdef NATIVE_CRLF\n+\tEOL_NATIVE = EOL_CRLF\n+#else\n+\tEOL_NATIVE = EOL_LF\n+#endif\n+};\n+\n+extern enum eol eol;\n+\n enum branch_track {\n \tBRANCH_TRACK_UNSPECIFIED = -1,\n \tBRANCH_TRACK_NEVER = 0,\ndiff --git a/config.c b/config.c\nindex b60a1ff..1479ee6 100644\n--- a/config.c\n+++ b/config.c\n@@ -461,6 +461,8 @@ static int git_default_core_config(const char *var, const char *value)\n \n \tif (!strcmp(var, \"core.autocrlf\")) {\n \t\tif (value && !strcasecmp(value, \"input\")) {\n+\t\t\tif (eol == EOL_CRLF)\n+\t\t\t\treturn error(\"core.autocrlf=input conflicts with core.eol=crlf\");\n \t\t\tauto_crlf = AUTO_CRLF_INPUT;\n \t\t\treturn 0;\n \t\t}\n@@ -477,6 +479,20 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.eol\")) {\n+\t\tif (value && !strcasecmp(value, \"lf\"))\n+\t\t\teol = EOL_LF;\n+\t\telse if (value && !strcasecmp(value, \"crlf\"))\n+\t\t\teol = EOL_CRLF;\n+\t\telse if (value && !strcasecmp(value, \"native\"))\n+\t\t\teol = EOL_NATIVE;\n+\t\telse\n+\t\t\teol = EOL_UNSET;\n+\t\tif (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)\n+\t\t\treturn error(\"core.autocrlf=input conflicts with core.eol=crlf\");\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.notesref\")) {\n \t\tnotes_ref_name = xstrdup(value);\n \t\treturn 0;\ndiff --git a/convert.c b/convert.c\nindex a309e07..9f4ee81 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -20,12 +20,6 @@ enum action {\n \tCRLF_AUTO,\n };\n \n-enum eol {\n-\tEOL_UNSET,\n-\tEOL_LF,\n-\tEOL_CRLF,\n-};\n-\n struct text_stat {\n \t/* NUL, CR, LF and CRLF counts */\n \tunsigned nul, cr, lf, crlf;\n@@ -99,14 +93,40 @@ static int is_binary(unsigned long size, struct text_stat *stats)\n \treturn 0;\n }\n \n+static enum eol get_output_conversion(enum action action) {\n+\tswitch (action) {\n+\tcase CRLF_BINARY:\n+\t\treturn EOL_UNSET;\n+\tcase CRLF_CRLF:\n+\t\treturn EOL_CRLF;\n+\tcase CRLF_INPUT:\n+\t\treturn EOL_LF;\n+\tcase CRLF_GUESS:\n+\t\tif (auto_crlf == AUTO_CRLF_FALSE)\n+\t\t\treturn EOL_UNSET;\n+\t\t/* fall through */\n+\tcase CRLF_TEXT:\n+\tcase CRLF_AUTO:\n+\t\tif (eol == EOL_UNSET) {\n+\t\t\tif (auto_crlf == AUTO_CRLF_FALSE)\n+\t\t\t\treturn EOL_NATIVE;\n+\t\t\telse if (auto_crlf == AUTO_CRLF_TRUE)\n+\t\t\t\treturn EOL_CRLF;\n+\t\t\telse\n+\t\t\t\treturn EOL_LF;\n+\t\t} else {\n+\t\t\treturn eol;\n+\t\t}\n+\t}\n+}\n+\n static void check_safe_crlf(const char *path, enum action action,\n                             struct text_stat *stats, enum safe_crlf checksafe)\n {\n \tif (!checksafe)\n \t\treturn;\n \n-\tif (action == CRLF_INPUT ||\n-\t    (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_INPUT)) {\n+\tif (get_output_conversion(action) == EOL_LF) {\n \t\t/*\n \t\t * CRLFs would not be restored by checkout:\n \t\t * check if we'd remove CRLFs\n@@ -117,8 +137,7 @@ static void check_safe_crlf(const char *path, enum action action,\n \t\t\telse /* i.e. SAFE_CRLF_FAIL */\n \t\t\t\tdie(\"CRLF would be replaced by LF in %s.\", path);\n \t\t}\n-\t} else if (action == CRLF_CRLF ||\n-\t\t   (action == CRLF_GUESS && auto_crlf == AUTO_CRLF_TRUE)) {\n+\t} else if (get_output_conversion(action) == EOL_CRLF) {\n \t\t/*\n \t\t * CRLFs would be added by checkout:\n \t\t * check if we have \"naked\" LFs\n@@ -244,11 +263,10 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,\n \tchar *to_free = NULL;\n \tstruct text_stat stats;\n \n-\tif ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||\n-\t    (action != CRLF_CRLF && auto_crlf != AUTO_CRLF_TRUE))\n+\tif (!len)\n \t\treturn 0;\n \n-\tif (!len)\n+\tif (get_output_conversion(action) != EOL_CRLF)\n \t\treturn 0;\n \n \tgather_stats(src, len, &stats);\n@@ -670,7 +688,7 @@ int convert_to_git(const char *path, const char *src, size_t len,\n {\n \tstruct git_attr_check check[5];\n \tenum action action = CRLF_GUESS;\n-\tenum eol eol = EOL_UNSET;\n+\tenum eol eol_attr = EOL_UNSET;\n \tint ident = 0, ret = 0;\n \tconst char *filter = NULL;\n \n@@ -682,7 +700,7 @@ int convert_to_git(const char *path, const char *src, size_t len,\n \t\t\taction = 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\teol = git_path_check_eol(path, check + 3);\n+\t\teol_attr = git_path_check_eol(path, check + 3);\n \t\tif (drv && drv->clean)\n \t\t\tfilter = drv->clean;\n \t}\n@@ -692,7 +710,7 @@ int convert_to_git(const char *path, const char *src, size_t len,\n \t\tsrc = dst->buf;\n \t\tlen = dst->len;\n \t}\n-\taction = determine_action(action, eol);\n+\taction = determine_action(action, eol_attr);\n \tret |= crlf_to_git(path, src, len, dst, action, checksafe);\n \tif (ret) {\n \t\tsrc = dst->buf;\n@@ -705,7 +723,7 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc\n {\n \tstruct git_attr_check check[5];\n \tenum action action = CRLF_GUESS;\n-\tenum eol eol = EOL_UNSET;\n+\tenum eol eol_attr = EOL_UNSET;\n \tint ident = 0, ret = 0;\n \tconst char *filter = NULL;\n \n@@ -717,7 +735,7 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc\n \t\t\taction = 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\teol = git_path_check_eol(path, check + 3);\n+\t\teol_attr = git_path_check_eol(path, check + 3);\n \t\tif (drv && drv->smudge)\n \t\t\tfilter = drv->smudge;\n \t}\n@@ -727,7 +745,7 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc\n \t\tsrc = dst->buf;\n \t\tlen = dst->len;\n \t}\n-\taction = determine_action(action, eol);\n+\taction = determine_action(action, eol_attr);\n \tret |= crlf_to_worktree(path, src, len, dst, action);\n \tif (ret) {\n \t\tsrc = dst->buf;\ndiff --git a/environment.c b/environment.c\nindex db4a5e9..83d38d3 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -40,6 +40,7 @@ const char *editor_program;\n const char *excludes_file;\n enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;\n int read_replace_refs = 1;\n+enum eol eol = EOL_UNSET;\n enum safe_crlf safe_crlf = SAFE_CRLF_WARN;\n unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\ndiff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh\nnew file mode 100755\nindex 0000000..5b6c297\n--- /dev/null\n+++ b/t/t0026-eol-config.sh\n@@ -0,0 +1,83 @@\n+#!/bin/sh\n+\n+test_description='CRLF conversion'\n+\n+. ./test-lib.sh\n+\n+has_cr() {\n+\ttr '\\015' Q <\"$1\" | grep Q >/dev/null\n+}\n+\n+test_expect_success setup '\n+\n+\tgit config core.autocrlf false &&\n+\n+\techo \"one text\" > .gitattributes\n+\n+\tfor w in Hello world how are you; do echo $w; done >one &&\n+\tfor w in I am very very fine thank you; do echo $w; done >two &&\n+\tgit add . &&\n+\n+\tgit commit -m initial &&\n+\n+\tone=`git rev-parse HEAD:one` &&\n+\ttwo=`git rev-parse HEAD:two` &&\n+\n+\techo happy.\n+'\n+\n+test_expect_success 'eol=lf puts LFs in normalized file' '\n+\n+\trm -f .gitattributes tmp one two &&\n+\tgit config core.eol lf &&\n+\tgit read-tree --reset -u HEAD &&\n+\n+\t! has_cr one &&\n+\t! has_cr two &&\n+\tonediff=`git diff one` &&\n+\ttwodiff=`git diff two` &&\n+\ttest -z \"$onediff\" -a -z \"$twodiff\"\n+'\n+\n+test_expect_success 'eol=crlf puts CRLFs in normalized file' '\n+\n+\trm -f .gitattributes tmp one two &&\n+\tgit config core.eol crlf &&\n+\tgit read-tree --reset -u HEAD &&\n+\n+\thas_cr one &&\n+\t! has_cr two &&\n+\tonediff=`git diff one` &&\n+\ttwodiff=`git diff two` &&\n+\ttest -z \"$onediff\" -a -z \"$twodiff\"\n+'\n+\n+test_expect_success 'eol=lf overrides autocrlf=true' '\n+\n+\trm -f .gitattributes tmp one two &&\n+\tgit config core.eol lf &&\n+\tgit config core.autocrlf true &&\n+\tgit read-tree --reset -u HEAD &&\n+\n+\t! has_cr one &&\n+\t! has_cr two &&\n+\tonediff=`git diff one` &&\n+\ttwodiff=`git diff two` &&\n+\ttest -z \"$onediff\" -a -z \"$twodiff\"\n+'\n+\n+test_expect_success 'autocrlf=true overrides unset eol' '\n+\n+\trm -f .gitattributes tmp one two &&\n+\tgit config --unset-all core.eol &&\n+\tgit config core.autocrlf true &&\n+\tgit read-tree --reset -u HEAD &&\n+\n+\thas_cr one &&\n+\thas_cr two &&\n+\tonediff=`git diff one` &&\n+\ttwodiff=`git diff two` &&\n+\ttest -z \"$onediff\" -a -z \"$twodiff\"\n+'\n+\n+test_done\n-- \n1.7.1.5.gc3ee8\n"}]}