Git Course 0%

Line endings, permissions, binaries

Beginner Git CLI ≈ 8 min

What you will learn

  • What CRLF and LF are and why Windows and macOS differ
  • What core.autocrlf and .gitattributes do, and which one a team should rely on
  • Why a diff can show "old mode / new mode" and how to stop it
  • How Git decides a file is binary and what that changes

After this lesson you can

  • I can explain a "whole file changed" diff and fix its cause
  • I am not alarmed by the CRLF warning, and I know when to act on it

Why this matters

Some day a diff will show every line of a file as changed although you edited one word, or a merge request will contain a file you never touched, marked "mode changed". Both are invisible-character problems, both are harmless once understood, and both waste hours when they are not.

Line endings

Text files end each line with an invisible character. Windows historically uses two (CRLF, carriage return + line feed); macOS, Linux, and GitLab's and GitHub's web editors use one (LF). If Ben on Windows saves a file with CRLF and Ana on macOS opens it, every line differs by one invisible character, and Git reports the whole file as changed.

Git's fix is to convert on the way in and out:

  • core.autocrlf true (Windows): convert to LF when committing, back to CRLF when checking out. Files in the repository are always LF; files on disk look native.
  • core.autocrlf input (macOS, Linux): convert to LF when committing, never convert on checkout.

You set this in lesson 3.3. When Git converts, it may warn:

Terminal
$ git add docs/crlf.md
warning: in the working copy of 'docs/crlf.md', CRLF will be replaced by LF the next time Git touches it

The warning is informational: the file on disk has CRLF, the committed version will have LF. Nothing is wrong; the next checkout normalizes the working copy. It becomes a problem only when a file must keep CRLF (some Windows scripts), which is what .gitattributes is for.

.gitattributes: the team's rule

core.autocrlf is personal configuration; every teammate has to set it correctly. A .gitattributes file in the repository applies to everyone, whatever their settings:

.gitattributes
# Normalize all text files to LF in the repository; check out with the platform's native ending
* text=auto

# These must always have Windows line endings
*.bat text eol=crlf

# These are binary: never convert, never diff as text
*.png binary
*.jpg binary

If your project has one, it wins over your core.autocrlf. If it does not, and line-ending noise appears in reviews, propose adding * text=auto in a small merge request; it is the standard fix.

File modes

Git stores one bit of permission per file: executable or not. On macOS and Linux, chmod +x on a script changes it, and the diff shows it:

Terminal
$ chmod +x src/trailguide.py
$ git diff
diff --git a/src/trailguide.py b/src/trailguide.py
old mode 100644
new mode 100755

100644 is a normal file, 100755 an executable. Such a change is legitimate for a script meant to be run directly, and noise when a tool or a file copy flipped the bit by accident. On Windows the bit does not exist, and Git for Windows ignores mode changes by default (core.fileMode false). If a colleague's merge request shows mode changes on files that should not have them, git update-index --chmod=-x <file> (or copying the file again) restores the mode.

Binary files

Git decides a file is binary by looking at its content: if the first few thousand bytes contain a NUL character, it is binary. Binary files are stored whole, never converted, never diffed line by line:

Terminal
$ git diff --staged
diff --git a/docs/images/photo.png b/docs/images/photo.png
new file mode 100644
index 0000000..aa860ab
Binary files /dev/null and b/docs/images/photo.png differ

Two consequences: reviews cannot show what changed inside, and merges cannot combine two versions (Git asks you to pick one). Mark known binary types in .gitattributes so line-ending conversion never damages them; PDFs and images corrupted by CRLF conversion are a classic problem on projects without that file.

Text files that happen to contain unusual bytes (some exports, some CSVs with a byte-order mark) may be misdetected as binary; .gitattributes can force *.csv text.

Common mistakes

  • Panicking at the CRLF warning. It describes a normalization, not damage.
  • "Fixing" line endings by re-saving the whole file in a different editor and committing a 400-line diff. Fix the configuration, not the file.
  • Committing a mode change by accident (often after copying files from a USB drive or a download). Check git diff for old mode lines before committing.
  • Converting binaries. Without binary attributes and with autocrlf on, a PNG containing the bytes for CR and LF can be altered. Declare binaries in .gitattributes.

Try it yourself

Goal: produce the CRLF warning on purpose and see a mode change.

  1. In the playground, create a Windows-style file: printf 'line1\r\nline2\r\n' > docs/crlf.md, then git add docs/crlf.md and read the warning (it appears with core.autocrlf input or true).
  2. Unstage and delete it: git restore --staged docs/crlf.md && rm docs/crlf.md.
  3. On macOS or Linux: chmod +x src/trailguide.py, run git diff, then chmod -x src/trailguide.py.

Expected result: the warning names the file and says CRLF will be replaced by LF; the diff shows old mode 100644 / new mode 100755 and nothing else.

Show solution

The warning is the conversion at work: the committed version would have LF while your disk copy has CRLF. The mode diff is one line of metadata; chmod -x reverses it and git diff goes quiet. On Windows, step 3 shows nothing because file modes are not tracked there.

Check yourself

1. A colleague's merge request shows every line of a file as changed, but only one sentence is different. Most likely cause?
2. Which is the team-wide solution for line endings, independent of each person's settings?
3. git diff shows old mode 100644 / new mode 100755 and no content change. What happened?

Key terms

Diff Commit