Git - Windows and Linux Line-Endings

How to change line-ending settings

The normal way to control this is with git config

For example

git config --global core.autocrlf true

For details, scroll down in this link to Pro Git to the section named "core.autocrlf"


If you want to know what file this is saved in, you can run the command:

git config --global --edit

and the git global config file should open in a text editor, and you can see where that file was loaded from.

Git - Windows AND linux line-endings

On Windows:

$ git config --global core.autocrlf true

On Linux:

$ git config --global core.autocrlf input

Read more about Dealing with line endings

Git - Use Linux-style Line Endings on Linux and Windows-style Line Endings on Windows

Git by default performs line ending conversions on text files. If you want files to have their line endings converted, you can mark them as text by flagging them in your .gitattributes file:

*.cs text

If you'd like Git to guess about which files are text automatically, then you should write something like this:

* text=auto

Assuming your build agents do not have custom Git configuration that overrides line endings, that should be sufficient for Git to convert line endings to the native ones. If you'd like to be explicit, you can perform the clone with core.eol set to native, like so: git -c core.eol=native clone URL.

However, having said that, it's generally not a good idea in your code to make assumptions about line endings. If your users are using the Windows Subsystem for Linux, they may well be using a Unix Git and use Unix line endings in their working tree, and that shouldn't affect the correctness of your unit tests. Line endings should be set either explicitly to LF or CRLF because of functional reasons (e.g., shell scripts won't work with CRLF endings on any platform) or left to the user's desires based on their development environment.

If you need to explicitly use the platform's native line endings in a test, you should write an explicit string that will be parsed appropriately based on the platform, or wrap your test strings in a helper function that converts them based on the platform.

How can I use `LF` line endings in Git for Windows in 2020 for good?

There are two git config attributes that affect the line endings: core.autocrlf and core.eol.

Previously, you were told to use core.autocrlf = true to be able to work on cross-platform projects, but it's not true any more.

If your system/IDE/tooling support LF and you do want to use LF as everyone else in your team without any silent lf->crlf->lf normalizations, you must turn off autocrlf and configure eol to not infer native line endings, but force it to use lf.

Now there are two ways to achieve LF in all your files a/o repos:

  1. Globally for all repositories on your local machine.
  2. Per-repository via checked-in .gitattributes file.

    This file overrides any local configuration for everyone who clones the repo.

I personally recommend to go with both for all local repos and to ensure cross-platform cross-dev consistency.



1) Globally for all repositories on your local machine

Being in your working directory (your local repo):

  1. First commit everything

  2. Let's be paranoid a bit and set it both globally and in repo as well. Just in case.

    git config --global core.eol lf
    git config --global core.autocrlf false

    git config core.eol lf
    git config core.autocrlf false
  3. Delete everything "code" except .git.

    You can also omit dependencies, installed files (such as node_modules), build files and any git-ignored file as well.

  4. and lastly run

    git reset --hard HEAD

Things should be working now. Newly checked files should follow the new configuration and keep whatever line-endings were cloned from the remote repo.

Note that if your remote repo uses mix of crlf lf endings, you will also have to run and push

git add --renormalize .


2) Per-repository via checked-in .gitattributes file

Being in your working directory (your local repo):

  1. Create .gitattributes file in the root with this content:

    * text=auto eol=lf
  2. Commit the file (and everything else)

  3. Same as above

  4. Same as above

IMPORTANT NOTE: After you introduce the file into the repository, it is necessary that everyone who still has old CRLF files does step 3 and 4 to update their working directory as just checking out the commit doesn't affect already existing files.

git reset hard



Notes

setting core.autocrlf to true or input overrides core.eol

https://www.git-scm.com/docs/git-config#Documentation/git-config.txt-coreautocrlf

core.autocrlf = input is the preferred value on unix systems.
https://stackoverflow.com/a/41282375/985454

https://stackoverflow.com/a/4425433/985454

Troubleshooting

Reinstall git for windows with third option (as in the screenshot in Q)

Checkout as is - Commit as is (core.autocrlf = false)

git: always force windows line endings for repo

First, you have two conflicting settings. * text=auto and * text eol=crlf are not compatible. You need to pick one.

However, I regret that neither will do what you want. Git has no option that is "always convert to CR/LF in the repository". Your only options control what goes into the working directory.

For example, * text eol=crlf will put Windows-style line endings in the working directory but will put Unix-style line in the repository.

You can disable line ending configuration entirely by using * -text, but this will simply stop doing text conversion and check in the file literally as it exists on disk. It does not eliminate the possibility of a Unix user rewriting the file with Unix-style line endings.

I would encourage you to ignore the contents of the file that exists in the repository, and focus on what you want in working directories instead. If you always want CR/LF on disk then use * text eol=crlf.

Git CRLF and LF line ending on windows/linux

Don't forget to add a .gitattributes rule for that file, in order to force lf for that file

a_file text=auto eol=lf

But regarding your send-email issue, see "git am/format-patch: control format of line endings", try and use git send-email --transfer-encoding=base64, to make sure everything is preserved (including eol)

How to change the line ending in a Git repo dynamically


But my files are still having the CRLF line ending.

If a file inside a commit inside the repository has CRLF line endings, that version of that file is stuck that way forever. No part of any existing commit can ever be changed.

If a file inside a commit inside the repository has LF-only line endings, that version of that file is stuck that way forever. However, you can can choose the ending you want to have Git place in your work-tree when you extract that file.

If you already extracted the file, Git has already done the conversion. Git now thinks everything is fine, even if you just now changed the conversion setting.

Thus, if you change the conversion setting, you must force Git to re-extract the file. The easiest way to do this consistently in all versions of Git is to remove the file from your work-tree, then run git checkout -- path/to/file. Because the file is gone from the work-tree, Git will be forced to extract it again. The updated EOL-conversion will be applied this time.

(Another way to do it is to alter the file, then run the same git checkout, or in Git 2.23 or later, to use git restore. By telling Git that Git should discard your version of the file, and Git seeing that your version of the file is indeed "wrong" in that it doesn't match the index copy because you changed it, Git will be forced to re-extract the index copy.)

That may suffice for you case, or may not. If it does not, read on.

What to know about Git's end-of-line conversions

I'm a firm believer in the "never use Windows at all so that you never need to have your version control system muck with line endings" philosophy myself, but there are a few things to know if you are in some other camp and do want Git to muck with line endings. The most important of these is this: What you store in Git, and what you use when you work with files you got out of Git, are not necessarily the same thing.

To see how this works, remember that Git stores commits rather than directly storing files. The files inside those commits come from Git's index, not from your work-tree. The format of an index-copy of a file is the same as the internal format that Git uses for frozen-for-all-time commits: the data are pre-compressed. So the copy of each file that's in the index is already significantly different from the copy you use in your work-tree, in that the one in your work-tree is not a Git blob object, and generally not zlib-compressed.

Git reads commits into the index before copying them out to your work-tree. Running git add on a file compresses and blob-ifies the file in order to store it in Git's index. Right at this point of conversion, while Git is compressing and Git-ifying a file (git add) or de-Git-ifying and decompressing a file (git checkout-index or equivalent), it's trivial for Git to insert additional conversion operations.

Git therefore does its thing at this point. The things that Git can do—the only things built in directly—are that, on the way out of the index, Git can replace \n-only line endings with \r\n line endings, and on the way into the index, Git can replace \r\n line endings with \n-only line endings.

In other words, you can arrange for Git to throw away some carriage returns before storing a file, and to add some carriage returns when extracting a file. If you do both of these, you get CRLF line endings in your work-tree and newline-only line endings in the commits.

You can, if you like, have Git do only one of these: in particular, with the crlf=input setting, you can tell Git: do just one conversion, on the work-tree-to-index copy operation.

If you choose to have Git do conversions when extracting files, the only conversion available here is turn LF-only into CRLF. You cannot turn CRLF endings into LF-only endings. If the in-Git committed file has CRLF endings, the in-work-tree extracted file will have CRLF endings.

Again, each of these conversions happens in just one direction:

  • index → work-tree: optionally, replace \n with \r\n
  • work-tree → index: optionally, replace \r\n with \n

What you choose with core.autocrlf or .gitattributes directives are:

  • text, -text, and/or core.autocrlf: which files
  • eol=... and/or core.eol: get which treatment(s)
  • crlf=input: on which operation(s)

Once a file has been treated and converted—by copying it to or from the index—Git marks the index's copy as "matches the work-tree's copy" by grabbing key data from the OS: the file's size and other lstat system call values. The precise details here vary because different OSes store different data with different granularity.

The easy way to force a new conversion is to remove one or the other copy of the file: rm file or git rm --cached file destroys the work-tree or index copy respectively, so now a git checkout -- file or git add file will make a new one.

When you run git commit, whatever bytes are in the index copy of the file go into the new commit that Git makes. This new commit is now frozen for all time: the bytes that were in the index are now in the commit, forever (or for as long as the commit itself continues to exist). Nothing and no one can change them.

Consequences of the above

What the above mean is that if you do plan to have your version control system (i.e., Git) muck about with line endings, the line endings you can—and thus probably should—always use for every index copy, and therefore every committed copy, of every text file are LF-only line endings. These can always be converted to CRLF endings in a work-tree file, through an appropriate .gitattributes setting or core.* settings. If you've done such a conversion, that work-tree file can be converted back to LF-only line endings on git add operations.

If you ever do commit a file with CRLF line endings, that commit is stuck that way for all time, and extracting that commit will give you a work-tree copy that has CRLF line endings, every time, because Git has no built in index → work-tree operation that will change this. The only built in CRLF-to-LF operation that Git has only works in the other direction, index ← work-tree.

If you'd like to make a new and improved commit in which the committed copy of that file has LF-only line endings, you have these two options:

  1. make sure your index ← work-tree settings do that, then force Git to add the file (e.g., change it in the work-tree or use git rm --cached on the index copy, and git add it); or
  2. use any command that changes the work-tree copy to have LF-only line endings, e.g., run dos2unix on it or similar, then git add it.

The advantage to method 2 is that you can see the effect immediately (in your work-tree file) and it's hard to get it wrong. The problem with method 1 is that you can't see it, and it's easy to get it totally wrong: e.g., you might accidentally use git rm instead of git rm --cached, which deletes both the index and work-tree copies.

How to force consistent line endings in Git commits with cross-platform compatibility


Q1 Enforcing consistent lineendings

Q2 Enforcing at commit as well as checkout (comment)

I'll divide this into 2 parts: Practice and Principle

Practice

Expansion of code-apprentice's suggestion

  1. Strictly avoid autocrlf — See why autocrlf is always wrong.
    And here for the core git devs arguing about the ill-thoughtout-ness of autocrlf. Note particularly that the implementor is annoyed at the critic but doesn't deny the criticism.
  2. Religiously use .gitattributes instead
  3. Use safecrlf=true to enforce commit-cleanliness. safecrlf is the answer to your Q2 – a file that would change on check-in check-out round tripping would error out on the check-in stage itself.

When a new repo is init-ed:

Go through ls -lR and choose for it's type text, binary or ignore (ie put it in .gitignore)

Debugging:

Use git-check-attr to check that attribute matching and computation are as desired

Principle

Data Store

We may treat git as a data-store loosely analogous to how a USB drive is one.

We say the drive is working if the stuff we put in comes out the same. Else it's corrupted. Likewise if the file we commit comes out the same on checkout the repo is fine else (something) is borked. The key question is

What does "same" mean?

It's non-trivial because we implicitly apply different standards of "sameness" in different contexts!

Binary Files

  • A binary file is a sequence of bytes
  • Preserving that sequence faithfully amounts to reproducing the file

Text Files

...are different

  • A text file consists of a sequence of «printable characters» — let's leave the printable char notion unspecified other than to say no cr no lf!

  • How these lines are separated (or terminated) is again unspecified

  • Symbolically:

    type Line = [Char]

    type File = [Line]

  • Expanding on the 1st unspecified gives us ASCII, Latins, Unicode etc etc... Not relevant to this question

  • Expanding on the 2nd is what distinguishes windows *nix etc. JFTR this kind of file may be little known by the younger generation but also exists. And is particularly useful to remember that the notion "sequence of lines" can be imposed at many different levels.

    We don't care how the sameness respects the unspecified parts

To return to our

USB drive analogy

When I copy foo.txt from Windows to Linux I expect the contents to be invariant. However I'm quite satisfied if H:foo.txt changes to /media/name/Transcend/foo.txt. In fact it would be more than a bit annoying if the windowsisms came through untranslated or vice versa.

Far-fetched?? ¡¡Think again!!

IOW thanks to splendid folks like Theodore T'so we take it for granted that Linux can read a windows file (system). This happens because a non-trivial amt of

  • abstraction matching
  • abstraction hiding

happens under the hood.

Back to Git

We therefore expect that a file checked in to git is the same that's checked out... at a different time... And OS!

The catch is that the notion of same is sufficiently non-trivial that git needs some help from us in achieving that "sameness" to our satisfaction... That help is called .gitattributes!



Related Topics



Leave a reply



Submit