Сделайте так, чтобы Git автоматически удалял конечные пробелы перед фиксацией
Я использую Git со своей командой и хотел бы удалить изменения пробелов из моих различий, журналов, слияний и т. Д. Я предполагаю, что самый простой способ сделать это - Git автоматически удалить конечные пробелы (и другие ошибки пробелов) из всех коммитов по мере их применения.
Я попытался добавить в ~/.gitconfig
файл следующее, но при фиксации ничего не происходит. Может быть, он предназначен для чего-то другого. Какое решение?
[core]
whitespace = trailing-space,space-before-tab
[apply]
whitespace = fix
Я использую Ruby на случай, если у кого-то есть какие-то конкретные идеи Ruby. Следующим шагом будет автоматическое форматирование кода перед фиксацией, но это серьезная проблема, которая на самом деле не вызывает большой проблемы.
Ответов (16)16
Вы можете обманом заставить Git исправить пробелы за вас, заставив Git обрабатывать ваши изменения как патч. В отличие от решений «pre-commit hook», эти решения добавляют в Git команды исправления пробелов.
Да, это хаки.
Надежные решения
Следующие псевдонимы Git взяты из
моего~/.gitconfig
.
Под «надежным» я подразумеваю, что эти псевдонимы выполняются без ошибок, выполняя правильные действия, независимо от того, является ли дерево или индекс грязным. Однако они не работают, если интерактив git rebase -i
уже выполняется; см. мои~/.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://answacode.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 исправляет все ошибки пробелов в индексе и дереве, если таковые имеются:
# 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
везде использовал более сложный трюк.
Если мы сделали это вручную:
Установите
apply.whitespace
значениеfix
(вам нужно сделать это только один раз):git config apply.whitespace fix
Это указывает Git исправлять пробелы в патчах .
Убедите Git рассматривать ваши изменения как патч :
git add -up .
Нажмите a+, enterчтобы выбрать все изменения для каждого файла. Вы получите предупреждение о том, что Git исправляет ваши ошибки с пробелами.
(git -c color.ui=auto diff
на этом этапе выясняется, что ваши неиндексированные изменения являются в точности ошибками с пробелами).Удалите ошибки пробелов из вашей рабочей копии:
git checkout .
Верните свои изменения (если вы не готовы их зафиксировать):
git reset
В GIT_EDITOR=:
средстве для использования в :
качестве редактора, и как команда
:
является тождественным.
Пожалуйста, попробуйте мои перехватчики перед фиксацией . Он может автоматически определять конечный пробел и удалять его .
Он может работать под Git Bash (Windows), Mac OS X и 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(-)
Для пользователей Sublime Text .
Правильно установите следующие параметры в конфигурации Пользователь-Настройка .
"trim_trailing_white_space_on_save": true
Откройте файл в Vim. Чтобы заменить табуляцию пробелами, введите в командной строке Vim следующее:
:%s#\t# #gc
Чтобы избавиться от других конечных пробелов
:%s#\s##gc
Это в значительной степени помогло мне. Это утомительно, если вам нужно редактировать много файлов. Но мне это показалось проще, чем хуки перед фиксацией и работа с несколькими текстовыми редакторами.
Я написал эту ловушку перед фиксацией, которая удаляет только конечные пробелы из строк, которые вы изменили / добавили, поскольку предыдущие предложения, как правило, создают нечитаемые коммиты, если в целевых файлах слишком много конечных пробелов.
#!/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
Использование атрибутов Git и настройка фильтров с помощью конфигурации Git
Хорошо, это новый подход к решению этой проблемы ... Мой подход состоит в том, чтобы не использовать никаких хуков, а использовать фильтры и атрибуты Git. Это позволяет вам настроить на каждой машине, на которой вы разрабатываете, набор фильтров, которые будут удалять лишние конечные пробелы и лишние пустые строки в конце файлов перед их фиксацией.
Затем настройте файл .gitattributes, в котором указано, к каким типам файлов следует применять фильтр. Фильтры состоят из двух этапов, clean
которые применяются при добавлении файлов в индекс и smudge
которые применяются при добавлении их в рабочий каталог.
Скажите своему 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. (Однако вы можете избавиться от конечных пробелов без необходимости в отдельном сценарии sed. Просто установите что- filter.fix-eol-eof
то вроде того, sed 's/[ \t]*$//' %f
где \t
это фактическая вкладка, нажав Tab.)
Это require = true
приводит к возникновению ошибки, если что-то пойдет не так, чтобы избежать неприятностей.
Эти настройки ( 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 только с некоторыми изменениями, добавленными в промежуточную область, по-прежнему приводит к «атомарной» ревизии, которая, возможно, никогда не существовала в качестве рабочей копии и может не работать .
Например, Олдман предлагает в другой ответ на pre-commit
крючок , который детектирует и удалить пробелы.
Поскольку этот хук получает имя файла каждого файла, я бы рекомендовал быть осторожным с определенными типами файлов: вы не хотите удалять конечные пробелы в .md
файлах (уценки)!
В macOS (или, скорее всего, в любой 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 post-commit hook ко всем текущим и будущим репозиториям ) вы можете вставить его $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
- подобрать недавно добавленные шаблоны.
for
Петля для файлов использует $IFS
переменную оболочки. В данном сценарии имена файлов с символом в них, который также находится в переменной $ IFS, будут рассматриваться как два разных файла в for
цикле.
Этот скрипт исправляет это: модификатор многострочного режима, указанный в руководстве по sed , похоже, не работает по умолчанию в моем ящике Ubuntu, поэтому я искал другую реализацию и нашел ее с повторяющейся меткой, по сути, она начнет замену только на последняя строка файла, если я правильно понял.
#!/bin/sh
#
# A Git hook script to find and fix trailing white space
# 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
SAVEIFS="$IFS"
# only use new-line character as separator, introduces EOL-bug?
IFS='
'
# Find files with trailing white space
for FILE in $(
git diff-index --check --cached $against -- \
| sed '/^[+-]/d' \
| ( sed -r 's/:[0-9]+:.*//' || sed -E 's/:[0-9]+:.*//' ) \
| uniq \
)
do
# replace whitespace-characters with nothing
# if first execution of sed-command fails, try second one (Mac OS X version)
(
sed -i ':a;N;$!ba;s/\n\+$//' "$FILE" > /dev/null 2>&1 \
|| \
sed -i '' -E ':a;N;$!ba;s/\n\+$//' "$FILE" \
) \
&& \
# (re-)add files that have been altered to Git commit-tree
# when change was a [:space:]-character @EOL|EOF git-history becomes weird...
git add "$FILE"
done
# restore $IFS
IFS="$SAVEIFS"
# Exit script with the exit-code of git's check for white space characters
exec git diff-index --check --cached $against --
1 шаблон подстановки sed: как заменить новую строку (\ n) с помощью sed?
Это не удаляет пробелы автоматически перед фиксацией, но это довольно легко сделать. Я поместил следующий сценарий Perl в файл с именем git-wsf (исправление пробелов Git) в каталоге в $ PATH, чтобы я мог:
git wsf | sh
И он удаляет все пробелы только из строк файлов, о которых Git сообщает как разницу.
#! /bin/sh
git diff --check | perl -x $0
exit
#! /usr/bin/perl
use strict;
my %stuff;
while (<>) {
if (/trailing whitespace./) {
my ($file,$line) = split(/:/);
push @{$stuff{$file}},$line;
}
}
while (my ($file, $line) = each %stuff) {
printf "ex %s <<EOT\n", $file;
for (@$line) {
printf '%ds/ *$//'."\n", $_;
}
print "wq\nEOT\n";
}
Это, вероятно, не решит напрямую вашу проблему, но вы можете установить их с помощью git-config в вашем фактическом пространстве проекта, который редактирует файл ./.git/config, а не файл ~ / .gitconfig . Приятно сохранять единообразие настроек для всех участников проекта.
git config core.whitespace "trailing-space,space-before-tab"
git config apply.whitespace "trailing-space,space-before-tab"
Я нашел ловушку предварительной фиксации Git, которая удаляет конечные пробелы .
#!/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
Вот версия, совместимая с Ubuntu и Mac OS X:
#!/bin/sh
#
# A Git hook script to find and fix trailing white space
# 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