Setting up a Git external diff driver in a Git worktree
I’m reviewing some code that’s a refactor, where some parts of a C
function are put into a new function, and the old function calls the
new function. I found the default text from git diff
difficult
to read. A tool like meld
can be helpful to see the changes.
I was running meld
“by hand”, by extracting two revisions of
the file from git
and then running meld
on them. I
recalled that git
could be configured to run external diff
drivers. Looking into this, it seemed like I could get git diff
to use meld
on some files.
One wrinkle is that I’m using Git worktrees, which are copies of a Git repository with some shared state. This is a way to work on the same repository, but different branches, in different directory trees.
Setting up an external diff through the configuration files involves creating an external diff tool section with the external command in the git config, globally, locally (the repository), or at the worktree level. Great, I want the worktree. Then, you set the diff attribute for a file or files.
git config
has a --worktree
option to manipulate
configuration in just the current worktree. Useful!
Using git config set --worktree
to set the external diff
configuration hit a snag, as the documentation says to create a
section like this:
[diff "jcdiff"]
command = j-c-diff
I tried to run git config set --worktree 'diff "meld"'.command /usr/bin/meld
, but git
didn’t like it. I eventually gave up
and put it in the config file by hand. But first, I had to figure out
where the config file lived! For a repository, it lives in
.git/config
. But apparently that’s not the case for a worktree.
git
puts some data in the parent repository at
.git/worktrees/worktree
. But which file? I couldn’t find anything,
so I decided to create one with git config set --worktree foo.bar bletch
. I didn’t get a .git/config
in the top of the
worktree. I found it in the parent repo at
.git/worktrees/worktree/config.worktree
. Ah, so now I could add my
external diff driver for meld
in that file.
Interestingly, running git config list --worktree
after all
this gave me
diff.meld.command=/home/davemarq/bin/git-meld
Oh, so I could have used “diff.meld” as the section. And it would
likely have worked with git config set --worktree
. That would
likely be a better way to document this in the gitattributes(5) manual
page.
You may have noticed I’m running /home/davemarq/bin/git-meld
rather than meld
. Why? meld
wants two arguments, the
old file and new file. But git diff
passes seven arguments:
path old-file old-hex old-mode new-file new-hex new-mode
I just want old-file and new-file. git-meld
is a simple shell
script that calls meld with these arguments. The code is very short:
/usr/bin/meld $2 $5
Now I have a external diff driver for meld. But I don’t have a way to
tell git
to use it. That’s the final piece of the puzzle. You
update your git attributes to use the external diff driver for certain
file path patterns. So I added
to the .gitattributes
file in the worktree’s top directory. There was
already a .gitattributes
file there, so I just added this line.
Now, git diff
correctly runs meld
if I diff foo.c,
e.g.
git diff rev1 rev2 foo.c