Заставить git автоматически удалять конечные пробелы перед фиксацией - PullRequest
207 голосов
/ 26 февраля 2009

Я использую git со своей командой и хотел бы удалить изменения пробелов из моих различий, журналов, слияний и т. Д. Я предполагаю, что самый простой способ сделать это - git автоматически удалить конечные пробелы (и другие пробельные ошибки) из всех коммитов по мере их применения.

Я пытался добавить следующее в ~/.gitconfig файл, но он ничего не делает, когда я фиксирую. Может быть, он предназначен для чего-то другого. Какое решение?

[core]
    whitespace = trailing-space,space-before-tab
[apply]
    whitespace = fix

Я использую ruby ​​на тот случай, если у кого-нибудь есть какие-то конкретные идеи. Следующим шагом будет автоматическое форматирование кода перед фиксацией, но это сложная проблема, которая не вызывает больших проблем.

Ответы [ 16 ]

103 голосов
/ 26 февраля 2009

Эти настройки (core.whitespace и apply.whitespace) предназначены не для удаления конечных пробелов, а для:

  • core.whitespace: обнаруживать их и выдавать ошибки
  • apply.whitespace: и удалять их, но только во время патча, а не "всегда автоматически"

Я полагаю, что git hook pre-commit справится с этой задачей лучше (включая удаление конечных пробелов)


Обратите внимание, что в любой момент вы можете не запускать хук pre-commit:

  • временно: git commit --no-verify .
  • навсегда: cd .git/hooks/ ; chmod -x pre-commit

Предупреждение: по умолчанию сценарий pre-commit (например, этот ) имеет , а не функцию «удалить трейлинг», но функцию «предупреждения», такую ​​как:

if (/\s$/) {
    bad_line("trailing whitespace", $_);
}

Однако вы можете построить лучший pre-commit крюк , особенно если учесть, что:

Фиксация в Git только с некоторыми изменениями, добавленными в область подготовки, по-прежнему приводит к «атомарной» ревизии, которая , возможно, никогда не существовала в качестве рабочей копии и может не работать .


Например, oldman предлагает в другом ответе a pre-commit hook , который обнаруживает и удаляет пробелы.
Так как этот хук получает имя файла каждого файла, я бы рекомендовал быть осторожным с определенным типом файлов: вы не хотите удалять конечные пробелы в .md (markdown) файлах!

41 голосов
/ 14 марта 2013

Вы можете обмануть Git, чтобы исправить пробелы, обманывая Git, рассматривая ваши изменения как патч. В отличие от решений «pre-commit hook», эти решения добавляют в Git команды исправления пробелов.

Да, это хаки.


Надежные решения

Следующие псевдонимы Git взяты из мой ~/.gitconfig.

Под "устойчивым" я подразумеваю, что эти псевдонимы работают без ошибок, делая правильная вещь, независимо от того, являются ли дерево или индекс грязными. Однако они не работают, если интерактивный git rebase -i уже выполняется; см. my ~/.gitconfig для дополнительных проверок, если вам небезразличен этот угловой случай, где описанный в конце прием git add -e должен работать.

Если вы хотите запустить их прямо в оболочке, не создавая Git псевдоним, просто скопируйте и вставьте все между двойными кавычками (при условии, что ваша оболочка похожа на Bash).

Исправить индекс, но не дерево

Следующий fixws псевдоним Git исправляет все пробелы в индексе, если есть, но не касается дерева:

# Logic:
#
# The 'git stash save' fails if the tree is clean (instead of
# creating an empty stash :P). So, we only 'stash' and 'pop' if
# the tree is dirty.
#
# The 'git rebase --whitespace=fix HEAD~' throws away the commit
# if it's empty, and adding '--keep-empty' prevents the whitespace
# from being fixed. So, we first check that the index is dirty.
#
# Also:
# - '(! git diff-index --quiet --cached HEAD)' is true (zero) if
#   the index is dirty
# - '(! git diff-files --quiet .)' is true if the tree is dirty
#
# The 'rebase --whitespace=fix' trick is from here:
# https://stackoverflow.com/a/19156679/470844
fixws = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git stash save FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git stash pop && \
    git reset --soft HEAD~ ; \
  elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

Идея состоит в том, чтобы запустить git fixws до git commit, если у вас есть ошибки пробела в индексе.

Исправить индекс и дерево

Следующий fixws-global-tree-and-index Git alias исправляет все пробелы ошибки в индексе и дереве, если есть:

# The different cases are:
# - dirty tree and dirty index
# - dirty tree and clean index
# - clean tree and dirty index
#
# We have to consider separate cases because the 'git rebase
# --whitespace=fix' is not compatible with empty commits (adding
# '--keep-empty' makes Git not fix the whitespace :P).
fixws-global-tree-and-index = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~2 && \
    git reset HEAD~ && \
    git reset --soft HEAD~ ; \
  elif (! git diff-files --quiet .) ; then \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git reset HEAD~ ; \
  elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

Чтобы также исправить пробелы в неверсионных файлах, выполните

git add --intent-to-add <unversioned files> && git fixws-global-tree-and-index

Простые, но не надежные решения

Эти версии легче копировать и вставлять, но они не делают правильно, если их побочные условия не выполнены.

Исправить поддерево, укорененное в текущем каталоге (но сбрасывает индекс, если оно не пустое)

Использование git add -e для «редактирования» патчей с помощью редактора идентификации ::

(export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset

Исправить и сохранить индекс (но не работает, если дерево грязное или индекс пуст)

git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset --soft HEAD~

Исправить дерево и индекс (но сбрасывает индекс, если он не пустой)

git add -u :/ && git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset HEAD~

Объяснение export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue . трюка

Прежде чем я узнал о git rebase --whitespace=fix трюке от этого ответа Я использовал более сложный трюк git add везде.

Если мы сделали это вручную:

  1. Установите apply.whitespace на fix (вам нужно сделать это только один раз):

    git config apply.whitespace fix
    

    Это говорит Git исправлять пробелы в патчах .

  2. Убедите Git рассматривать ваши изменения как патч :

    git add -up .
    

    Нажмите a + , введите , чтобы выбрать все изменения для каждого файла. Вы получите предупреждение о том, что Git исправляет ваши пробельные ошибки.
    (git -c color.ui=auto diff в этот момент показывает, что ваши неиндексированные изменения являются именно ошибками пробелов).

  3. Удалите ошибки пробелов из вашей рабочей копии:

    git checkout .
    
  4. Верните ваши изменения (если вы не готовы их зафиксировать):

    git reset
    

GIT_EDITOR=: означает использовать : в качестве редактора и в качестве команды : это личность.

29 голосов
/ 19 августа 2010

Я нашел ловушку git pre-commit, которая удаляет конечные пробелы .

#!/bin/sh

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
   # Fix them!
   sed -i 's/[[:space:]]*$//' "$FILE"
   git add "$FILE"
done
exit
19 голосов
/ 04 января 2011

В Mac OS (или, вероятно, на любом BSD) параметры команды sed должны немного отличаться. Попробуйте это:

#!/bin/sh

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -E 's/:[0-9]+:.*//' | uniq` ; do
    # Fix them!
    sed -i '' -E 's/[[:space:]]*$//' "$FILE"
    git add "$FILE"
done

Сохраните этот файл как .git/hooks/pre-commit - или найдите тот, который уже там, и вставьте нижний чанк где-нибудь внутри него. И не забудьте chmod a+x это тоже.

Или для глобального использования (через Git commit hooks - глобальные настройки ) вы можете поместить его в $GIT_PREFIX/git-core/templates/hooks (где GIT_PREFIX - / usr или / usr / local или / usr / share или / opt / local / share) и запустите git init внутри существующих репозиториев.

Согласно git help init:

Запуск git init в существующем хранилище безопасен. Это не перезапишет то, что уже есть. Основная причина повторного запуска git init - это выбор новых добавленных шаблонов.

10 голосов
/ 27 февраля 2009

Я бы предпочел оставить это задание вашему любимому редактору.

Просто установите команду для удаления завершающих пробелов при сохранении.

9 голосов
/ 28 марта 2014

Пожалуйста, попробуйте my pre-commit hooks , он может автоматически определять конечный пробел и удалить его . Спасибо!

может работать под GitBash(windows), Mac OS X and Linux!


Фотоснимок:

$ git commit -am "test"
auto remove trailing whitespace in foobar/main.m!
auto remove trailing whitespace in foobar/AppDelegate.m!
[master 80c11fe] test
1 file changed, 2 insertions(+), 2 deletions(-)
9 голосов
/ 14 октября 2013

Я написал эту ловушку перед фиксацией, которая удаляет только завершающие пробелы из строк, которые вы изменили / добавили, поскольку предыдущие предложения, как правило, создают нечитаемые коммиты, если целевые файлы имеют слишком много конечных пробелов .

#!/bin/sh

if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

IFS='
'

files=$(git diff-index --check --cached $against -- | sed '/^[+-]/d' | perl -pe 's/:[0-9]+:.*//' | uniq)
for file in $files ; do
    diff=$(git diff --cached $file)
    if test "$(git config diff.noprefix)" = "true"; then
        prefix=0
    else
        prefix=1
    fi
    echo "$diff" | patch -R -p$prefix
    diff=$(echo "$diff" | perl -pe 's/[ \t]+$// if m{^\+}')
    out=$(echo "$diff" | patch -p$prefix -f -s -t -o -)
    if [ $? -eq 0 ]; then
        echo "$diff" | patch -p$prefix -f -t -s
    fi
    git add $file
done
8 голосов
/ 11 февраля 2015

Использование атрибутов git и настройка фильтров с помощью git config

ОК, это новый подход к решению этой проблемы ... Мой подход состоит в том, чтобы не использовать какие-либо хуки, а использовать фильтры и атрибуты git. Это позволяет вам настроить на каждом компьютере, на котором вы разрабатываете, набор фильтров, которые уберут лишние пробелы и лишние пустые строки в конце файлов перед их фиксацией. Затем настройте файл .gitattributes, в котором указано, к каким типам файлов должен применяться фильтр. Фильтры имеют две фазы: чистая, которая применяется при добавлении файлов в индекс, и нечеткая поверхность, которая применяется при добавлении их в рабочий каталог.

Скажите вашему git искать файл глобальных атрибутов

Сначала скажите вашей глобальной конфигурации использовать файл глобальных атрибутов:

git config --global core.attributesfile ~/.gitattributes_global

Создание глобальных фильтров

Теперь создайте фильтр:

git config --global filter.fix-eol-eof.clean fixup-eol-eof %f
git config --global filter.fix-eol-eof.smudge cat
git config --global filter.fix-eol-eof.required true

Добавьте магию сценариев sed

Наконец, поместите скрипт fixup-eol-eof где-нибудь на вашем пути и сделайте его исполняемым. Сценарий использует sed для редактирования на лету (удаление пробелов и пробелов в конце строк и лишних пустых строк в конце файла)

fixup-eol-eof должен выглядеть так:

#!/bin/bash
sed -e ’s/[  ]*$//‘ -e :a -e '/^\n*$/{$d;N;ba' -e '}' $1

моя суть этого

Сообщите git, какие типы файлов применять к вашему вновь созданному фильтру

Наконец, создайте или откройте ~ / .gitattributes_global в вашем любимом редакторе и добавьте такие строки, как:

pattern attr1 [attr2 [attr3 […]]]

Поэтому, если мы хотим исправить проблему с пробелами, для всех наших исходных файлов c мы добавим строку, которая выглядит следующим образом:

*.c filter=fix-eol-eof

Обсуждение фильтра

Фильтр имеет две фазы: чистая фаза, которая применяется, когда что-то добавляется в индекс или регистрируется, и фаза размазывания, когда git помещает вещи в ваш рабочий каталог. Здесь наше пятно просто запускает содержимое с помощью команды cat, которая должна оставить их без изменений, за исключением возможного добавления завершающего символа новой строки, если его не было в конце файла. Команда clean - это фильтрация пробелов, которую я собрал из заметок в http://sed.sourceforge.net/sed1line.txt.. Кажется, это нужно поместить в сценарий оболочки, я не мог понять, как ввести команду sed, включая очистку лишние лишние строки в конце файла прямо в файл git-config. (Вы CAN избавляетесь от конечных пробелов, однако, без необходимости отдельного сценария sed, просто установите filter.fix-eol-eof на что-то вроде sed 's/[ \t]*$//' %f, где \t - это действительная вкладка, нажав таб.)

Параметр require = true приводит к возникновению ошибки, если что-то идет не так, чтобы вы не попадали в неприятности.

Пожалуйста, прости меня, если мой язык относительно мерзости неточен. Я думаю, что я достаточно хорошо понимаю концепции, но все еще изучаю терминологию.

6 голосов
/ 07 июня 2011

Вот версия, совместимая с Ubuntu + Mac OS X:

#!/bin/sh
#

# A git hook script to find and fix trailing whitespace
# in your commits. Bypass it with the --no-verify option
# to git-commit
#

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
  against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | (sed -r 's/:[0-9]+:.*//' > /dev/null 2>&1 || sed -E 's/:[0-9]+:.*//') | uniq` ; do
  # Fix them!
  (sed -i 's/[[:space:]]*$//' "$FILE" > /dev/null 2>&1 || sed -i '' -E 's/[[:space:]]*$//' "$FILE")
  git add "$FILE"
done

# Now we can commit
exit

Веселись

5 голосов
/ 20 сентября 2012

Думал об этом сегодня. Это все, что я сделал для проекта Java:

egrep -rl ' $' --include *.java *  | xargs sed -i 's/\s\+$//g'
...