convert: unify the "auto" handling of CRLF

Before this change, $ echo "* text=auto" >.gitattributes $ echo "* eol=crlf" >>.gitattributes would have the same effect as $ echo "* text" >.gitattributes $ git config core.eol crlf Since the 'eol' attribute had higher priority than 'text=auto', this may corrupt binary files and is not what most users expect to happen. Make the 'eol' attribute to obey 'text=auto' and now $ echo "* text=auto" >.gitattributes $ echo "* eol=crlf" >>.gitattributes behaves the same as $ echo "* text=auto" >.gitattributes $ git config core.eol crlf In other words, $ echo "* text=auto eol=crlf" >.gitattributes has the same effect as $ git config core.autocrlf true and $ echo "* text=auto eol=lf" >.gitattributes has the same effect as $ git config core.autocrlf input Signed-off-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Torsten Bögershausen committed Jun 28, 2016 at 10:01 UTC 6523728499e77afaed0008875b19b308682c3f88
7 files changed +69 -59
Documentation/config.txt
+5 -7
@@ -389,13 +389,11 @@ file with mixed line endings would be reported by the `core.safecrlf`
389 mechanism.
390
391 core.autocrlf::
392 - Setting this variable to "true" is almost the same as setting
393 - the `text` attribute to "auto" on all files except that text
394 - files are not guaranteed to be normalized: files that contain
395 - `CRLF` in the repository will not be touched. Use this
396 - setting if you want to have `CRLF` line endings in your
397 - working directory even though the repository does not have
398 - normalized line endings. This variable can be set to 'input',
392 + Setting this variable to "true" is the same as setting
393 + the `text` attribute to "auto" on all files and core.eol to "crlf".
394 + Set to true if you want to have `CRLF` line endings in your
395 + working directory and the repository has LF line endings.
396 + This variable can be set to 'input',
397 in which case no output conversion is performed.
398
399 core.symlinks::
Documentation/gitattributes.txt
+9 -6
@@ -115,6 +115,7 @@ text file is normalized, its line endings are converted to LF in the
115 repository. To control what line ending style is used in the working
116 directory, use the `eol` attribute for a single file and the
117 `core.eol` configuration variable for all text files.
118 +Note that `core.autocrlf` overrides `core.eol`
119
120 Set::
121
@@ -130,8 +131,9 @@ Unset::
131 Set to string value "auto"::
132
133 When `text` is set to "auto", the path is marked for automatic
133 - end-of-line normalization. If Git decides that the content is
134 - text, its line endings are normalized to LF on checkin.
134 + end-of-line conversion. If Git decides that the content is
135 + text, its line endings are converted to LF on checkin.
136 + When the file has been commited with CRLF, no conversion is done.
137
138 Unspecified::
139
@@ -146,7 +148,7 @@ unspecified.
148 ^^^^^
149
150 This attribute sets a specific line-ending style to be used in the
149 -working directory. It enables end-of-line normalization without any
151 +working directory. It enables end-of-line conversion without any
152 content checks, effectively setting the `text` attribute.
153
154 Set to string value "crlf"::
@@ -186,9 +188,10 @@ the working directory, and prevent .jpg files from being normalized
188 regardless of their content.
189
190 ------------------------
191 +* text=auto
192 *.txt text
190 -*.vcproj eol=crlf
191 -*.sh eol=lf
193 +*.vcproj text eol=crlf
194 +*.sh text eol=lf
195 *.jpg -text
196 ------------------------
197
@@ -198,7 +201,7 @@ normalization in Git.
201
202 If you simply want to have CRLF line endings in your working directory
203 regardless of the repository you are working with, you can set the
201 -config variable "core.autocrlf" without changing any attributes.
204 +config variable "core.autocrlf" without using any attributes.
205
206 ------------------------
207 [core]
convert.c
+22 -20
@@ -176,7 +176,9 @@ static enum eol output_eol(enum crlf_action crlf_action)
176 return EOL_LF;
177 case CRLF_UNDEFINED:
178 case CRLF_AUTO_CRLF:
179 + return EOL_CRLF;
180 case CRLF_AUTO_INPUT:
181 + return EOL_LF;
182 case CRLF_TEXT:
183 case CRLF_AUTO:
184 /* fall through */
@@ -254,17 +256,15 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
256 if (crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
257 if (convert_is_binary(len, &stats))
258 return 0;
257 -
258 - if (crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
259 - /*
260 - * If the file in the index has any CR in it, do not convert.
261 - * This is the new safer autocrlf handling.
262 - */
263 - if (has_cr_in_index(path))
264 - return 0;
265 - }
259 + /*
260 + * If the file in the index has any CR in it, do not convert.
261 + * This is the new safer autocrlf handling.
262 + */
263 + if (checksafe == SAFE_CRLF_RENORMALIZE)
264 + checksafe = SAFE_CRLF_FALSE;
265 + else if (has_cr_in_index(path))
266 + return 0;
267 }
267 -
268 check_safe_crlf(path, crlf_action, &stats, checksafe);
269
270 /* Optimization: No CRLF? Nothing to convert, regardless. */
@@ -320,12 +320,10 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
320 return 0;
321
322 if (crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
323 - if (crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
324 - /* If we have any CR or CRLF line endings, we do not touch it */
325 - /* This is the new safer autocrlf-handling */
326 - if (stats.lonecr || stats.crlf )
327 - return 0;
328 - }
323 + /* If we have any CR or CRLF line endings, we do not touch it */
324 + /* This is the new safer autocrlf-handling */
325 + if (stats.lonecr || stats.crlf )
326 + return 0;
327
328 if (convert_is_binary(len, &stats))
329 return 0;
@@ -786,7 +784,11 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
784 ca->drv = git_path_check_convert(ccheck + 2);
785 if (ca->crlf_action != CRLF_BINARY) {
786 enum eol eol_attr = git_path_check_eol(ccheck + 3);
789 - if (eol_attr == EOL_LF)
787 + if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF)
788 + ca->crlf_action = CRLF_AUTO_INPUT;
789 + else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF)
790 + ca->crlf_action = CRLF_AUTO_CRLF;
791 + else if (eol_attr == EOL_LF)
792 ca->crlf_action = CRLF_TEXT_INPUT;
793 else if (eol_attr == EOL_CRLF)
794 ca->crlf_action = CRLF_TEXT_CRLF;
@@ -845,9 +847,9 @@ const char *get_convert_attr_ascii(const char *path)
847 case CRLF_AUTO:
848 return "text=auto";
849 case CRLF_AUTO_CRLF:
848 - return "text=auto eol=crlf"; /* This is not supported yet */
850 + return "text=auto eol=crlf";
851 case CRLF_AUTO_INPUT:
850 - return "text=auto eol=lf"; /* This is not supported yet */
852 + return "text=auto eol=lf";
853 }
854 return "";
855 }
@@ -949,7 +951,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
951 src = dst->buf;
952 len = dst->len;
953 }
952 - return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_FALSE);
954 + return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_RENORMALIZE);
955 }
956
957 /*****************************************************************
convert.h
+2 -1
@@ -7,7 +7,8 @@
7 enum safe_crlf {
8 SAFE_CRLF_FALSE = 0,
9 SAFE_CRLF_FAIL = 1,
10 - SAFE_CRLF_WARN = 2
10 + SAFE_CRLF_WARN = 2,
11 + SAFE_CRLF_RENORMALIZE = 3
12 };
13
14 extern enum safe_crlf safe_crlf;
t/t0025-crlf-auto.sh
+2 -2
@@ -114,7 +114,7 @@ test_expect_success 'autocrlf=true does not normalize CRLF files' '
114 test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
115 '
116
117 -test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
117 +test_expect_success 'text=auto, autocrlf=true does not normalize CRLF files' '
118
119 rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
120 git config core.autocrlf true &&
@@ -126,7 +126,7 @@ test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
126 LFonlydiff=$(git diff LFonly) &&
127 CRLFonlydiff=$(git diff CRLFonly) &&
128 LFwithNULdiff=$(git diff LFwithNUL) &&
129 - test -z "$LFonlydiff" -a -n "$CRLFonlydiff" -a -z "$LFwithNULdiff"
129 + test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
130 '
131
132 test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
t/t0027-auto-crlf.sh
+14 -15
@@ -175,8 +175,8 @@ attr_ascii () {
175 text,lf) echo "text eol=lf" ;;
176 text,crlf) echo "text eol=crlf" ;;
177 auto,) echo "text=auto" ;;
178 - auto,lf) echo "text eol=lf" ;;
179 - auto,crlf) echo "text eol=crlf" ;;
178 + auto,lf) echo "text=auto eol=lf" ;;
179 + auto,crlf) echo "text=auto eol=crlf" ;;
180 lf,) echo "text eol=lf" ;;
181 crlf,) echo "text eol=crlf" ;;
182 ,) echo "" ;;
@@ -397,10 +397,9 @@ commit_chk_wrnNNO "" "" false "" "" "" ""
397 commit_chk_wrnNNO "" "" true LF_CRLF "" "" "" ""
398 commit_chk_wrnNNO "" "" input "" "" "" "" ""
399
400 -commit_chk_wrnNNO "auto" "" false "$WILC" "$WICL" "$WAMIX" "" ""
401 -commit_chk_wrnNNO "auto" "" true LF_CRLF "" LF_CRLF "" ""
402 -commit_chk_wrnNNO "auto" "" input "" CRLF_LF CRLF_LF "" ""
403 -
400 +commit_chk_wrnNNO "auto" "" false "$WILC" "" "" "" ""
401 +commit_chk_wrnNNO "auto" "" true LF_CRLF "" "" "" ""
402 +commit_chk_wrnNNO "auto" "" input "" "" "" "" ""
403 for crlf in true false input
404 do
405 commit_chk_wrnNNO -text "" $crlf "" "" "" "" ""
@@ -408,8 +407,8 @@ do
407 commit_chk_wrnNNO -text crlf $crlf "" "" "" "" ""
408 commit_chk_wrnNNO "" lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
409 commit_chk_wrnNNO "" crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
411 - commit_chk_wrnNNO auto lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
412 - commit_chk_wrnNNO auto crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
410 + commit_chk_wrnNNO auto lf $crlf "" "" "" "" ""
411 + commit_chk_wrnNNO auto crlf $crlf LF_CRLF "" "" "" ""
412 commit_chk_wrnNNO text lf $crlf "" CRLF_LF CRLF_LF "" CRLF_LF
413 commit_chk_wrnNNO text crlf $crlf LF_CRLF "" LF_CRLF LF_CRLF ""
414 done
@@ -454,9 +453,9 @@ do
453 check_in_repo_NNO -text "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
454 check_in_repo_NNO -text lf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
455 check_in_repo_NNO -text crlf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
457 - check_in_repo_NNO auto "" $crlf LF LF LF LF_mix_CR CRLF_nul
458 - check_in_repo_NNO auto lf $crlf LF LF LF LF_mix_CR LF_nul
459 - check_in_repo_NNO auto crlf $crlf LF LF LF LF_mix_CR LF_nul
456 + check_in_repo_NNO auto "" $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
457 + check_in_repo_NNO auto lf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
458 + check_in_repo_NNO auto crlf $crlf LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
459 check_in_repo_NNO text "" $crlf LF LF LF LF_mix_CR LF_nul
460 check_in_repo_NNO text lf $crlf LF LF LF LF_mix_CR LF_nul
461 check_in_repo_NNO text crlf $crlf LF LF LF LF_mix_CR LF_nul
@@ -509,7 +508,7 @@ do
508 checkout_files text "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
509 # currently the same as text, eol=XXX
510 checkout_files auto "$id" "lf" "$crlf" "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
512 - checkout_files auto "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
511 + checkout_files auto "$id" "crlf" "$crlf" "$ceol" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
512 done
513
514 # core.autocrlf false, different core.eol
@@ -517,7 +516,7 @@ do
516 # core.autocrlf true
517 checkout_files "" "$id" "" true "$ceol" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
518 # text: core.autocrlf = true overrides core.eol
520 - checkout_files auto "$id" "" true "$ceol" CRLF CRLF CRLF LF_mix_CR LF_nul
519 + checkout_files auto "$id" "" true "$ceol" CRLF CRLF CRLF_mix_LF LF_mix_CR LF_nul
520 checkout_files text "$id" "" true "$ceol" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
521 # text: core.autocrlf = input overrides core.eol
522 checkout_files text "$id" "" input "$ceol" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
@@ -531,8 +530,8 @@ do
530 checkout_files text "$id" "" false "" $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
531 checkout_files text "$id" "" false native $NL CRLF $MIX_CRLF_LF $MIX_LF_CR $LFNUL
532 # auto: core.autocrlf=false and core.eol unset(or native) uses native eol
534 - checkout_files auto "$id" "" false "" $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
535 - checkout_files auto "$id" "" false native $NL CRLF $MIX_CRLF_LF LF_mix_CR LF_nul
533 + checkout_files auto "$id" "" false "" $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
534 + checkout_files auto "$id" "" false native $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
535 done
536
537 # Should be the last test case: remove some files from the worktree
t/t6038-merge-text-auto.sh
+15 -8
@@ -16,6 +16,13 @@ test_description='CRLF merge conflict across text=auto change
16
17 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
18
19 +compare_files () {
20 + tr '\015\000' QN <"$1" >"$1".expect &&
21 + tr '\015\000' QN <"$2" >"$2".actual &&
22 + test_cmp "$1".expect "$2".actual &&
23 + rm "$1".expect "$2".actual
24 +}
25 +
26 test_expect_success setup '
27 git config core.autocrlf false &&
28
@@ -30,7 +37,7 @@ test_expect_success setup '
37 git branch side &&
38
39 echo "* text=auto" >.gitattributes &&
33 - touch file &&
40 + echo first line >file &&
41 git add .gitattributes file &&
42 test_tick &&
43 git commit -m "normalize file" &&
@@ -81,7 +88,7 @@ test_expect_success 'Merge after setting text=auto' '
88 rm -f .gitattributes &&
89 git reset --hard a &&
90 git merge b &&
84 - test_cmp expected file
91 + compare_files expected file
92 '
93
94 test_expect_success 'Merge addition of text=auto' '
@@ -99,7 +106,7 @@ test_expect_success 'Merge addition of text=auto' '
106 rm -f .gitattributes &&
107 git reset --hard b &&
108 git merge a &&
102 - test_cmp expected file
109 + compare_files expected file
110 '
111
112 test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
@@ -121,7 +128,7 @@ test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
128 git reset --hard a &&
129 test_must_fail git merge b &&
130 fuzz_conflict file >file.fuzzy &&
124 - test_cmp expected file.fuzzy
131 + compare_files expected file.fuzzy
132 '
133
134 test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
@@ -143,7 +150,7 @@ test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
150 git reset --hard b &&
151 test_must_fail git merge a &&
152 fuzz_conflict file >file.fuzzy &&
146 - test_cmp expected file.fuzzy
153 + compare_files expected file.fuzzy
154 '
155
156 test_expect_failure 'checkout -m after setting text=auto' '
@@ -158,7 +165,7 @@ test_expect_failure 'checkout -m after setting text=auto' '
165 git reset --hard initial &&
166 git checkout a -- . &&
167 git checkout -m b &&
161 - test_cmp expected file
168 + compare_files expected file
169 '
170
171 test_expect_failure 'checkout -m addition of text=auto' '
@@ -173,7 +180,7 @@ test_expect_failure 'checkout -m addition of text=auto' '
180 git reset --hard initial &&
181 git checkout b -- . &&
182 git checkout -m a &&
176 - test_cmp expected file
183 + compare_files expected file
184 '
185
186 test_expect_failure 'cherry-pick patch from after text=auto was added' '
@@ -187,7 +194,7 @@ test_expect_failure 'cherry-pick patch from after text=auto was added' '
194 git reset --hard b &&
195 test_must_fail git cherry-pick a >err 2>&1 &&
196 grep "[Nn]othing added" err &&
190 - test_cmp expected file
197 + compare_files expected file
198 '
199
200 test_expect_success 'Test delete/normalize conflict' '