Replace tabs by space on git history

So, I prefer spaces, and when I press tab, it should instead write 4 spaces. I forgot to set this option on Sublime Text and did some commits with tabs. The following git command will rewrite the repo history and replace tabs by 4 spaces.

git filter-branch --tree-filter 'git diff-tree --name-only --diff-filter=AM -r --no-commit-id $GIT_COMMIT | find . -exec perl -pi -e "s/\t/    /g" {} \;' HEAD






6 responses to “Replace tabs by space on git history”

  1. Worried scripter Avatar
    Worried scripter

    This code looks total wrong to me (Don’t use it!). The pipe seems to do nothing and even if it did the perl script is told to processes all files by find so it’s pointless (and it will modify tabs in .git!!!) Please tell me if I’m missing something??

  2. Julio Nobrega Netto Avatar

    Hi Worried,

    Yes, you’re wrong. This code rewrites git history, not files in path.

  3. Worried scripter Avatar
    Worried scripter

    Sorry! Please feel free to delete my FUD. Could you explain why pearl gets a piped list of just some of the files and also every file with the find command? I ran the red highlighted code and perl deleted everything (it just did what find told it to), but the git diff-tree output seemed to do nothing? I don’t understand what perl is doing or how the scripts environment changes in a tree-filter.

  4. Worried scripter Avatar
    Worried scripter

    Rephrase, what is the pipe for it seems redundant?

  5. Julio Nobrega Netto Avatar

    Sure, so here’s what the command does:

    “git filter-branch” rewrites git revision history. If you run only this command you’ll see it going through every changeset to rewrite it. But since you did not told what to do, it does nothing.

    This is the manual entry for –tree-filter:

    “This is the filter for rewriting the tree and its contents. The argument is evaluated in shell with the working directory set to the root of the checked out tree. The new tree is then used as-is”

    So what comes right after inside the quotes will be evaluated on shell. We feed “git diff-tree” with $GIT_COMMIT, that’s the changeset from “git filter-branch”

    Run only this command:

    $ git diff-tree –name-only –diff-filter=AM -r –no-commit-id HEAD

    It gets the file names, with their complete paths, from your last commit (actually from the HEAD, which is also probably your last commit).

    And then, the output of “git diff-tree” is piped into “find”, which executes perl to replace t with ” ” (four spaces).

    In other words, every changeset is passed to diff-tree, which uses find/perl to replace the tabs. But it does on the git changeset, not on the current files.

    You’re not supposed to run only the red part of the command 🙂 It’s an argument for “git filter-branch”, so it executes on changesets.

  6. Worried scripter Avatar
    Worried scripter

    Thanks for your reply.
    I did get wrong at the start that –tree-filter doesn’t actually checkout another .git folder when it makes the temporary working tree but other than that I have read the man and have run the diff-tree thing on HEAD / understand the basics.

    So, if `git diff-tree –name-only –diff-filter=AM -r –no-commit-id HEAD` outputs file1 file2 then why does:

    printf ‘%sn’ file1 file2 | perl -pi -e “s/t/ /g” file3 # only change file 3?

    It seems that the perl script is not using standard input at all and the only thing that matters is the find loop and the git diff-tree pipe is NON functional?