Git: Suppressing Irrelevant Git Diff Output for Specific Files (e.g., binary files, encrypted files…)

Sometimes, it is useful to suppress certain files from git diff output, especially when the files are large, machine-generated, or not intended for human reading. Typical examples include encrypted files such as *.gpg and *.asc, as well as binary assets like images, audio files, and similar media. These files are usually represented as opaque binary data in diffs, providing no useful information and adding extraneous clutter to git diff output.

Git offers a solution for this through adding YOUR-FILE-PATTERN -diff -text to the .gitattributes file.

Solution: A .gitattributes file that is local to the repository

The .gitattributes file can be defined locally within each individual repository.

For example, to prevent git diff from displaying diffs for files with the .asc and .gpg extensions, include the following lines in the .gitattributes file at the root of your Git repository:

*.asc -diff -text
*.gpg -diff -textCode language: plaintext (plaintext)

Using -diff -text in .gitattributes is beneficial for binary or non-human-readable files because it ensures Git neither attempts to generate textual diffs (-diff) nor applies any text-related processing like end-of-line normalization (-text).

This combination prevents irrelevant or misleading changes from appearing in diffs, avoids potential corruption from automatic text conversions, and keeps version control output clean and focused on meaningful, human-readable changes.

Alternative solution: A global .gitattributes_global file

Rather than adding these rules to every repository individually, you can define them once in a global ~/.gitattributes_global file. This file applies to all Git repositories for your user account unless overridden by a repository-specific .gitattributes.

To set up a global .gitattributes_global file:

Configure Git to use it:

git config --global core.attributesfile ~/.gitattributes_globalCode language: plaintext (plaintext)

Add global rules to the ~/.gitattributes_global file. For example:

*.asc -diff -text -diff
*.gpg -diff -text -diffCode language: plaintext (plaintext)

This setup ensures consistent handling of non-human-readable files across all repositories without the need for redundant configuration.

Conclusion

Suppressing diffs for non-human-readable files with .gitattributes, whether configured locally or globally, reduces noise in version control workflows. This keeps Git diff output focused on meaningful textual modifications, prevents clutter from binary content, and safeguards against unwanted transformations.

Related links

jc-dotfiles – A collection of configuration files for UNIX/Linux systems

The jamescherti/jc-dotfiles repository is a collection of configuration files. You can either install them directly or use them as inspiration your own dotfiles. These dotfiles provide configurations for various tools, enhancing productivity and usability. This article will explore the contents of the jc-dotfiles repository, highlight the main configurations, and guide you through the installation process.

In addition to the jc-dotfiles repository, the author also recommends the following repositories:

  • jamescherti/jc-firefox-settings: Provides the user.js file, which holds settings to customize the Firefox web browser to enhance the user experience and security.
  • jamescherti/jc-xfce-settings: A script that can customize the XFCE desktop environment programmatically, including window management, notifications, keyboard settings, and more, to enhance the user experience.
  • jamescherti/jc-gnome-settings: A script that can customize the GNOME desktop environment programmatically, including window management, notifications, and more.
  • jamescherti/bash-stdops: A collection of Bash helper shell scripts.
  • jamescherti/jc-gentoo-portage: Provides configuration files for customizing Gentoo Linux Portage, including package management, USE flags, and system-wide settings.
  • jamescherti/minimal-emacs.d: A lightweight and optimized Emacs base (init.el and early-init.el) that gives you full control over your configuration. It provides better defaults, an optimized startup, and a clean foundation for building your own vanilla Emacs setup.
  • jamescherti/jc-vimrc: The jc-vimrc project is a customizable Vim base that provides better Vim defaults, intended to serve as a solid foundation for a Vim configuration.

The jc-dotfiles Repository Overview

The repository includes configurations for several components of a UNIX/Linux system, ranging from shell settings to terminal multiplexer and input customizations. Here’s a breakdown of the key configurations included:

1. Shell Configuration (.bashrc, .profile, .bash_profile)

The shell configuration files are optimized for efficient Bash shell settings:

  • Efficient Command Execution: Predefined aliases, functions, and environment variables to speed up common tasks.
  • Interactive Sessions: Custom prompt configurations and other interactive settings that improve the overall shell experience.

2. Terminal Multiplexer Configuration (.tmux.conf)

Tmux is a tool for managing multiple terminal sessions, and this configuration enhances your Tmux setup. It includes:

  • Session Management: Custom keybindings and layout configurations for improved session switching.
  • Productivity Boosters: Enhanced usability and efficiency for handling multiple tasks within terminal windows.

3. Readline Configuration (.inputrc)

The Readline configuration improves interactive shell input by allowing additional keybindings:

  • Custom Keybindings: Use Alt-h, Alt-j, Alt-k, and Alt-l for cursor movement, enabling Vim-style HJKL navigation within the terminal.

4. Other Configurations

The repository also includes a variety of other configuration files and scripts:

  • .gitconfig: Custom Git settings to improve version control workflow. (Here is an article about .gitconfig: Optimized Git configuration ~/.gitconfig for performance and usability)
  • ranger: File manager configuration for enhanced navigation and file handling.
  • .fdignore: Ignore rules for fd, the fast search tool, to filter out unwanted files during searches.
  • .wcalcrc: Configuration for wcal, a command-line calculator.
  • mpv: Media player settings for a better viewing experience.
  • picom: Window compositor configuration for optimized display and transparency effects.
  • feh: Image viewer configuration with useful options and custom keybindings.

Conclusion

The jamescherti/jc-dotfiles configuration scripts can enhance your workflow. Whether you install them directly or use them as inspiration for your own dotfiles, you’ll gain access to optimized settings for your shell, terminal, Git, and various other tools, all aimed at boosting productivity and efficiency.

Enhancing Git configuration ~/.gitconfig for performance, efficiency, data integrity, and workflow automation

Optimizing your Git configuration is fundamental to enhancing efficiency and productivity in version control workflows. A finely-tuned Git setup not only accelerates operations but also aligns the tool with the specific requirements of both individual developers and teams.

In this article, we will explore Git configurations that enhance both performance and usability.

All the configurations mentioned in this article should be added to the ~/.gitconfig file. This file follows the INI format, with [sections] defining categories and variables=values specifying settings.

Performance tuning

[pack]
threads = 0
windowMemory = 5g
packSizeLimit = 2g
Code language: plaintext (plaintext)
  • threads = 0 Configures Git to auto-detect the number of CPUs and set the number of threads accordingly.
  • windowMemory = 5g This enhances Git’s efficiency for large repositories. It tells Git to use up to 5GB of RAM when creating packfiles. (A packfile is a way Git stores changes between versions of files efficiently.) By allocating more memory, Git can compare and store these changes more effectively, which can make the packing process faster.
  • packSizeLimit = 2g The packSizeLimit configuration in Git determines the maximum size of a packfile that Git will generate.

Garbage collection

[gc]
auto = 8000
  • auto = 8000 Automatically triggers garbage collection when the number of loose objects exceeds 8000, optimizing repository storage and performance. The default value is 6700.

Core configurations

[core]
whitespace = space-before-tab,trailing-space
preloadindex = true
Code language: plaintext (plaintext)
  • whitespace = space-before-tab,trailing-space Configures whitespace handling.
  • preloadindex = true Preloads the index into memory for improved performance when running Git commands like status and diff. Setting this to true will make Git load the index into memory early in the execution of commands. This can lead to performance improvements for operations that involve the index, especially in repositories with a large number of files..

Rebase

[rebase]
autoStash = true
missingCommitsCheck = warn
autoSquash = true
missingCommitsCheck = errorCode language: plaintext (plaintext)
  • autoStash = true Before executing a rebase, automatically stash all uncommitted change. This ensures the rebase proceeds without interference from local modifications. Once the rebase completes, the stashed changes are reapplied, restoring the developer’s original working state.
  • missingCommitsCheck = warn If commits are removed or missing during a rebase, issue a warning rather than # silently ignoring them.
  • autoSquash = true When you use interactive rebase (e.g., git rebase -i), this setting makes Git automatically recognize and handle commits that begin with “fixup!” or “squash!”. A commit with “squash!” means “combine this with another commit and keep both messages”. A commit with “fixup!” means “combine this with another commit and discard this message”.
  • missingCommitsCheck = error If commits are removed or missing during a rebase, issue an error rather than silently ignoring them.

Merge

[merge]
autoStash = true
ff = only
conflictStyle = zdiff3
summary = true
diffstat = true
log = 60Code language: plaintext (plaintext)
  • autoStash = true Before executing a merge, automatically stash all uncommitted changes in the working directory and index. This ensures the merge proceeds without interference from local modifications. Once the merge completes, the stashed changes are reapplied, restoring the developer’s original working state.
  • ff = only This setting enforce fast-forward merges for merge, preventing unnecessary merge commits and maintaining a linear commit history.
  • conflictStyle = zdiff3 Uses the zdiff3 conflict style for merges, which includes additional context by showing the base, local, and remote versions of conflicting changes in a more readable format, making it easier to resolve complex conflicts.
  • summary = true Display a brief summary describing what was merged. This includes information such as the merged branch names and the number of commits.
  • diffstat = true Display a diffstat after the merge completes, which summarizes the changes made, showing how many lines were added and removed in each file.
  • log = 60 Include up to 60 commit messages from the merged branch in the merge commit message. This provides historical context and helps reviewers understand the scope of the merge.

Pull

[pull]
ff = only
default = currentCode language: plaintext (plaintext)
  • ff = only This setting enforce fast-forward merges for pull, preventing unnecessary merge commits and maintaining a linear commit history.
  • default = current This setting automatically pulls the current branch, which helps prevent “There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details. git pull “

Push behavior

[push]
default = current
autoSetupRemote = true
followTags = true
Code language: plaintext (plaintext)
  • default = current This setting automatically pushes the current branch, which helps prevent the error: “fatal: The current branch BRANCH has no upstream branch.” When this setting is in place, Git will push the current branch to the remote, assuming the branch has an upstream set.
  • autoSetupRemote = true This setting automatically sets up remote tracking for new branches, eliminating the need to manually run git branch --set-upstream-to after creating a new branch. It automates the process of linking local branches to their corresponding remote branches. This is useful for users who frequently create new branches, as it reduces the need for repetitive configuration.
  • followTags = true Push all annotated tags to the remote repository along with the branch # updates. This ensures that tags created locally are also synchronized with the # remote repository during a push operation.

Enhanced diffing

[diff]
tool = vimdiff
algorithm = histogram
renames = copies
renameLimit = 2400
context = 5Code language: plaintext (plaintext)
  • tool = vimdiff This setting uses vimdiff as the default diff tool when git difftool is executed.
  • algorithm = histogram Diff algorithm for improved diff accuracy, especially in detecting moved code blocks.
  • renames = copies Configures Git to detect file copies in addition to renames during diff generation. This helps identify files that were copied, improving the accuracy of the diff output for changes involving file copying.
  • renameLimit = 2400 Sets the maximum number of renames to detect in a diff. When there are more than this number of renames, Git will stop trying to detect renames to avoid performance degradation. The default is 1000, but increasing it can help in repos with many file renames.
  • context = 5 Show 5 lines of context around changes in diffs instead of the default 3.

Rerere (Reuse Recorded Resolution)

[rerere]
enabled = 1
autoUpdate = trueCode language: plaintext (plaintext)
  • enabled = 1 Enables automatic resolution of previously encountered merge conflicts, reducing the effort required for repeated merges.
  • autoUpdate = true After a conflict is resolved manually, Git will automatically store the resolution in the rerere cache, making it available for future use.

Commit

[commit]
verbose = trueCode language: plaintext (plaintext)
  • verbose = true This includes the full commit diff in the text editor when composing the commit message, aiding in recalling the context and purpose of the changes.

Git log dates

[log]
date = isoCode language: plaintext (plaintext)
  • date = iso Display dates as YYYY-MM-DD HH:MM:DD (e.g., 2025-03-25 14:59:12 -0400)

Data integrity (Fsck objects)

[transfer]
fsckObjects = true

[receive]
fsckObjects = true

[fetch]
fsckObjects = true
prune = true
pruneTags = trueCode language: plaintext (plaintext)
  • [transfer|receive|fetch] fsckObjects = true Verifies the integrity of all objects during data transfer operations like clone, fetch, and receive (data transfer), helping detect corruption or tampering early.
  • [fetch] prune = true Automatically remove remote-tracking branches that no longer exist on the remote repository during a ‘git fetch’. This helps keep the local repository clean by removing outdated references to remote branches that have been # deleted.
  • [fetch] pruneTags = true Automatically delete remote tags in your local repository that have been removed from the remote repository during a ‘git fetch’. This ensures that your local tags list is up-to-date and prevents the accumulation of tags that no longer exist on the remote.

Branch

[branch]
sort = -committerdate
autoSetupMerge = trueCode language: plaintext (plaintext)
  • sort = -committerdate Configures git branch to sort branches by most recently used rather than alphabetically, making it easier to locate active branches.
  • autoSetupMerge = true Automatically sets up an upstream tracking branch when creating a new branch from a remote-tracking branch This allows ‘git pull’ and ‘git push’ to work without specifying the remote/branch.

Tag

[tag]
sort = taggerdateCode language: plaintext (plaintext)
  • sort = taggerdate Sort tags by the date they were created.

Help

[help]
autoCorrect = promptCode language: plaintext (plaintext)
  • autoCorrect = prompt When a mistyped Git command closely resembles a valid one, this setting prompts the user with a suggestion before executing the corrected command.

Aliases

Here are aliases that abbreviate common Git commands:

[alias]
# Pull with rebase and automatically stash local changes before the operation
up = pull --rebase --autostash

# Show details of the most recent commit or a specified object
s = show

# Commit all tracked changes
cia = commit -a

# Continue a rebase after resolving conflicts
rc = rebase --continue

# Amend the most recent commit with staged changes
amend = commit --amend

# Amend the most recent commit without changing the commit message
commend = commit --amend --no-edit

# Push commits to the remote repository
p = push

# Force push to the remote repository, overwriting history
pf = push --force

# Stage a specific file or files for commit
a = add

# Stage all changes (including deletions) in the working directory
aa = add -A

# Show a short summary of the working directory status
st = status -s

# Switch branches or restore working tree files
co = checkout

# List, create, or delete branches
b = branch

# Discard all local changes and reset to the latest commit on the current branch
rh = reset --hard HEAD

# Fetch and integrate changes from the remote repository
pl = pull

# Show changes in the working directory relative to the last commit
d = diff HEAD

# Show staged changes that will be included in the next commit
dc = diff --cached

# Launch the configured diff tool to view changes
dt = difftool

# Show word-by-word diff of the working directory against the last commit
dw = diff --color-words HEAD

# Show word-by-word details of the most recent commit
sw = show --color-words

# Display the commit history
l = log

# Show commit history with GPG signature verification and file change summary
logs = log --show-signature --stat

# Show full commit history with diffs
lp = log -p

# Apply changes introduced by existing commits
chp = cherry-pick

# Abort an in-progress cherry-pick operation
chpa = cherry-pick --abort

# Continue an in-progress cherry-pick after resolving conflicts
chpc = cherry-pick --continue

# Quit an in-progress cherry-pick without committing
chpq = cherry-pick --quit

# Skip the current commit during an in-progress cherry-pick
chps = cherry-pick --skipCode language: Bash (bash)

Conclusion

This Git configuration enhances usability, improves performance, and simplifies repository management through thoughtful settings and powerful aliases. The author’s .gitconfig file is available in the jc-dotfiles repository.

Ansible: Installing and configuring Gitolite using Ansible for secure Git repository management

EDIT: The latest version of the code is available in the repository:
jamescherti/ansible-role-gitolite

Gitolite provides a way to manage Git repositories, control access to those repositories, and maintain a central configuration using simple configuration files and SSH keys.

Automating Gitolite Installation with Ansible

The Ansible tasks outlined in this article are designed to simplify the installation and configuration of Gitolite on your server. These tasks can automatically handle the entire setup process, including prerequisites like installing necessary packages and configuring system users and groups.

This automation significantly reduces the risk of human error and ensures a consistent setup across different environments.

The Ansible tasks:

---
# Automating Gitolite Installation with Ansible
# License: MIT
# Author: James Cherti
# URL: https://www.jamescherti.com/ansible-install-gitolite-linux/

- name: Install Gitolite
  block:
    - name: Check if the Operating System is supported
      fail:
        msg: "Operating System family is not supported: {{ ansible_os_family }}"
      when: ansible_os_family not in ["Debian", "RedHat"]

    - name: Install Gitolite on Debian-based Systems
      apt:
        name: gitolite3
      when: ansible_os_family == "Debian"

    - name: Install Gitolite on RedHat-based Systems
      yum: name=gitolite3
      when: ansible_os_family == "RedHat"

    - name: Create Gitolite system group
      group:
        name: "{{ gitolite_group }}"
        system: true

    - name: Create Gitolite system user
      user:
        name: "{{ gitolite_user }}"
        group: "{{ gitolite_group }}"
        home: "{{ gitolite_home }}"
        shell: "{{ gitolite_shell }}"
        create_home: true
        system: true

    - name: Ensure Gitolite home directory exists with proper permissions
      file:
        state: directory
        path: "{{ gitolite_home }}"
        owner: "{{ gitolite_user }}"
        group: "{{ gitolite_group }}"
        mode: 0700

- name: Configure Gitolite SSH key
  block:
    - name: Generate Gitolite SSH key pair if it does not exist
      become: true
      become_user: "{{ gitolite_user }}"
      command: ssh-keygen -t rsa -b 4096 -f {{ gitolite_ssh_key_path | quote }} -N ""
      args:
        creates: "{{ gitolite_ssh_key_path }}"

    - name: Set permissions for the Gitolite .ssh directory
      file:
        path: "{{ gitolite_ssh_directory }}"
        owner: "{{ gitolite_user }}"
        group: "{{ gitolite_user }}"
        mode: 0700

    - name: Set permissions for the SSH public key
      file:
        path: "{{ gitolite_ssh_key_path }}.pub"
        owner: "{{ gitolite_user }}"
        group: "{{ gitolite_user }}"
        mode: 0644

    - name: Set permissions for the SSH private key
      file:
        path: "{{ gitolite_ssh_key_path }}"
        owner: "{{ gitolite_user }}"
        group: "{{ gitolite_user }}"
        mode: 0600

- name: Setup Gitolite
  block:
    - name: Initialize Gitolite with the admin public key
      become: true
      become_user: "{{ gitolite_user }}"
      command:
        argv:
          - "gitolite"
          - "setup"
          - "-pk"
          - "{{ gitolite_ssh_public_key_path }}"
      args:
        creates: /var/lib/gitolite/repositories/gitolite-admin.gitCode language: YAML (yaml)

The required Ansible variables:

---
# Automating Gitolite Installation with Ansible
# License: MIT
# Author: James Cherti
# URL: https://www.jamescherti.com/ansible-install-gitolite-linux/

gitolite_user: gitolite
gitolite_group: gitolite
gitolite_shell: /bin/bash
gitolite_home: "/var/lib/{{ gitolite_user }}"
gitolite_ssh_directory: "{{ gitolite_home }}/.ssh"
gitolite_ssh_key_path: "{{ gitolite_ssh_directory }}/id_rsa"
gitolite_ssh_public_key_path: "{{ gitolite_ssh_directory }}/id_rsa.pub"Code language: YAML (yaml)

Related links

A Git Tool that can decide whether to use ‘git mv’ or ‘mv’ to move files and/or directories

The git-smartmv command-line tool, written by James Cherti, allows moving files and/or directories without having to worry about manually choosing whether to use mv or git mv.

  • If the file or directory is being moved within the same Git repository, git-smartmv uses git mv.
  • If the file or directory is being moved between a Git repository and a non-Git directory or a different Git repository, git-smartmv uses mv.

Installation

sudo pip install git-smartmvCode language: plaintext (plaintext)

Shell alias

To simplify the usage of this tool, you can add the following line to your ~/.bashrc:

alias mv="git-smartmv"Code language: plaintext (plaintext)

Usage

The git-smartmv command-line tool accepts similar arguments as the mv command, including the source file or directory to be moved, and the destination file or directory.

Example:

git smartmv file1 file2 directory/

Second example (rename):

git smartmv file1 file2

Links related to git-smartmv

Gentoo: How to Speed Up emerge ‐‐sync

Synchronizing with the Gentoo Portage ebuild repository using emerge --sync can be slow when utilizing the rsync protocol. However, an effective solution exists that can greatly improve the synchronization speed: Configuring emerge --sync to synchronize using Git instead.

In this article, we will explore how to set up emerge to synchronize from the official Gentoo ebuild Git repository and save valuable time during the synchronizing process.

Step 1: Install Git using the following command:

sudo emerge -a dev-vcs/gitCode language: plaintext (plaintext)

Step 2: Remove any file from the directory /etc/portage/repos.conf/ that configures the emerge command to use rsync.

Step 3: Create the file /etc/portage/repos.conf/gentoo.conf containing:

[DEFAULT]
main-repo = gentoo

[gentoo]

# The sync-depth=1 option speeds up initial pull by fetching 
# only the latest Git commit and its immediate ancestors, 
# reducing the amount of downloaded Git history.
sync-depth = 1

sync-type = git
auto-sync = yes
location = /var/db/repos/gentoo
sync-git-verify-commit-signature = yes
sync-openpgp-key-path = /usr/share/openpgp-keys/gentoo-release.asc
sync-uri = https://github.com/gentoo-mirror/gentoo.gitCode language: plaintext (plaintext)

Step 4: Finally, run the following command to synchronize with the Gentoo ebuild repository using Git:

sudo emerge --sync

The initial download of the entire Git repository will cause the first emerge --sync command to take some time. However, subsequent synchronizations will be significantly quicker, taking only a few seconds.

Using Git can be a great way to speed up synchronization with the Gentoo ebuild repository. By following the steps outlined in this article, you can clone the Portage repository to your local machine and keep it up-to-date with the latest changes using Git. This can save you a lot of time when syncing your local repository.

Vim: Edit all the files in the current directory of a Git repository in new tabs (git ls-files)

" Language: Vim script
" Description: edit all the Git files in the current
"              directory in new tabs (git ls-files
" License: MIT
" Author: James Cherti
" URL: https://www.jamescherti.com/vim-edit-git-ls-files-new-tabs/

function! GitEditFiles() abort
  if &modified
    echoerr 'fatal: No write since last change.'
    return
  endif

  let l:list_lines = systemlist('git ls-files')
  if v:shell_error !=# 0
    echomsg 'fatal: Git: ' . join(l:list_lines, "\n")
    return
  endif

  let l:list_files = []
  for l:filename in l:list_lines
    if filereadable(l:filename)
      call add(l:list_files, l:filename)
    endif
  endfor

  if len(l:list_files) ==# 0
    echo 'No Git files were found in the directory ' . getcwd()
    return
  endif

  if len(l:list_files) > 7
    for l:filename in l:list_lines
      echo l:filename
    endfor

    echo "\n"
    echo 'Git directory: ' . getcwd()
    echo 'Number of Git files: ' . len(l:list_files)
    echo "\n"
    let l:answer = input('Edit? [y,n]')
    if l:answer !=# 'y'
      return
    endif
  endif

  let l:first = 1
  for l:file in l:list_files
    if l:first
      let l:first = 0
    else
      execute 'tabnew'
    endif

    execute 'edit ' . fnameescape(l:file)
  endfor
endfunction

command! -nargs=0 GitEditFiles call GitEditFiles()Code language: Vim Script (vim)