14

When including the line

*.py diff=python

in a local .gitattributes file, git diff produces nice labels for the different diff hunks of Python files (with the name of the function where the changes are, etc.).

Is is possible to ask git to use this diff mode for all Python files across all git projects? I tried to set a global ~/.gitattributes, but it is not used by local git repositories. Is there a more convenient method than initializing each new git project with a ln -s ~/.gitattributes?

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260

3 Answers3

4

Quoting from gitattributes(5):

Attributes that should affect all repositories for a single user should be placed in a file specified by the core.attributesfile configuration option (see git-config(1)). Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/attributes is used instead. Attributes for all users on a system should be placed in the $(prefix)/etc/gitattributes file.

TL;DR: echo '*.py diff=python' >> "${XDG_CONFIG_HOME:-$HOME/.config}"/git/attributes


Update, 7 years later

Ok, it's not necessary to configure diff=python for *.py files — it's the default since long ago.

But the general point remains: anything you can set up in local (per-repository) .gitattributes, you can also make global (per-machine).

There're many good examples in man 5 gitattributes itself, so please go RTFM.

Let's do just one custom setup: --word-diff for all Markdown files (kudos to @RayLuo for suggesting this in comments).

First, we add an external diff driver:

git config --global diff.stackoverflow-word-diff.command ~/.local/bin/stackoverflow-word-diff

The API is such that we must make a standalone wrapper executable.

cat > ~/.local/bin/stackoverflow-word-diff << 'EOF'
#!/bin/bash -eu

#-- uncomment for debug:
#echo >&2 "$(basename $0) args: $@"; set -x

FILENAME="$1"
OLDFILE="$2"
OLDHASH="$3"
OLDMODE="$4"
NEWFILE="$5"
NEWHASH="$6"
NEWMODE="$7"

git diff --no-ext-diff --word-diff "$OLDFILE" "$NEWFILE" || exit 0

#-- from https://stackoverflow.com/a/18948381/531179
#-- see `man 1 git` /EXTERNAL_DIFF, or https://www.git-scm.com/docs/git
EOF
chmod +x ~/.local/bin/stackoverflow-word-diff

Finally, we tie that in to *.md, *.markdown via global gitattributes:

mkdir -vp "${XDG_CONFIG_HOME:-$HOME/.config}"/git

{ echo '*.md diff=stackoverflow-word-diff'; \
  echo '*.markdown diff=stackoverflow-word-diff; \
} \
    >> "${XDG_CONFIG_HOME:-$HOME/.config}"/git/attributes

And that's all folks! Test it.

ulidtko
  • 14,740
  • 10
  • 56
  • 88
  • 1
    This works (with git 1.8.4), thanks! It seems that `*.py diff=python` is actually now the default, though, so the particular case of my original question does not seem to be relevant anymore. It's good to know that there is a user-level attributes file, in any case! – Eric O. Lebigot Sep 23 '13 at 13:30
  • As @EricOLebigot pointed out, this setting does not have a tangible effect, which is probably because that `*.py diff=python` behavior is already the default. So, how, if at all possible, can we further customize the diff options? For example, `git diff --word-diff ... ...` would be an interesting attempt. How can we declare it in our setup, ideally for markdown files? – RayLuo Apr 21 '20 at 08:07
  • Hey @RayLuo, thanks for suggestion! I've added a way to achieve that. Sorry it took so long; StackOverflow has completely lost an almost-ready previous draft of this during a browser restart. Hope that helps! – ulidtko Apr 28 '20 at 11:40
  • @ulidtko You are so kind to spend quite some time to work out a solution for this word diff challenge! Thanks! Your new solution almost works, it can generate the expected `--word-diff` output. However, at the end of the diff result, there is always one line says `fatal: external diff died, stopping at TheFirstDifferentFile.md`. And then my other changed files would not be diff-ed. This even effectively breaks some other git functionality which relies on diff behavior. I'm running bash on a Debian-based Linux. Why does [my attempt](https://github.com/rayluo/home/compare/word-diff) not work? – RayLuo May 03 '20 at 17:27
  • 1
    Ah jeez. @RayLuo I've seen that too, but was unable to figure it out. On second look, it's embarassingly clear: that's my `set -e` backfiring. Just need to add `|| exit 0`... Will update in a minute. – ulidtko May 05 '20 at 11:53
3

To tell git to use ~/.gitattributes you need to put this in ~/.gitconfig:

[core]
  attributesfile = ~/.gitattributes
ku1ik
  • 1,848
  • 22
  • 20
  • +1. Thanks. This is interesting, but doesn't this precludes the use of a local .gitattributes for local settings? I was wondering whether it would be possible to have some attributes common to all projects, that could be customized locally. – Eric O. Lebigot Dec 18 '11 at 20:08
  • 1
    @EricOLebigot This DOES NOT override local settings. Repo-local attributes have precedence over global and system-wide attributes. The precedence of local .gitattributes files depends on their distance from the path in question. See https://git-scm.com/docs/gitattributes – DylanYoung Jul 20 '20 at 18:24
0

No, git only looks for attributes locally: .gitattributes and .git/info/attributes

Oblomov
  • 761
  • 3
  • 10