Intermediate

Git Configuration and Attributes

Git is one of those skills that never goes away. What many don't know is how configurable Git can be. Before we dive into the details, here's an important note: setup is a very personal t...

Table of Contents

  1. Course presentation
  2. Customize Git with configuration
  1. Manage files with attributes
  1. Improve Git workflows

1. Course presentation

Welcome to the Git Configuration and Attributes course. This course is presented by AJ Foster, Software Engineer at CodeSandbox, who has been using Git to develop software since 2012.

Git is a powerful tool that many of us use every day, but we rarely give it a second thought. What we don’t always realize is how Git can be configured to better fit our workflow. In this course, we’ll take a practical look at how Git’s configuration and attributes features can simplify everyday life.

The main themes covered

  • How Git handles multiple configuration levels
  • How attributes allow you to adapt Git’s behavior to each file type
  • Concrete ways to use these features to improve workflow

By the end of this course, you will have the confidence to take control of Git’s behavior and adapt it to your needs.

Prerequisites

Before starting this course, you should be familiar with Git and the basic commands for working with a repository.


2. Customize Git with configuration

Total section duration: 17m 1s

2.1 Verification of versions

Before you begin, it is important to check the Git versions covered by this course. The course was created with a specific version of Git, and the information presented applies to specific versions. Verify that your version of Git is compatible before following the examples.


2.2 How Git evaluates configuration

Introduction

Git is one of those skills that never goes away. What many don’t know is how configurable Git can be. Before we dive into the details, here’s an important note: setup is a very personal thing. Git allows you to change so many aspects of its behavior because reasonable people can have different opinions about the best way to use Git depending on the circumstances.

The examples presented in this course do not necessarily mean that you should use exactly these options. Talk with your team, assess your own needs and figure out what works best for you. More opinionated recommendations will come later, in the section on improving workflow.

Hundreds of configuration options

Git has several hundred configuration options, identified by a name, such as user.name or core.editor. Each option has a default value or default behavior that Git will use if not set. These defaults form the base layer of configuration evaluation.

Configuration levels

Git divides its configuration into several levels:

LevelDescriptionScope
systemSystem configuration affecting all usersThe whole machine
globalConfiguration per user (most common)The current user
localConfig by repository (most specific)A single deposit
  1. System level — System configuration affects all users of a machine. It is rarely used, but we will see an example of a case where it can be useful. It is stored in a central location and manipulated with the --system flag.

  2. Global level (per user) — This is the most common place to store preferences. Different users can choose their own options without affecting each other. These options are stored in the user’s home directory, manipulated with the --global flag.

  3. Local level (per repository) — Local configuration is limited to a single copy of a repository. It can affect any user on a system that interacts with this directory, but it does not propagate during push or pull. We use the --local flag or simply omit the flag, because local is the default.

The evaluation hierarchy

When Git has to decide how to behave, it evaluates all levels of configuration from the most specific to the most general:

Local (dépôt) → Global (utilisateur) → System (machine) → Valeur par défaut

Local configuration has the highest priority. Git will use options from a specific repository if they are available. Then it looks at the user’s global configuration. Finally, if nothing has been found, it consults the system configuration and ultimately uses the default value.

System level use cases

The system configuration is not often used because it is generally not the right level of abstraction. Either you are working on a computer with a single main user (in which case the user configuration is as useful as the system configuration), or you are working on a multi-user machine where each individual probably has different preferences.

However, the system configuration has its uses. For example, imagine that you are working on a computer where several commonly used programs are installed in a different location than normal. Git may not be able to find these binaries when it needs them. Using the system configuration, we can tell Git to look in an alternative location, and by doing this at the system level, this will solve the problem for all users on the machine.

Environment variables

In addition to the file hierarchy, there are environment variables that can modify the behavior of Git. For example, the GIT_AUTHOR_EMAIL variable overrides the user.email setting in the configuration file. These variables are not used often, and it is recommended to use configuration instead.

Conditional inclusions

Git also allows introducing additional configuration levels if necessary, using the includeIf option to conditionally include configuration files based on the repository or branch path. This is beyond the scope of this course, but it is available if you need it.


2.3 Add new configuration

Define your name and email

Many users’ first interaction with the Git configuration is adding their name and email address, which will be used in the author section of a commit. Using Git’s command line interface (CLI), one can set a name and email at the global level.

git config is the base command used throughout this module.

Set username globally:

git config --global user.name "Votre Nom"

Breakdown of the order:

  • git config: the basic command
  • --global: specifies the configuration for the current user
  • user.name: the name of the option
  • "Your Name": the value to define (quotes are necessary if the value contains spaces)

Read a configuration value:

To confirm that a change is in place, you can call the same command without providing a value. Instead of setting the configuration, Git will read it to us:

git config --global user.name

Set email:

git config --global user.email "votre@email.com"

Direct file editing

It is not required to use the command line to modify the Git configuration. You can directly edit the configuration file created in the background. Once the file is saved, it is exactly as if we had defined the option via the CLI.

Configuration at system level

To define a system-wide preference, replace the --global flag with --system:

git config --system core.editor vim

For example, this sets the editor program used by Git if a user does not have their own configuration.

Configuration locally (per repository)

To define a configuration specific to a repository, we use the --local flag (or omit it, because it is the default). This command must be executed inside the repository that you want to modify:

git config --local user.email "pro@entreprise.com"

This can be useful when you have personal and professional projects on the same computer.

Command Summary

ActionOrder
Set option (global)git config --global option.name "value"
Read an optiongit config --global option.name
Set at system levelgit config --system option.name "value"
Define locallygit config --local option.name "value"

Everything is stored in files in the background, regardless of whether you go through the CLI or directly edit the file.


2.4 Structure of a configuration file

Configuration files

Whenever one changes the configuration via the command line, Git transparently edits a file in the background. Each configuration level has its own file, and the exact location of these files may vary between systems.

Regardless of configuration level or location, the file structure is the same.

For our examples, we focus on the global configuration located in the user’s home directory. This is often a .gitconfig file directly in the home directory, although Git also supports another location compatible with the Cross-Desktop Group (XDG) standard.

Configuration file format

Let’s take the example of a configuration with user.name and user.email. In the configuration file, we see these terms divided in an interesting way:

[user]
    name = AJ Foster
    email = aj@example.com

Structural principles:

  • The first part of an option forms the section, which is surrounded by brackets and serves as the title in the file (e.g. [user])
  • The following parts form the option name, followed by an = sign and its value (e.g. name = AJ Foster)

Example of extended configuration file

A more complex configuration file may have multiple sections with multiple options, as well as a subsection (subsection) surrounded by quotes, whose options only apply in certain cases:

[user]
    name = AJ Foster
    email = aj@example.com

[core]
    editor = vim
    autocrlf = input

[diff "jpeg"]
    textconv = exif

[alias]
    stash-all = stash --include-untracked

Configuration files may have extra spaces and comments using the # or semicolon ;. The order of the sections and their options does not matter, as long as the options are in the correct section.

Note on subsections

Subsection syntax (enclosed in quotes) is a way to target specific behaviors. For example, [diff "jpeg"] sets a diff configuration that only applies to files marked with the diff=jpeg attribute.

Similarities with INI and TOML

If you’ve seen a lot of configuration files, this syntax probably looks like INI or TOML files. However, beware of misleading similarities:

  • Git does not support multiline strings or other advanced TOML features
  • Subsection syntax for INI files is considered deprecated in Git

Common sections in configuration files

SectionContent
[user]Author information used in commits (name, email)
[core]Fundamental options affecting many Git commands (editor, compression, etc.)
[push]Options controlling git push behavior
[rebase]Options for git rebase
[alias]Shortcuts for Git subcommands
[diff]Configuration for diff generation
[filter]Configuring clean/smudge filters
[init]Options for git init
[branch]Branch management

There are several hundred options in the git config documentation, and the list is known to be incomplete. Subcommands tend to document their own options, and it is even expected that configuration files may include options for tools other than Git itself.

Tips for navigating options

Faced with the multitude of options available, here is how to approach them methodically:

  1. Evaluate your personal pain points. For example, if you are irritated by having to hit the --set-upstream flag every time you git push to a new branch, there is an option for that: push.autoSetupRemote.

  2. Talk to your team. There may be configurable best practices that help avoid problems with your team’s workflow. For example, if the team has standardized dev or trunk as the default branch name, you can use init.defaultBranch.

  3. Find other people’s setups online. Many people share their global setup files and dotfiles to inspire others.


2.5 View and delete configuration

The problem: Difficult to debug configuration

We have seen that there are many configuration options available in Git. Since options tend to be spread across multiple files and accumulate over time, here are the tools available to manage our configuration.

Let’s say we recently added an option to the global configuration, but Git is not behaving as expected. We checked the option name and its value, but something is still wrong.

Show active configuration: git config --list

A great first step is to ask Git to display the active configuration options:

git config --list

This command displays the active configuration for the current directory. This is a great way to quickly check if an option is set and if it has the desired value.

By default, Git uses the less program to paginate output. Type Q to exit.

Depending on your shell environment, you can sort or filter the output:

git config --list | sort
git config --list | grep user

Show the origin of an option: --show-origin

The --list command is useful for confirming the active configuration, but it does not diagnose why a configuration value is different from what was expected, or why it appears multiple times. To do this, we can use the --show-origin flag:

git config --list --show-origin

This command displays the name of the file where the configuration can be found. Example output:

file:/home/user/.gitconfig    user.name=AJ Foster
file:/home/user/.gitconfig    user.email=aj@example.com
file:.git/config              user.email=pro@company.com

Show scope: --show-scope

We can go even further by using the --show-scope flag to add the scope (system, global or local) to the output:

git config --list --show-origin --show-scope

This allows you to clearly see when you have the same configuration in multiple scopes, which can override the desired value. For example :

system  file:/etc/gitconfig         core.editor=nano
global  file:/home/user/.gitconfig  user.name=AJ Foster
local   file:.git/config            user.name=Work Name

In this example, the local configuration user.name overrides the global user.name.

Remove an option: --unset

If you no longer want to replace the global name with a local configuration, you can delete the local configuration using the --unset flag:

git config --local --unset user.name

This removes the configuration for this scope, allowing the more general scope or default to be used.

Remove an entire section: --remove-section

To go even further and delete an entire configuration section of a file, you can use the --remove-section flag:

git config --local --remove-section user

Best practices for configuration management

When evaluating and debugging your configuration, keep in mind the general guidelines on which options should go in which scope:

  • Local: for one-off changes and special cases of a repository
  • Global (user): for most of your personal preferences
  • System: for items that affect all users of the machine

Management Command Summary

# Afficher toute la configuration active
git config --list

# Afficher avec l'origine (fichier source)
git config --list --show-origin

# Afficher avec la portée
git config --list --show-scope

# Supprimer une option spécifique
git config --local --unset user.name

# Supprimer une section entière
git config --local --remove-section user

Git has hundreds of configuration options affecting many aspects of its behavior. But sometimes you want Git to behave differently depending on the type of file it’s working with. For these kinds of options, Git provides attributes.


3. Manage files with attributes

3.1 How Git evaluates attributes

Why attributes?

Git has a large number of configuration options controlling many aspects of its behavior. Sometimes, however, you want Git to behave a little differently depending on the file it’s working on. This is where attributes come into play.

Analogy with .gitignore

To understand what we mean by “changing behavior based on file”, it’s helpful to think about something that isn’t really an attribute, but acts like one: ignoring files.

With a .gitignore file, we specify a list of paths, optionally including patterns with wildcards and globs, that Git should treat differently from other files. The attributes are going to look very similar, but with an even greater level of control.

Structure of a .gitattributes file

Here is an example of a .gitattributes file:

*.ex diff=elixir
*.png -diff
*.jpg -text -diff
*.bat eol=crlf
*.sh eol=lf

Just like a .gitignore file, each line begins with a pattern which corresponds to one or more file paths.

Important differences between .gitattributes and .gitignore

Warning! There are two major differences between patterns in a .gitattributes file and a .gitignore file:

  1. No negative patterns. In .gitignore, you can prefix a pattern with ! to exclude files. In .gitattributes this is not possible. Instead, use a regular pattern and define a different attribute than the one above.

  2. No matching with a single trailing slash. In .gitignore, you can match all files in a directory by writing the directory name followed by a slash. In .gitattributes this is not possible. Instead, use a glob pattern to match all files.

Attribute Types

After the file pattern, one or more attributes can be listed. These attributes can be:

  1. Set to a value (e.g. diff=elixir) — as in the first line of the example, where Git is told to treat .ex files as Elixir code when generating diffs. This includes Elixir module and function information in the diff chunk headers.

  2. Boolean in nature (e.g. -text) — like the -text attribute which is defined (or undefined) on multiple lines. This attribute controls whether Git automatically handles the line feed character type (carriage return or line feed) when a file is checked in or checked out.

General rule: Attributes are available when Git needs to make an assumption about how to work with a particular file type, and it might make the wrong choice.

Attribute levels

Just like configuration, attributes exist in several levels, each level being supported by a file:

LevelFileScope
Default(integrated with Git)Git default behavior
Systemsystem-wide fileAll machine users
UserFile controlled by core.attributesFileThe current user
Local.git/info/attributesOnly one copy of the deposit
Repository.gitattributes in repositoryAll repository users

system, user, and local level files do not travel with the repository when pushing or pulling — they work exactly like the Git configuration in this regard.

.gitattributes files in the repository

There is, however, an additional set of locations where Git attributes can reside: .gitattributes files inside the repository itself. Like a .gitignore file, these files can be placed in subdirectories and are committed to the repository to be shared by everyone.

Local (uncommitted) attributes always have the highest priority, but these files are evaluated next in order of their proximity to the file Git is currently working on. This makes .gitattributes quite powerful, but also more difficult to determine where a given attribute should reside.

Attribute priority order

Local (.git/info/attributes) > .gitattributes (plus proche) > .gitattributes (racine) > User > System > Default

3.2 Setting Git attributes

Handle binary files (JPEG images)

Sometimes repositories contain images — for example, a JPEG file. Attributes can be used to ensure that Git treats this file appropriately throughout its lifecycle.

Problem: Git might see a byte that looks like a carriage return or line feed character and think it needs to modify those characters when the file is checked in or checked out.

Solution: Disable the text attribute for this file type. Since this is something beneficial — even mandatory — for everyone who works with this repository, we can include this attribute in the .gitattributes file at the root of the repository:

# Désactiver la conversion de fin de ligne et les diffs texte pour les JPEG
*.jpg -text -diff
*.jpeg -text -diff

With -diff, instead of trying to show the differences in the binary version of the image, Git will simply say that the image has changed.

The binary macro

Disabling these two attributes is so common that Git has a special macro called binary to accomplish the same thing:

# Équivalent à -text -diff
*.jpg binary
*.jpeg binary
*.png binary
*.gif binary

Configure a diff tool for binary files

Let’s say we installed a tool called exif, which takes an image file and displays its metadata. Git can be told to use this program when generating a diff of a JPEG file, with the help of a configuration:

# Dans la configuration globale (~/.gitconfig)
[diff "jpeg"]
    textconv = exif

Then, in the .gitattributes file:

*.jpg diff=jpeg

Problem: Since we disabled the diff attribute earlier in the repository and the repository attributes have a higher priority than global attributes or configuration, we will not see the results of this change.

Solution: We can even replace the attributes defined in the repository via the local attributes file (.git/info/attributes). There, we can target JPEG files and reactivate the diff attribute:

# Dans .git/info/attributes (local, non committé)
*.jpg diff=jpeg

This only affects our copy of the repository, so everyone gets the safe default behavior, while we benefit from the exif tool. Now, when we look at a diff of a JPEG file, we see the text generated by exif, including changes to the image metadata.

This pattern is typical of Git attributes

  • Mandatory or very beneficial defaults can be committed to the repository.
  • Individuals can override these attributes with their own local attributes.
  • These overrides, along with user and system attributes, may use additional programs and configuration that may not be available to everyone.

Check end-of-line

Another common attribute in many repositories is controlling the end-of-line characters used in a file.

We have already seen the text attribute which controls whether Git will automatically handle line endings when checking in or checking out. Sometimes a file must always have a certain type of line ending:

# Les scripts PowerShell nécessitent CRLF (Windows)
*.ps1 eol=crlf

# Les scripts shell UNIX nécessitent LF
*.sh eol=lf
  • PowerShell: scripts native to environments that use carriage returns (CRLF) at the end of each line. We set eol=crlf to ensure that these files are always committed and extracted with the correct characters.

  • UNIX Shell: scripts native to environments that use newline characters (LF). We set eol=lf to ensure they stay that way.

Each of these attributes should probably be applied for everyone who works in a repository. So you’ll usually find them in a .gitattributes file in the repository, not in someone’s user or system attributes.

The idea that attributes can affect how files are modified when they are checked in or checked out is called a filter in Git.


3.3 Git filter attributes

The fundamental role of Git

At the heart of Git, its mission is to efficiently move files between its internal storage and the working directory (working directory) where they are edited. Sometimes the data shouldn’t look exactly the same in each of these places.

For example, Git prefers to store files with simple newlines in its storage, but these files may need carriage returns when edited on Windows.

What are filters

This process of modifying files as they move between Git storage and the working directory is not only accessible to Git — it can also be used, with filters.

A filter requires both an attribute and a configuration:

  1. In the .gitattributes file: we match one or more files and we define a filter attribute with a string name as value. This name is the driver of the filter.
.env filter=secrets
*.ex filter=elixir-format
  1. In the configuration: we define a subsection of the filter configuration with a corresponding driver name.
[filter "secrets"]
    clean = clean-secrets.sh
    smudge = smudge-secrets.sh
    required = true

[filter "elixir-format"]
    clean = mix format -
    smudge = cat

Important: configuration vs attributes

It is important to repeat: attributes can be committed to a repository, but not configuration. If you decide to implement a filter for your repository, consider whether it should be local or user-specific attributes so that it doesn’t travel with the rest of the repository. Otherwise, it’s probably a good idea to document the configuration very well so that others can create the necessary configuration.

The metaphor of the filing cabinet and the workshop

To understand when each step executes, think about the data from Git’s perspective:

  • A Git repository’s internal storage is like a neat file cabinet where Git efficiently organizes everything.
  • The working directory where we edit files is like a messy workshop where changes occur everywhere.

With this in mind:

  • clean: Git takes information from the messy workbench and cleans it up before tidying it up. This is when a file is committed (checked into Git).
  • smudge: Git takes a clean piece of data and dirty it up a bit to make it match the rest of the workbench. This is when a file is checked out (extracted to the working directory).
Répertoire de travail  ──[clean]──→  Stockage Git (commit)
Stockage Git           ──[smudge]──→ Répertoire de travail (checkout)

Concrete example: format the code automatically

Many languages ​​have autoformators. We can use a clean filter to format the code before it is committed. All one needs is a command that accepts the code as standard input and displays the clean result as standard output.

With the Elixir language, the clean filter would be:

mix format -

The hyphen signals that the command should read the contents of the file from standard input. So every time a file is checked in, Git will format the code and store the formatted output.

In this case, we don’t want to “de-format” the code during extraction, so we can omit the smudge filter or use cat to leave the content intact:

[filter "elixir-format"]
    clean = mix format -
    smudge = cat

Note: Another Git feature, hooks, might be better suited for formatting code during commit. However, this example gives you an idea of ​​the power of filters.

Process filters

When a large number of files are checked in or checked out from a repository, it can become computationally expensive to start and stop the clean and smudge programs for each file. Process filters allow a program to start once and receive files asynchronously from Git. There is a special protocol for this, which is beyond the scope of this course, but it is available if you need it.


3.4 Use clean and smudge filters

Use case: exclude secrets from commits

When we think about modifying files during their commit or checkout, it is natural to think about the types of data that we do not want to see committed to a repository. Let’s see an example of using the clean and smudge filters to transparently keep API keys and other secrets out of our commits.

Step 1: Identify files to filter

First, we identify the files that must be processed by the new filter. In this example, we focus on the .env file where the environment variables are stored:

# .env
API_KEY=mon-secret-api-key
DATABASE_URL=postgres://user:password@localhost/db
THIRD_PARTY_TOKEN=super-secret-token

We have some API keys that we need for local development, but which should not be shared with the outside world.

Step 2: Define filter in .gitattributes

We define a filter called secrets and we scope it only to the file that interests us:

# Dans .git/info/attributes (local, non committé)
.env filter=secrets

Note: We use local attributes here so that this filter does not travel with the repository. Otherwise, other users without the necessary configuration would have problems.

Step 3: Set filter in local configuration

In the local repository configuration (.git/config), we define a new filter driver called secrets using subsection syntax. It will have both options, clean and smudge, and we mark this filter as required so that Git informs us if it fails rather than continuing silently:

# Dans .git/config (configuration locale)
[filter "secrets"]
    clean = ./scripts/clean-secrets.sh
    smudge = ./scripts/smudge-secrets.sh
    required = true
  • The clean filter will use a script to search for secrets and replace them with safe placeholders to push to a centralized repository.
  • The smudge filter will do the exact opposite, replacing these placeholders with the real secret so that it can be used.

Most projects use skip files for this sort of thing, but it’s not always an option. Git filters give you the flexibility to keep a file in the repository while making its contents safe.

Step 4: Define Scripts

The last step is to define the script that cleans or applies the smudge. For this example, we use sed to quickly replace values.

Clean script (replace secrets with placeholders):

#!/bin/bash
# clean-secrets.sh
# Remplace les valeurs d'API par des placeholders
sed 's/API_KEY=.*/API_KEY=PLACEHOLDER/g' \
    | sed 's/DATABASE_URL=.*/DATABASE_URL=PLACEHOLDER/g' \
    | sed 's/THIRD_PARTY_TOKEN=.*/THIRD_PARTY_TOKEN=PLACEHOLDER/g'

Smudge script (replace placeholders with real secrets):

#!/bin/bash
# smudge-secrets.sh
# Remplace les placeholders par les vraies valeurs depuis un gestionnaire de mots de passe
sed "s/API_KEY=PLACEHOLDER/API_KEY=$(password-manager get API_KEY)/g"

Important: Filters can be very complex scripts or binaries compiled or interpreted in any language. If defined outside of the attributes file, the program must be executable and must be in a path where your shell looks for binaries. Your password manager may have a command line tool that can help.

Result

With everything in place, when we commit the .env file, Git runs the clean script and stores the version with placeholders. When we consult the file on GitHub (or any other service), we see:

API_KEY=PLACEHOLDER
DATABASE_URL=PLACEHOLDER
THIRD_PARTY_TOKEN=PLACEHOLDER

When checking out (checking out the repository), Git runs the smudge script and replaces the placeholders with the real values, allowing normal local use.

Additional resource

To see recommended attributes used with a variety of languages ​​and frameworks, there is a GitHub repository that collects attribute files: github/gitattributes.


4. Improve Git workflows

4.1 Make status easily visible

Git as background tool

For many developers, Git is an integral part of their daily workflow. It is a great tool that helps solve many common problems that can arise during the software development process. However, Git is meant to be a background tool. Every interaction we have with Git is a chore that distracts us from what we’re really trying to accomplish.

That’s why in this module, we’re going to focus on ways to streamline our Git experience. We will start by making it effortless to consult the current status of a repository. Next, we will configure aliases to simplify and automate common tasks. Finally, we will see opinionated recommendations for your configuration and the next steps.

Git status: your compass

The result of git status tells us a lot about the status of our project in a small space. Even without reading everything, the output is a visual indication of the current state of the project:

  • Are there many files waiting to be committed or few?
  • Are these modified existing files or new untracked files?
  • Have we already staged anything for the commit?
git status

Example output:

On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
        modified:   src/app.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        config/new-settings.json

This information is needed so often that it is common to have the git status command aliased in the shell with the single letter g for easy access.

Git status in IDEs

Many IDEs, including VS Code, keep Git status information permanently visible in the status bar at the bottom, in the Source Control Panel, and in File Explorer.

Git status in shell prompt

There is another interesting place to store status information: the shell prompt. Similar to the full git status output, this area of ​​the shell prompt gives a quick visual indication of the status:

  • The current branch
  • Modified or staged files
  • The number of commits ahead or behind the remote copy

ZSH plugin: gitstatusd / powerlevel10k

For ZSH in a Unix-like environment, a particular plugin keeps the output dense to save space, but is also highly configurable. You can install it directly or simply activate it if you use a framework like Oh My ZSH, where it is provided as a bundle.

# Activer le plugin git dans Oh My ZSH (~/.zshrc)
plugins=(git)

This also works in Windows Subsystem for Linux (WSL).

Ports for Bash and Fish

For Bash or Fish in a Unix-like environment, a port of the same plugin is available. Installation requires a quick download and adding a few lines to the shell profile.

PowerShell with posh-git

In non-Unix environments like Windows, you don’t have to do without this functionality. Although the legacy command line terminal does not support custom prompts like this, PowerShell does. Git has an appendix in its documentation that describes how to integrate Git with PowerShell, including installing the posh-git plugin for status information.

# Installer posh-git
Install-Module posh-git -Scope CurrentUser
Import-Module posh-git

Customizing the prompt

Like configuration, shell prompts are very personal. If you choose to integrate a Git status plugin into your shell, it is strongly encouraged to explore the available configuration options. For example :

  • Keep prompt as small as possible with Git status discretely hidden for directories that are not Git repositories
  • Separate status indicators with spaces instead of pipes or other characters for better readability

The goal is to find a way for Git to communicate information quickly when we need it, and to fade into the background when we don’t need it.


4.2 Using Git aliases

Why aliases?

Although text editors and IDEs like to give us graphical ways to interact with Git, many developers find themselves working on the command line at some point in a project’s lifecycle. There, you have to type commands — often including flags — and frequently. Aliases help reduce typing and simplify each step.

There are two main types of aliases to consider.

Type 1: Shell aliases

Shell aliases allow you to type something other than git followed by a subcommand name. For example :

  • ggit status
  • gagit add
  • gcgit commit

These aliases are configured in the shell profile, like a .zshrc file or a Bash profile. ZSH even has a plugin that defines common aliases for Git.

# Dans ~/.zshrc
alias g="git status"
alias ga="git add"
alias gc="git commit"
alias gp="git push"
alias gl="git log --oneline --graph"

Advantages of shell aliases:

  • Allow entire Git commands to be reduced to a few letters
  • Beyond the pure speed of interacting with Git, this has long-term benefits for the hands by reducing the risk of repetitive stress injuries (repetitive stress injuries)

Type 2: Git aliases

Git aliases are defined in the Git configuration and help simplify the way Git subcommands are called. We create an alias by adding an entry in the [alias] section of the configuration.

Example: create a stash-all alias

In the user-specific global configuration, we create a new subsection called alias. The name of the configuration option will be what you type on the command line after git:

# Dans ~/.gitconfig
[alias]
    stash-all = stash --include-untracked

Think of the alias subsection of your configuration as a “find and replace” for Git commands. When typing git stash-all, this will replace the stash-all part of the command with the alias value. We can now quickly stash all modified files:

git stash-all
# Équivalent à : git stash --include-untracked

This is much easier to remember and much quicker to type than the full git stash command including the necessary flag.

If you look for recommended Git aliases online, you’ll find many that might be useful to your workflow. Here are some examples:

Alias ​​to undo a commit:

[alias]
    undo = reset --soft HEAD~1

Alias ​​for a graphic log:

[alias]
    lg = log --oneline --graph --decorate --all

Alias ​​to unindex files (unstage):

[alias]
    unstage = restore --staged

Aliases for branch deployment:

[alias]
    # Pousser le travail actuel vers la branche de staging distante
    push-staging = push origin HEAD:staging

    # Réinitialiser la branche staging avec ce qui est sur le remote main
    reset-staging = "!f() { git fetch origin; git push origin origin/main:staging --force; }; f"

These dense commands are easy to mistype with potentially negative consequences. In general, if you find yourself having trouble remembering or typing a long command, a Git alias might be a good addition to your setup.

Advanced Git aliases with shell functions

All examples seen so far were single command aliases that worked on parts of the command coming after git. It is also possible to execute arbitrary code by prefixing the alias with an exclamation point !. Large and complex interactions can be defined by creating a shell function and immediately invoking it in the alias:

[alias]
    # Alias avancé avec fonction shell
    publish = "!f() { git push origin HEAD:$1 --force; }; f"

    # Créer une branche depuis main et pousser
    feature = "!f() { git checkout -b feature/$1 main && git push -u origin HEAD; }; f"

As with status visibility, using aliases is about letting Git fade into the background while you do your work.


4.3 Next steps

What you learned

Throughout this course, we’ve talked about ways to change Git’s behavior to better fit the workflow:

  • Configuration: with hundreds of options and several levels to meet our needs
  • Attributes: with their ability to process files differently
  • Workflow: including how to check status and type commands

The best thing to do now is to spend time discovering which configurations and attributes can best improve your workflow. This could include:

  1. Talk to your team and decide on common best practices
  2. For each interaction with Git, spend some time thinking about whether it could be improved:
  • Is this a common command that requires a lot of typing?
  • Do I always use the same flags for this command, which I could replace with a configuration?
  • Are there any errors that constantly need to be fixed, such as a default branch name during git init?

Here are some common configuration options that are particularly useful in everyday life:

Default branch during git init

When creating a new repository with git init, Git chooses a name for the default branch. This name is configurable:

git config --global init.defaultBranch main

Some teams prefer dev or trunk. This option allows you to standardize this behavior.

[init]
    defaultBranch = main
Autocorrect incorrectly typed commands

If you type a command incorrectly, Git can try to guess which command you wanted to type and execute it with an optional delay:

git config --global help.autocorrect 10

The value 10 represents a delay of 1 second (in tenths of a second). With 0, Git executes immediately. It is recommended to put this in the global configuration.

[help]
    autocorrect = 10
Sorting and displaying branches

When displaying a list of branches, you can change the order in which they are sorted. You can also deactivate the pager so that the output remains visible in the terminal:

git config --global branch.sort -committerdate
git config --global pager.branch false
[branch]
    sort = -committerdate

[pager]
    branch = false
Preferred editor for commit messages

Writing complete commit messages is a great way to help your team and yourself in the future. It’s much easier to do this when you don’t include a message in the call to git commit and instead use the editor to enter it:

git config --global core.editor vim
# ou
git config --global core.editor "code --wait"  # VS Code
# ou
git config --global core.editor nano
[core]
    editor = vim
Default verbose commit

Another way to improve commit messages is to make it easier to see what has changed in the message editor. We prefer to use the default verbose commit option, which is also configurable:

git config --global commit.verbose true

With this option, git commit will display the changes in the editor below the commit message, allowing you to see exactly what you are committing as you write the message.

[commit]
    verbose = true
Automatic upstream push

When pushing a new branch to a remote repository, it is common to need the -u or --set-upstream flag. This is a frequent source of irritation. The push.autoSetupRemote option does this automatically:

git config --global push.autoSetupRemote true

So git push without flags works even for brand new branches.

[push]
    autoSetupRemote = true
Automatic rebase when pulling

When you pull a branch that has new commits, you can ask Git to automatically rebase local changes on top of it:

git config --global pull.rebase true

Warning: This option is potentially dangerous because it could make it difficult to push changes later. Use it wisely.

[pull]
    rebase = true
Automatic stash around rebase

Whether it’s an automatic rebase on pull or a manual rebase, having to first stash all your changes can be irritating. The rebase.autoStash option tells Git to stash and re-stash the changes around the operation:

git config --global rebase.autoStash true
[rebase]
    autoStash = true

Example of complete .gitconfig file

Here is an example of a complete global configuration file incorporating all the recommendations of this course:

[user]
    name = Votre Nom
    email = votre@email.com

[init]
    defaultBranch = main

[help]
    autocorrect = 10

[core]
    editor = vim

[commit]
    verbose = true

[push]
    autoSetupRemote = true

[pull]
    rebase = true

[rebase]
    autoStash = true

[branch]
    sort = -committerdate

[pager]
    branch = false

[alias]
    stash-all = stash --include-untracked
    undo = reset --soft HEAD~1
    lg = log --oneline --graph --decorate --all
    unstage = restore --staged

Conclusion

All of these recommendations are based on experiences with Git that mostly revolve around centralized repositories on GitHub and small teams of developers. Using what you have learned in this course, assess your own needs and pain points, and look for the setup and attributes that will make your life easier.

Good luck!


5. General Summary

Key git config commands

OrderDescription
git config --global user.name "Name"Set username globally
git config --global user.email "email"Set email globally
git config --system core.editor vimSet publisher at system level
git config --local user.email "pro@work.com"Define a repository specific email
git config --listShow all active configuration
git config --list --show-originShow configuration with source file
git config --list --show-scopeShow configuration with scope
git config --global --unset option.nameRemove a configuration option
git config --global --remove-section sectionDelete an entire section

Common Git attributes

AttributeDescription
textControls automatic line ending management
-textDisables handling of line endings (for binary files)
diffControls the generation of text diffs
-diffDisables text diffs (binary files)
binaryMacro equivalent to -text -diff
diff=nameUses a custom diff driver
filter=nameApply a clean/smudge filter
eol=crlfForce Windows Line Ends (CRLF)
eol=lfForce Unix line endings (LF)

Priority hierarchy (from highest to lowest)

Attributs locaux (.git/info/attributes)
    ↓
.gitattributes (le plus proche du fichier)
    ↓
.gitattributes (à la racine du dépôt)
    ↓
Attributs utilisateur (core.attributesFile)
    ↓
Attributs système
    ↓
Comportement par défaut de Git

Configuration hierarchy (strongest to weakest)

Variables d'environnement (GIT_*)
    ↓
Configuration locale (.git/config)
    ↓
Configuration globale (~/.gitconfig)
    ↓
Configuration système (/etc/gitconfig)
    ↓
Valeurs par défaut de Git


Search Terms

git · configuration · attributes · ci/cd · devops · aliases · filter · gitattributes · status · attribute · automatic · binary · define · filters · hierarchy · options · recommended · shell · show · command · commands · commit · config · default

Interested in this course?

Contact us to book it or get a custom training plan for your team.