I use tabs for indentation in my python programs, but I would like to collaborate (using git) with people who use spaces instead.

Is there a way for git to automatically convert between spaces and tabs (say, 4 spaces = 1 tab) on pushing/fetching? (similar to the CR/LF conversion)

Solution 1

Here is the complete solution:

In your repository, add a file .git/info/attributes which contains:

*.py  filter=tabspace

Linux/Unix

Now run the commands:

git config --global filter.tabspace.smudge 'unexpand --tabs=4 --first-only'
git config --global filter.tabspace.clean 'expand --tabs=4 --initial'

OS X

First install coreutils with brew:

brew install coreutils

Now run the commands:

git config --global filter.tabspace.smudge 'gunexpand --tabs=4 --first-only'
git config --global filter.tabspace.clean 'gexpand --tabs=4 --initial'

All systems

You may now check out all the files of your project. You can do that with:

git checkout HEAD -- **

and all the python files will now have tabs instead of spaces.

Edit: changed the forced checkout command. You should commit your work first, of course.

Solution 2

Yes, one potential solution is to use a git attribute filter driver (see also GitPro book), to define a smudge/clean mechanism.

That way:

  • each time you checkout some files of your repo, spaces can be converted in tabs,
  • but when you check-in (and push and publish), those same files are stored back using only spaces.

You can declare this filter driver (named here 'tabspace') in the .git/info/attributes (for a filter applied to all files within the Git repo), with the following content:

*.py  filter=tabspace

Now run the commands:

# local config for the current repo
git config filter.tabspace.smudge 'script_to_make_tabs'
git config filter.tabspace.clean 'script_to_make_spaces'

See Olivier's answer for a concrete working example of such a smudge/clean set of instructions.

Solution 3

Very useful info for everyone using GitHub (or other similar service)

~/.gitconfig

[filter "tabspace"]
    smudge = unexpand --tabs=4 --first-only
    clean = expand --tabs=4 --initial
[filter "tabspace2"]
    smudge = unexpand --tabs=2 --first-only
    clean = expand --tabs=2 --initial

Then I have two files: attributes

*.js  filter=tabspace
*.html  filter=tabspace
*.css  filter=tabspace
*.json  filter=tabspace

and attributes2

*.js  filter=tabspace2
*.html  filter=tabspace2
*.css  filter=tabspace2
*.json  filter=tabspace2

Working on personal projects

mkdir project
cd project
git init
cp ~/path/to/attributes .git/info/

That way, when you finally push your work on github, it won't look silly in the code view with 8 space tabs which is default behavior in all browsers.

Contributing to other projects

mkdir project
cd project
git init
cp ~/path/to/attributes2 .git/info/attributes
git remote add origin [email protected]:some/repo.git
git pull origin branch

That way you can work with normal tabs on 2 space indented projects.

Of course you can write similar solution for converting from 4 space to 2 space which is the case if you want to contribute to projects published by me and you tend to use 2 spaces while developing.

Solution 4

If you are on windows then you have a few extra steps to get @Olivier Verdier's solution to work.

  1. Download CoreUtils for windows
  2. After installing put the install location in your PATH (How to add a path variable)
  3. I renamed expand.exe to gexpand.exe as there is already a windows expand utility.