Сделать git автоматически удалять конечные пробелы перед фиксацией

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

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

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

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

Ответ 1

Эти настройки (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 (markdown)!

Ответ 2

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

Да, это хаки.


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

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

Под "устойчивым" я имею в виду, что эти псевдонимы работают без ошибок, делая правильная вещь, независимо от того, являются ли дерево или индекс грязными. Однако они не работают, если интерактивный git rebase -i уже выполняется; см. my ~/.gitconfig для дополнительных проверок, если вам небезразличен этот angular случай, где описанный в конце прием 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 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:
# /questions/32680/git-remove-trailing-whitespace-in-new-files-before-commit/238832#238832
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.

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

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

    git config apply.whitespace fix
    

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

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

    git add -up .
    

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

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

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

    git reset
    

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

Ответ 3

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

#!/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

Ответ 4

В 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 - выбрать новые шаблоны.

Ответ 5

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

Просто установите команду для удаления конечных пробелов при сохранении.

Ответ 6

Я написал этот крюк 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

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

Ответ 7

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

он может работать под 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(-)

Ответ 8

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

Хорошо, это новый подход к решению этой проблемы... Мой подход состоит в том, чтобы не использовать какие-либо хуки, а использовать фильтры и атрибуты 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 приводит к возникновению ошибки, если что-то идет не так, чтобы избежать неприятностей.

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

Ответ 9

Вот совместимая версия 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

Удачи

Ответ 10

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

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

Ответ 11

for-loop для файлов использует переменную оболочки IFS. в данном script имена файлов с символом в них, который также находится в переменной IFS, будут рассматриваться как два разных файла в for-loop. Этот script исправляет это: модификатор многострочного режима, как указано sed-manual, по-видимому, не работает по умолчанию в моем блоке ubuntu, поэтому я искал другую реализацию и нашел это с помощью итерационной метки, по сути, она только начнет замену в последней строке файла если я понял это правильно.

#!/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

SAVEIFS="$IFS"
# only use new-line character as seperator, introduces EOL-bug?
IFS='
'
# Find files with trailing whitespace
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( MacOSx-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 check for whitespace-characters
exec git diff-index --check --cached $against --

[1] sed-subsition pattern: Как заменить новую строку (\n) с помощью sed?.

Ответ 12

Для пользователей Sublime Text.

Правильно установите правильную настройку Конфигурация пользователя.

"trim_trailing_white_space_on_save": true

Ответ 13

Это не удаляет пробелы автоматически перед фиксацией, но это довольно легко осуществить. Я поместил следующий perl script в файл с именем git -wsf (git whitespace fix) в каталоге в переменной $PATH, чтобы я мог:

git wsf | ш

и удаляет все пробелы только из строк файлов, которые git сообщает как diff.

#! /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";
}

Ответ 14

Немного поздно, но так как это может помочь кому-то там, вот так.

Откройте файл в VIM. Чтобы заменить вкладки пробелами, введите следующую команду в командной строке vim

:%s#\t#    #gc

Чтобы избавиться от других отстающих пробелов

:%s#\s##gc

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

Ответ 15

Чтобы удалить конечные пробелы в конце строки в файле переносимо, используйте ed:

test -s file &&
   printf '%s\n' H ',g/[[:space:]]*$/s///' 'wq' | ed -s file

Ответ 16

Это, вероятно, не решит вашу проблему напрямую, но вы можете установить их через git -config в своем фактическом пространстве проекта, который редактирует. /.git/config в отличие от ~/.gitconfig. Приятно поддерживать соответствие параметров всем участникам проекта.

git config core.whitespace "trailing-space,space-before-tab"
git config apply.whitespace "trailing-space,space-before-tab"