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
      updateRefs = 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”.
      • updateRefs = true When you have multiple stacked branches, it can become complex to handle the rebasing process manually for each branch, especially if you have many commits in each branch. Setting updateRefs to true simplifies this process. By enabling this option, whenever you perform a rebase operation on one branch, the references to the other dependent branches are automatically updated as well. This ensures that each branch in the stack remains properly aligned with the others, without requiring you to manually update or rebase each one individually.
      • 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)