Can git automatically switch between spaces and tabs?
Asked Answered
C

4

226

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)

Crenation answered 23/2, 2010 at 8:26 Comment(4)
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.Crenation
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. :-)Crenation
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.Sutra
@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.Trierarch
C
215

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.

Crenation answered 23/2, 2010 at 12:32 Comment(14)
@Olivier: nice trice, the git checkout --force (quicker than remove and checkout again the relevant files)Conservatism
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?Originative
@Jeremy: expand/unexpand are unix commands. You'll either have to find Windows ports/equivalents or use something like CygwinSully
I've found bast working version sourceforge.net/projects/gnuwin32/files/coreutils/5.3.0Pleasurable
On OS X (snow leopard), unexpand and expand don't have the same options and unexpand appear to be broken (no difference with or without -a option)Duhl
@Marc-André Good point. I actually use the coreutils versions. (Install homebrew, and then run brew install coreutils).Crenation
git checkout HEAD -- ** gives me errors like error: pathspec 'name.sublime-project' did not match any file(s) known to git.. Any ways to get past them?Sangria
for anyone confused... to use the commands as referenced... you either need to append g to the beginning of both commands - brew leaves the coreutils executables prefixed, i.e. gexpand - or alias them, somehow..Benefactor
Why are the configs global, but the attributes file just in that one repo? Wouldn't it make more sense to put the config in that repo as well, or make everything global?Redevelop
@OlivierVerdier, The last step does nothing in my case. Probably because I don't really understand what it is supposed to do. I know that I can git checkout filename to undo changes to a certain file. But according to my understanding of your post, git checkout HEAD -- ** should run through all the files in the repo and apply the filter? Is that correct?Tarriance
what's the real profit of this scheme? Why not just replace in all files?Suspensor
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?Armenia
@PhilippLudwig Run git config filtername without the --global to make sure there isn't a local filter interfering with your global one.Plassey
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.Plassey
C
152

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.

Conservatism answered 23/2, 2010 at 8:36 Comment(5)
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...Crenation
@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 #62764Conservatism
Thanks! Now it works. See the complete solution: #2317177Crenation
@Vonc: perhaps one should remove the --global flag, since this would imply, you send spaces to every collaboration project...Racehorse
@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.Conservatism
L
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.

Latini answered 7/2, 2013 at 19:59 Comment(3)
Related: Storing git config as part of the repository; also note you can use (and commit) a .gitattributes file in your repoMedardas
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.Voluntary
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.Voluntary
N
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.
Nader answered 19/5, 2018 at 3:38 Comment(1)
CoreUtils is part of installation of Git for Windows, and is available when using git-bash.Ether

© 2022 - 2024 — McMap. All rights reserved.