{"thread":{"id":"56457","subject":"Git apply can convert LF file into CRLF when .gitattributes file is a part of a patch","startedAt":"2021-09-08T00:53:55Z","lastAt":"2021-09-11T06:46:34Z","messageCount":2,"participants":["Mirochnik, Oleg V","Torsten B��gershausen"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"434981","messageId":"SJ0PR11MB49429B90AA71A06BD4A3BD15C6D49@SJ0PR11MB4942.namprd11.prod.outlook.com","threadId":"56457","inReplyTo":null,"subject":"Git apply can convert LF file into CRLF when .gitattributes file is a part of a patch","fromName":"Mirochnik, Oleg V","fromEmail":"oleg.v.mirochnik@intel.com","sentAt":"2021-09-08T00:53:42Z","receivedAt":"2021-09-08T00:53:55Z","isPatch":false,"sender":{"key":"oleg.v.mirochnik@intel.com","avatar":null},"body":"Hi,\n\nOriginally the issue was reported in https://github.com/git-for-windows/git/issues/3409\nBut I was asked to report it in the mail list as the issue is not a Windows related.\n\nAs you can see below \"git apply --binary\" converts files into CRLF mode if .gitattributes file modification is included into a patch file.\nIMO behavior should be consistent - \"git apply --binary\" should not convert files into CRLF mode because of unrelated change in .gitattributes file.\n\nSteps:\n$ cat doit\ngit --version --build-options\ngit config --global core.autocrlf true\n\nrm -rf tst\nmkdir tst\ncd tst\nmkdir repo.git\ncd repo.git\ngit init --bare\ncd ..\ngit clone repo.git 1\ncd 1\nmkdir cr lf\necho '* -text' > lf/.gitattributes\nfor d in *; do for b in {1..5}; do echo foo >> $d/foo; done; done\ngit add *\ngit commit -m init\ngit push\ncd ../\n\ngit clone repo.git/ 2\ncd 2\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\nfor a in `find * -type f`; do echo '# dummy' >> $a; done\ngit commit -a -m dummy\necho \"\"\necho +++ After commit and before diff\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\ngit diff --binary HEAD~ -- cr lf     > ../patch.with\ngit diff --binary HEAD~ -- cr lf/foo > ../patch.without\ngit diff --binary HEAD~ --    lf     > ../patch.lf-only\ncd ..\n\necho \"\"\necho +++ Patches\nfor a in patch*; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n\ngit clone repo.git 3\ncd 3\n\necho \"\"\necho +++ patch w/o .gitattributes\ngit reset --hard\necho +++ After reset and before patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\ngit apply --binary ../patch.without\necho +++ After patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n\necho \"\"\necho +++ patch with .gitattributes\ngit reset --hard\necho +++ After reset and before patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\ngit apply --binary ../patch.with\necho +++ After patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n\n\necho \"\"\necho +++ patch no CR\ngit reset --hard\necho +++ After reset and before patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\ngit apply --binary ../patch.lf-only\necho +++ After patch\nfor a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n\ngit config --global core.autocrlf false\n\n$ sh doit\ngit version 2.29.2\ncpu: x86_64\nno commit associated with this build\nsizeof-long: 8\nsizeof-size_t: 8\nshell-path: /bin/sh\nInitialized empty Git repository in /tmp/issue3409/tst/repo.git/\nCloning into '1'...\nwarning: You appear to have cloned an empty repository.\ndone.\nwarning: LF will be replaced by CRLF in cr/foo.\nThe file will have its original line endings in your working directory\n[master (root-commit) fc793f1] init\n 3 files changed, 11 insertions(+)\n create mode 100644 cr/foo\n create mode 100644 lf/.gitattributes\n create mode 100644 lf/foo\nEnumerating objects: 6, done.\nCounting objects: 100% (6/6), done.\nDelta compression using up to 24 threads\nCompressing objects: 100% (3/3), done.\nWriting objects: 100% (6/6), 378 bytes | 378.00 KiB/s, done.\nTotal 6 (delta 0), reused 0 (delta 0), pack-reused 0\nTo /tmp/issue3409/tst/repo.git\n * [new branch]      master -> master\nCloning into '2'...\ndone.\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\nwarning: LF will be replaced by CRLF in cr/foo.\nThe file will have its original line endings in your working directory\n[master 718534d] dummy\n 3 files changed, 3 insertions(+)\n\n+++ After commit and before diff\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n\n+++ Patches\nCR patch.lf-only\nCR patch.with\nCR patch.without\nCloning into '3'...\ndone.\n\n+++ patch w/o .gitattributes\nHEAD is now at fc793f1 init\n+++ After reset and before patch\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n+++ After patch\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n\n+++ patch with .gitattributes\nHEAD is now at fc793f1 init\n+++ After reset and before patch\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n+++ After patch\nCR cr/foo\nCR lf/foo\nCR lf/.gitattributes\n\n+++ patch no CR\nHEAD is now at fc793f1 init\n+++ After reset and before patch\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n+++ After patch\nCR cr/foo\nLF lf/foo\nLF lf/.gitattributes\n"},{"id":"435518","messageId":"20210911064357.r7cpmxkt5w5davzb@tb-raspi4","threadId":"56457","inReplyTo":"SJ0PR11MB49429B90AA71A06BD4A3BD15C6D49@SJ0PR11MB4942.namprd11.prod.outlook.com","subject":"Re: Git apply can convert LF file into CRLF when .gitattributes file is a part of a patch","fromName":"Torsten B��gershausen","fromEmail":"tboegi@web.de","sentAt":"2021-09-11T06:43:57Z","receivedAt":"2021-09-11T06:46:34Z","isPatch":false,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On Wed, Sep 08, 2021 at 12:53:42AM +0000, Mirochnik, Oleg V wrote:\n> Hi,\n>\n> Originally the issue was reported in https://github.com/git-for-windows/git/issues/3409\n> But I was asked to report it in the mail list as the issue is not a Windows related.\n>\n> As you can see below \"git apply --binary\" converts files into CRLF mode if .gitattributes file modification is included into a patch file.\n> IMO behavior should be consistent - \"git apply --binary\" should not convert files into CRLF mode because of unrelated change in .gitattributes file.\n\n\nIt was probablty me who suggested to report this issue here on the mailing list.\nThanks for doing so.\n\nI extracted your test script, run it on a Mac,\nand it seems to give a different result then your run.\n\nJust to quote the las lines:\n+++ patch no CR\nHEAD is now at a47b66d init\n+++ After reset and before patch\nLF cr/foo\nLF lf/foo\nLF lf/.gitattributes\n+++ After patch\nLF cr/foo\nLF lf/foo\nLF lf/.gitattributes\n---------\n\nSeeing this, I am unsure what is going on.\nIt may be an idea to convert the script into a real git test case,\nand send it to the list.\nThere is a command\ngit ls-files --eol\nto let Git report the line endings of files\n\nUsing\n----------------\necho \"this is what I expected\" >expect &&\ngit ls-files --eol >actual &&\ntest_cmp expect actual\n------------------\n\nwould allow everybody to see what is possible wrong.\nAnd somebody may be willing to dig deeper - and if it\na bug, send a patch\n\n\n\n>\n> Steps:\n> $ cat doit\n> git --version --build-options\n> git config --global core.autocrlf true\n>\n> rm -rf tst\n> mkdir tst\n> cd tst\n> mkdir repo.git\n> cd repo.git\n> git init --bare\n> cd ..\n> git clone repo.git 1\n> cd 1\n> mkdir cr lf\n> echo '* -text' > lf/.gitattributes\n> for d in *; do for b in {1..5}; do echo foo >> $d/foo; done; done\n> git add *\n> git commit -m init\n> git push\n> cd ../\n>\n> git clone repo.git/ 2\n> cd 2\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n> for a in `find * -type f`; do echo '# dummy' >> $a; done\n> git commit -a -m dummy\n> echo \"\"\n> echo +++ After commit and before diff\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n> git diff --binary HEAD~ -- cr lf     > ../patch.with\n> git diff --binary HEAD~ -- cr lf/foo > ../patch.without\n> git diff --binary HEAD~ --    lf     > ../patch.lf-only\n> cd ..\n>\n> echo \"\"\n> echo +++ Patches\n> for a in patch*; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n>\n> git clone repo.git 3\n> cd 3\n>\n> echo \"\"\n> echo +++ patch w/o .gitattributes\n> git reset --hard\n> echo +++ After reset and before patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n> git apply --binary ../patch.without\n> echo +++ After patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n>\n> echo \"\"\n> echo +++ patch with .gitattributes\n> git reset --hard\n> echo +++ After reset and before patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n> git apply --binary ../patch.with\n> echo +++ After patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n>\n>\n> echo \"\"\n> echo +++ patch no CR\n> git reset --hard\n> echo +++ After reset and before patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n> git apply --binary ../patch.lf-only\n> echo +++ After patch\n> for a in `find * -type f`; do echo `od -c $a | grep -q '\\\\r' && echo CR || echo LF` $a; done\n>\n> git config --global core.autocrlf false\n>\n> $ sh doit\n> git version 2.29.2\n> cpu: x86_64\n> no commit associated with this build\n> sizeof-long: 8\n> sizeof-size_t: 8\n> shell-path: /bin/sh\n> Initialized empty Git repository in /tmp/issue3409/tst/repo.git/\n> Cloning into '1'...\n> warning: You appear to have cloned an empty repository.\n> done.\n> warning: LF will be replaced by CRLF in cr/foo.\n> The file will have its original line endings in your working directory\n> [master (root-commit) fc793f1] init\n>  3 files changed, 11 insertions(+)\n>  create mode 100644 cr/foo\n>  create mode 100644 lf/.gitattributes\n>  create mode 100644 lf/foo\n> Enumerating objects: 6, done.\n> Counting objects: 100% (6/6), done.\n> Delta compression using up to 24 threads\n> Compressing objects: 100% (3/3), done.\n> Writing objects: 100% (6/6), 378 bytes | 378.00 KiB/s, done.\n> Total 6 (delta 0), reused 0 (delta 0), pack-reused 0\n> To /tmp/issue3409/tst/repo.git\n>  * [new branch]      master -> master\n> Cloning into '2'...\n> done.\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n> warning: LF will be replaced by CRLF in cr/foo.\n> The file will have its original line endings in your working directory\n> [master 718534d] dummy\n>  3 files changed, 3 insertions(+)\n>\n> +++ After commit and before diff\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n>\n> +++ Patches\n> CR patch.lf-only\n> CR patch.with\n> CR patch.without\n> Cloning into '3'...\n> done.\n>\n> +++ patch w/o .gitattributes\n> HEAD is now at fc793f1 init\n> +++ After reset and before patch\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n> +++ After patch\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n>\n> +++ patch with .gitattributes\n> HEAD is now at fc793f1 init\n> +++ After reset and before patch\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n> +++ After patch\n> CR cr/foo\n> CR lf/foo\n> CR lf/.gitattributes\n>\n> +++ patch no CR\n> HEAD is now at fc793f1 init\n> +++ After reset and before patch\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n> +++ After patch\n> CR cr/foo\n> LF lf/foo\n> LF lf/.gitattributes\n"}]}