Line ending configuration in Git

When you work with people on different OS systems, you probably run into line-ending (CRLF or LF) issues. Git provides different ways to control line ending style to help you with this issue. Most content is organized from Configuring Git to handle line endings and new content is added for comprehensive understanding.

For a quick view, there are two ways to set your line ending style:

  • core.autocrlf is a global or repository setting that is configured by git config command. The configuration file where core.autocrlf is set into can not be committed into the repository.

  • Git attributes is a per-repository setting. The attributes are written in a file named .gitattribute that can be committed into the repository and overrides core.autocrlf, ensure an consistent behavior of different users regardless of their Git settings.

Git configuration variable – core.autocrlf

core.autocrlf is a Git configuration variable that you can set with git config for all repositories or for a specific repository. It can be set with one of the three following values: true, input and false.

  • true (If you want CRLF line ending in repository and working directory even on Linux)

    Let Git to handle the files in whatever way it thinks is best.

    On Windows (Git v2.13.2):

    LF to CRLF on commit.

    LF to CRLF on checkout if the file does not exist in the working directory yet.

    $ git config core.autocrlf true
    
    $ git add eol-lf.txt
    warning: LF will be replaced by CRLF in eol-lf.txt.
    The file will have its original line endings in your working directory.
    
  • input (If you want LF line ending in repository and working directory even on Windows)

    CRLF to LF on commit.

    CRLF to LF on checkout if the file does not exist in the working directory yet.

    $ git config core.autocrlf input
    
    $ git add eol-crlf.txt
    warning: CRLF will be replaced by LF in eol-crlf.txt.
    The file will have its original line endings in your working directory.
    
  • false (If you do not want conversion of line ending)

    No conversion.

Therefore test that on your machine and decide how to set core.autocrlf.

Note

Below description from git config about core.autocrlf is incorrect:

Set to true if you want to have CRLF line endings in your working directory and the repository has LF line endings.

Examples

# Set core.autocrlf to input globally
$ git config --global core.autocrlf input

# Config core.autocrlf to input locally for the current repository.
$ git config core.autocrlf input

Note:

Git configuration file can not committed into the repository, but .gitattributes file can be committed.

Not sure which value to use

If you have a new repository, just set core.autocrlf to input or use git attribute eol and set it to lf introduced below no matter the OS you are working on (even Windows).

Git attributes – text and eol

text and eol are two attributes that can help to set your line ending style in .gitattribute file.

  • text=auto

    Git will handle the files in whatever way it thinks is best. This is a good default option.

  • text eol=crlf

    Git will always convert line endings to CRLF on checkout. You should use this for files that must keep CRLF endings, even on OSX or Linux.

  • text eol=lf

    Git will always convert line endings to LF on checkout. You should use this for files that must keep LF endings, even on Windows.

  • binary

    Git will understand that the files specified are not text, and it should not try to change them. The binary setting is also an alias for -text -diff.

Note

Below description in .gitattributes about text and eol is incorrect:

Set text to string value auto

When text is set to “auto”, the path is marked for automatic end-of-line conversion. If Git decides that the content is text, its line endings are converted to LF on checkin. When the file has been committed with CRLF, no conversion is done.

Set eol to string value lf

This setting forces Git to normalize line endings to LF on checkin and prevents conversion to CRLF when the file is checked out.

Example from Configuring Git to handle line endings

Here’s an example .gitattributes file. You can use it as a template for your repositories:

# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text

# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf

# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

Note: A .gitattributes file can be located in any directory of a repository which takes effect to content of that folder and its sub folders.

Refresh a repository after changing line endings

If you have committed files with wrong CRLF/LF line endings. You can force to add the tracked files again to the index even their content is not modified after changing core.autocrl or text attribute.

# Suppose you have committed file with wrong CRLF/LF line endings.

# Now:
# Change `core.autocrlf` or `text` to use your desired line ending style.

# Execute `git add` with --renormalize option
$ git add --renormalize .
Show the rewritten, normalized files.

# Show what happed
$ git status

# Commit the changes to your repository.
$ git commit -m "Normalize all the line endings"

Resources

Configuring Git to handle line endings

core.autocrlf

Git attributes

Useful Git aliases

For heavy Git users, adding aliases for some common used Git commands brings big convenience especially when a command is too long to remember. Here you will see some useful alias commands. You can rename them according your personal habits and add your own alias gradually.

Add Git aliases

You may add alias with git config one by one or just edit your ~/.gitconfig.

Add an alias with git config command.

# Add alias "st" for "status" command
$ git config --global alias.st status

# Add alias "last" for "log -1 HEAD" command
$ git config --global alias.last "log -1 HEAD"

~/.gitconfig:

[alias]
  st = status
  co = checkout
  last = log -1 HEAD
  cm = commit --message
  ca = commit --amend
  cane = commit --amend --no-edit
  lg = log --all --graph --oneline --decorate

The last alias above git lg is very useful, it gives you a visual representation of the commit history in the terminal. See viewing git commit history for more information about the command.

$ git log --all --graph --oneline --decorate
* c3324ea (HEAD -> master, origin/master, origin/HEAD) feat: allow uplading file
| * d7fff0f (feat) up: hello.php
| * 3a2201f add: build
|/
| * 99b000f (clean-branch) add: a graph
| * b495f71 add: new.md
| * 625cdcb add: clean-branch.md
| *   f0d56f9 (refs/stash) WIP on master: b1f092d update: hello.php
| |
|/ /
| * ef288eb index on master: b1f092d update: hello.php
|/
* b1f092d feat: update form 
* 5439504 Revert "hello.php : add input tag"
* f82d809 readme.md: add new line2
*   9da2b81 Merge branch 'test'
|
| * 4f10b4a hello.php : add new line
* | 2a12f93 readme.md: adde new line
|/
* 5f49a56 update readme and hello
* cd6b3aa add hello.php
* a9d90e9 initial commit 

If you like, you can make some commands very short.

[alias]
  a = add
  b = branch
  c = commit
  d = diff
  f = fetch
  g = grep
  l = log
  m = merge
  o = checkout
  p = pull
  r = remote
  s = status

How many keystrokes saved by using aliases

Below is a table tells how many keystrokes you will save for using some aliases.

Original command Alias Keystrokes saved
git commit –message git cm 14
git commit –amend git ca 12
git commit –amend –no-edit git cane 20
git log –all –graph –oneline –decorate git lg 35

Gitalias repository

If you want to use aliases as many as possible, there is a GitHub repository gitalias which provides many Git alias commands with explaination that you can use as you like.

Below are some complex examples:

  # log like - we like this summarization our key performance indicators. Also aliased as `log-like`.
  ll = log --graph --topo-order --date=short --abbrev-commit --decorate --all --boundary --pretty=format:'%Cgreen%ad %Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%cn]%Creset %Cblue%G?%Creset'

  # log like long  - we like this summarization our key performance indicators. Also aliased as `log-like-long`.
  lll = log --graph --topo-order --date=iso8601-strict --no-abbrev-commit --abbrev=40 --decorate --all --boundary --pretty=format:'%Cgreen%ad %Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%cn ]%Creset %Cblue%G?%Creset'

Resources

Setting default commit template in Git

Well-formed commit messages can be rather helpful in terms of tracking the history or generating the changelog automatically. Here we will configure a commit template to help writing commit messages in a good format. You can force it across the whole team.

Configure Git commit message template

Write you own commit template file, such as ~/.gitmessage. Below is a good example :

<type>: subject (try to keep under 50 characters)

Multi-line description of commit to explain
what, why and how this change is being made
(try to limit each line under 72 Characters)

Provide ticket numbers or links to other relevant resource
Example:
[Ticket: #53]

This template structure contains 3 sections:

  1. head, a subject line starting with a commit type, for example feat: add settings page (followed by a blank line).
  2. body, explains the commit.
  3. foot, optional, provides tickets or other information.

Setup it as the default commit template with below command:

# Config a custom commit template
$ git config --global commit.template ~/.gitmessage

When you run git commit command, an editor opens containing something like this.

Subject line (try to keep under 50 characters)

Multi-line description of commit to explain
what, why and how this change is being made
(try to limit each line under 72 Characters)

Provide links or ids to relevant issues or other resources
Example:
[Ticket: #53]
# Please enter the commit message for your changes. Lines starting
# with '' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   my-plugin.php
#

Then replace the 3 parts with your actual content.

Commit types

The commit types known to developers that you can set:

Commit Type Description
feat New feature.
fix Fix a bug.
refactor Refactor production code.
style Format issues like missing semi colons, etc, no code change.
docs Change documentation files.
test Add or refactor tests, no production code change.
chore Update grunt tasks, no production code change. Some examples of grunt tasks: configuration changes (.gitignore, .gitattributes), tool changes or something that would not go into the product.

You can put commit types in the template file to help to pick the proper commit type as Git commit template shows:

# <type>: (If applied, this commit will...) <subject> (Max 50 char)
# |<----  Using a Maximum Of 50 Characters  ---->|


# Explain why this change is being made
# |<----   Try To Limit Each Line to a Maximum Of 72 Characters   ---->|

# Provide links or keys to any relevant tickets, articles or other resources
# Example: Github issue #23

# --- COMMIT END ---
# Type can be
#    feat     (new feature)
#    fix      (bug fix)
#    refactor (refactoring production code)
#    style    (formatting, missing semi colons, etc; no code change)
#    docs     (changes to documentation)
#    test     (adding or refactoring tests; no production code change)
#    chore    (updating grunt tasks etc; no production code change)
# --------------------
# Remember to
#   - Capitalize the subject line
#   - Use the imperative mood in the subject line
#   - Do not end the subject line with a period
#   - Separate subject from body with a blank line
#   - Use the body to explain what and why vs. how
#   - Can use multiple lines with "-" for bullet points in body
# --------------------
# For updated template, visit:
# https://gist.github.com/adeekshith/cd4c95a064977cdc6c50
# Licence CC

Resources

Ignoring files in Git

Normally there are some files or folders that you do not want them to be tracked. Here you will learn how to set up various ignore rules to exclude them from git repositories.

Ignore files in a git repository

Use .gitignore to allow sharing ignore rules (version controlled)

If you want some files being untracked in a repository, create an .gitignore file at its root directory and commit it. .gitignore file is used to list all the file patterns to ignore, standard glob patterns work in it.

Basic rules for patterns have been included in below example .gitignore file :

# ignore all .class files
*.class

# Exclude lib.class from "*.class", meaning all lib.class are still tracked
!lib.class

# Ignore all json files whose name begin with 'temp-'
temp-*.json

# Only ignore the build.log file in current directory, not those in its subdirectories
/build.log

# Specify a folder with appending a slash in the end
# Below statement ignores all files in any directory named temp
temp/

# Ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# Ignore all .pdf files in the doc/ directory and any of its subdirectories
# /** matches 0 or more directories
doc/**/*.pdf

Remember to commit and push to share the rules in the repository. If you forget to do that, .gitignore will only take effect locally.

$ git add .gitignore
$ git commit -m 'chore: add .gitignore'
$ git push

Note

Adding new patterns in .gitignore won’t affect the files already tracked by Git. You can stop tracking a file.

You can have more than one .gitignore file in subdirectories. Its content will only apply to the folder it resides.

Use .git/info/exclude to ignore files locally (without sharing ignore rules)

If you just want a personal and local ignore configuration without committing a .gitignore file, use .git/info/exclude to achieve that.

What you do is just adding exclude rules in .git/info/exclude within in the root of a repository.

Any rule added in the file will not be pushed to a remote repository. This way enables you to ignore locally generated files and no need to worry about other users.

Ignore files in all repositories

If you want to ignore some files for all repositories, you can set a global .gitignore file. Below is an example to ignore .DS_Store files :

File ~/.gitignore_global :

*~
.*.swp
.DS_Store

Then configure it as your global gitignore settings in Git :

$ git config --global core.excludesfile ~/.gitignore_global

Debug gitignore files with check-ignore command

check-ignore command can be used to debug if a file/folder match some ignore rules in .gitigonre (or other input files to the exclude mechanism).

Debug ignore rule

# -v, verbose, output details about the matching pattern (if any)
# for each given pathname.
$ git check-ignore -v <pathname>

With -v, if the pathname matches an ignore pattern, it outputs the pattern’s line number, the pattern itself and your queried pathname, or it output nothing. Without -v, if some pattern matches, only the pattern is output.

Below example debugs if user.csv is excluded:

# Debug whether user.csv is exclude by some ignore rules
$ git check-ignore -v user.csv
.gitignore:3:*.csv      user.csv

Here user.csv is ignored by *.csv pattern in .gitignore.

Note: By default, tracked files are not output since they are not subject to exclude rules. If a file is tracked before and untracked currently, it can be output.

With --no-index option, it will also output files that has been tracked but ignored in .gitignore.

# Track user.csv even it has been ignored.
git add -f user.csv

# Check without --no-index, nothing is output
git check-ignore -v user.csv

# Check with --no-inxex
git check-ignore -v --no-index user.csv
.gitignore:3:*.csv      user.csv

Debug ignore and exclude rules

If user.csv is ignored by *.csv but then excluded by !user.csv in .gitignore, such as below .gitignore file.

# ignore all .csv files
*.csv

# exclude user.csv
!user.csv

Then it will only output the ignore rule.

$ git check-ignore -v user.csv
.gitignore:3:*.csv      user.csv

Note:

Compared with check-ignore command, you may just want to see whether a file is tracked. ls-files command will help you.

# Check whether user.csv is traked or not
$ git ls-files -- user.csv

# List all untrancked files excluding ignored files
$ git ls-files --others --exclude-standard

Reference

Read more

Setting up Beyond Compare as difftool and mergetool in Git

Set up Becond Compare as difftool and mergetool in Git. Last updated: 2018-12-18

Environment: Windows, Git v2.13.2

You can use git commands or directly edit global git config file to configure Beyond Compare as difftool and mergetool.

Method 1 : use git commands

# Config Beyond Compare 4 as difftool

$ git config --global diff.tool bc4
$ git config --global difftool.prompt false
# Note: set parameters for Beyond Compare tool
$ git config --global difftool.bc4.cmd '"C:\Program Files\Beyond Compare 4\BCompare.exe" "$LOCAL" "$REMOTE"' 

# Config Becond Compare 4 as mergetool

$ git config --global merge.tool = bc4
$ git config --global mergetool.prompt false
$ git config --global mergetool.bc4.path 'C:\Program Files\Beyond Compare 4\BCompare.exe'

Method 2 : directly edit global git config file

Edit git global git file .gitconfig in your home directory and add below contents:

[diff]
    tool = bc4
[difftool]
    prompt = false
[difftool "bc4"]
    cmd = "\"C:\\Program Files\\Beyond Compare 4\\BCompare.exe\" \"$LOCAL\" \"$REMOTE\""
[merge]
    tool = bc4
[mergetool]
    prompt = false
[mergetool "bc4"]
    path = C:\\Program Files\\Beyond Compare 4\\BCompare.exe