229

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)

7
  • 43
    PEP8 is precisely my problem. Everybody follows it and I'm stuck with my tabs. I happen to think that one indentation = one tab is the right thing to do (why spaces? why 4 spaces? PEP8 doesn't explain that...). Anyway, with this git trick, I can happily use tabs on my computer and share my code with all the PEP8 followers out there. Commented Feb 23, 2010 at 8:57
  • 8
    Oh! I use TextMate, and I can convert between spaces to tabs. The thing is, when I hit tab, I like my editor to write... tab. So if I checkout a python project with spaces, I will insert all sort of tabs. I must manually convert to tabs, but when I check in, it looks like 1000 deletions, 1000 additions, and my collaborators will not be happy. :-) Commented Feb 23, 2010 at 9:45
  • 7
    The reason PEP8 specifies spaces instead of tabs is because of the continuation indentation rules. There are two ways to continue an over-long line inside a parenthetical. If you start a new line immediately after a parenthetical you just indent one. If you instead put part of the content of the parenthetical on the first line then you have to continue the parenthetical on the next line at the indentation level of the opening parenthetical. If you use tabs that doesn't work. Commented Nov 25, 2015 at 4:58
  • 3
    @JohnChristopherJones for that situation, one could use tabs to match indentation with the previous line then spaces to match a position in the previous line. This can be converted to spaces easily. Unfortunately the reverse is not true, because it commingles indentation information with alignment information. Commented Sep 11, 2019 at 15:31
  • @JohnChristopherJones so an arbitrary implementation rule neuters tabs because python can't be bothered to parse properly? pretty weak rationale. besides which, I indent continuations all the time with tab beyond line where parenthetical started. python never complained, always handled it fine. don't think your info is accurate. Commented Aug 28 at 19:20

4 Answers 4

218

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.

Sign up to request clarification or add additional context in comments.

14 Comments

The clean filter isn't working for me. When I do git add . I get an error saying "error: external filter expand --tabs=4 --initial failed". I'm on Windows. Does that make a difference?
@Jeremy: expand/unexpand are unix commands. You'll either have to find Windows ports/equivalents or use something like Cygwin
@Marc-André Good point. I actually use the coreutils versions. (Install homebrew, and then run brew install coreutils).
It seems that this does not work anymore, the filters do nothing for me. After checkout, the files still have spaces. Any update on this?
I also found it easier to just put the attributes file in ~/.config/git/attributes and the config file in ~/.gitconfig so these rules apply to all projects instead of messing around with each one.
|
153

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

alt text

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.

5 Comments

Unfortunately, it just doesn't work. I followed all the instructions, but git does not apply the fiter. :-( When I checkout, the smudge filter is not applied, and when I checkin, nothing happens either... git is so frustrating sometimes...
@Olivier: Strange, I never had any problem with that, as long as I carefully limit the scope of the attribute filter (to a specific subtree, for a specific type of files only) in order to not slow down the checkout/check-in process. See for instance stackoverflow.com/questions/62264/…
Thanks! Now it works. See the complete solution: stackoverflow.com/questions/2316677/…
@Vonc: perhaps one should remove the --global flag, since this would imply, you send spaces to every collaboration project...
@CommuSoft only to the projects which have the right .gitattributes. But yes, it is easier to understand if the config is kept local to the repo. I have edited the answer.
46

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.

3 Comments

Related: Storing git config as part of the repository; also note you can use (and commit) a .gitattributes file in your repo
Small nit: this has nothing to do with GitHub or any particular service. It's just Git. Nit 2: You probably want to use $XDG_CONFIG_HOME/git/config over $HOME/.gitconfig to keep your config files organized and your home directory clean.
But there's a high chance you want this in .git in the project folder as it's not uncommon for projects to have different settings.
1

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.

1 Comment

CoreUtils is part of installation of Git for Windows, and is available when using git-bash.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.