Read Anthropic’s case study about Graphite Reviewer

Git merge drivers

Kenny DuMez
Kenny DuMez
Graphite software engineer


Note

This guide explains this concept in vanilla Git. For Graphite documentation, see our CLI docs.


A Git merge driver is a component of Git's version control system that allows developers to customize how file versions are merged when conflicts occur. This guide provides an in-depth look at configuring and using Git merge drivers, exploring both built-in and custom options, to enhance your project's version control strategies.

A Git merge driver is a tool that Git uses to reconcile differences between file versions during a merge. While Git has several built-in merge strategies, merge drivers offer a more granular level of control over the merge process, especially useful in projects with complex merge needs or non-text files.

  • Git merge strategies: These are built-in methods that Git uses to automatically merge files based on their content changes.
  • Git merge drivers: These are custom scripts or commands configured to handle the merge process for specific types of files or in specific scenarios where default strategies are not sufficient.

To configure a custom merge driver in Git, you need to define it in your Git configuration files and then specify how it should be applied to particular files. Here’s how to set it up:

  1. Define the merge driver in .gitconfig:

    Terminal
    [merge "customdriver"]
    name = Custom merge driver for specific file types
    driver = custom-merge-driver %O %A %B %L %P
    recursive = binary

    In this configuration:

  2. [merge "customdriver"]:

    • This line starts the definition of a new merge driver called customdriver. The name inside the brackets, prefixed with merge, tells Git that this is a merge-related configuration. The term customdriver is an identifier used later to link specific file types to this custom merge behavior.
  3. name = Custom merge driver for specific file types:

    • This line provides a descriptive name for the merge driver. It doesn’t impact the functionality directly but serves as a human-readable description to help users understand the purpose of the custom merge driver in the configuration file.
  4. driver = custom-merge-driver %O %A %B %L %P:

    • This line defines the command that Git executes to perform the merge when this driver is invoked. custom-merge-driver is the executable (which needs to be in your path) that Git will call.
    • %O represents the base version of the file, %A is the current branch version, %B is the other branch version, %L provides the conflict marker size, and %P gives the pathname of the file being merged. These placeholders are replaced by Git with actual values during the merge process, allowing the custom merge script or executable to access these inputs and decide how to merge the files.
  5. recursive = binary:

    • This line specifies how the merge driver should handle subdirectories or when the merge involves more than just two versions (for example, during a rebase of multiple commits). Setting it to binary suggests that Git should treat the files as binary during recursive merging, which usually means avoiding line-by-line merging and potentially using a simpler strategy like "take-their" or "take-ours" without attempting to merge the contents intelligently.
  6. Assign the merge driver to specific files in .gitattributes:

    Terminal
    *.ext merge=customdriver

    Replace *.ext with the file extension to which you want your custom merge driver applied.

Your custom merge driver script should be designed to handle three versions of a file (base, current, and incoming) and output a merged result. Here’s a simple example in Bash:

Terminal
#!/bin/bash
# custom-merge-driver.sh
base=$1
local=$2
remote=$3
# Custom merging logic here
# For demonstration, we just choose the local version
cp $local $base
exit 0

This example is designed to handle merging conflicts by automatically favoring the local version of a file over the incoming changes:

  1. base=$1:

    • This line assigns the first argument passed to the script ($1) to the variable base. In the context of a Git merge driver, this base variable represents the base version of the file (the common ancestor in the merge process).
  2. local=$2:

    • This line assigns the second argument ($2) to the variable local. In a merge operation, local refers to the version of the file from the current branch (where the merge command was initiated).
  3. remote=$3:

    • This line assigns the third argument ($3) to the variable remote. This represents the incoming or other branch’s version of the file that is being merged.
  4. cp $local $base:

    • This command copies the local version of the file ($local) over the base file ($base). In many Git setups, $base would be a temporary file location where Git expects the final merged result to be written. By copying $local to $base, this script resolves any merge conflict by favoring the local version, effectively ignoring any changes from the remote branch.
  5. exit 0:

    • This command exits the script with a status code of 0, which indicates to Git (and any other calling process) that the merge has completed successfully. A non-zero exit code would indicate an error or conflict that couldn't be resolved automatically.

This script is a fairly simple example but in a real-world scenario, you might need more sophisticated merging strategies that consider the contents of both the local and remote versions to intelligently combine changes or handle conflicts.

Custom merge drivers in Git provide a powerful way to handle file merges more appropriately according to the project's specific needs, especially when dealing with file types that require special handling or when default merge strategies fail to produce desired results. By carefully implementing and testing your merge drivers, you can enhance your project’s version control processes, reducing the need for manual intervention during merge.

Git inspired
Graphite's CLI and VS Code extension make working with Git effortless.
Learn more

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2