Добавить только изменения без пробелов

343

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

Каждый раз, когда я пытаюсь представить патч, я должен сначала игнорировать все изменения только пробелов вручную, чтобы выбрать только соответствующую информацию. Мало того, но когда я бегу, git rebaseя обычно сталкиваюсь с несколькими проблемами из-за них.

Поэтому я хотел бы иметь возможность добавлять в индекс только непробельные изменения, аналогичным git add -pобразом, но без необходимости выбирать все изменения самостоятельно.

Кто-нибудь знает как это сделать?

РЕДАКТИРОВАТЬ: Я не могу изменить способ работы проекта, и они решили, обсудив его в списке рассылки, игнорировать это.

Эду Фелипе
источник

Ответы:

395

Решение @Frew было не совсем тем, что мне было нужно, так что это псевдоним, который я сделал для той же проблемы:

alias.addnw=!sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero -'

Или вы можете просто запустить:

git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

Обновить

В соответствии с этим комментарием добавлены параметры -U0и, --unidiff-zeroсоответственно, для обхода проблем с сопоставлением контекста .

В основном это применяет патч, который будет применяться addбез изменений пробелов. Вы заметите, что после того, как git addnw your/fileвсе еще будут внесенные изменения, это оставленные пробелы.

--No-color не требуется, но так как у меня установлены цвета всегда, я должен его использовать. В любом случае, лучше, чем потом сожалеть.

Колин Хеберт
источник
7
Это хорошо для меня, но я должен был использовать, git apply --ignore-whitespaceиначе патч не будет применяться по понятным причинам.
jupp0r
106
Там действительно должна быть опция git add, вот git add -wтак это и делается .
Ярл
7
Это вызывает у меня проблемы patch does not applyи error while searching for... Есть идеи?
DTI-Matt
18
не работал для меня Получил patch does not applyошибку.
Джерри Саравиа
13
Если вы получаете «патч не удалось» из - за пробелов в контексте , как @bronson указывает, эта пересмотренная команда работает (Он генерирует патч, без контекста): git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. Это не рискованно, потому что индекс уже настолько актуален, насколько это возможно, поэтому он является надежной базой для патча.
void.pointer
36

Это работает для меня:

Если вы хотите сохранить тайник вокруг, это работает

git stash && git stash apply && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Я не как тайники, но я уже столкнулся с ошибкой в мерзавец + Cygwin , где я потеряю изменения, так , чтобы убедиться , что материал пошел в reflog по крайней мере , я создал следующее:

git add . && git commit -am 'tmp' && git reset HEAD^ && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

По сути, мы создаем diff, который не включает изменения пространства, отменяем все наши изменения, а затем применяем diff.

Фрю Шмидт
источник
1
+1. Возможно, вы захотите git stashвместо проверки оформить резервную копию ваших изменений, по крайней мере, до тех пор, пока она не будет протестирована.
Пауло Эберманн
1
В итоге у вас будет много тайников, и в целом вам не нужно делать все это. Это работает, но я думаю, что это немного грязно
Колин Хеберт
3
Я согласен с Колином. Если скрипт работает, тогда не нужно создавать тайник. Хотя, возможно, стоит подумать о том, чтобы запустить тайник, а затем тайник. Выскочившие тайники могут быть восстановлены в случае необходимости, но иначе у вас не будет много тайников. Это также оставляет дополнительный файл лежащим вокруг
Casebash
Как насчет пропуска двоичных файлов? При попытке применить приведенный выше фрагмент кода я получаю сообщения об ошибках, которые невозможно применить без полной строки индекса! Что меня удивляет, так это то, что я вообще не трогал эти файлы / двоичные файлы!
tver3305
1
Я думаю, что в конце первой команды "git rm foo.patch" должно быть просто "rm foo.patch". В противном случае очень полезно, спасибо.
Джек Кейси
33

Создайте файл исправления, содержащий только реальные изменения (исключая строки только с изменениями пробелов), затем очистите рабочее пространство и примените этот файл исправления:

git diff> резервная копия
git diff -w> изменения
git reset --hard
patch <изменения

Просмотрите оставшиеся отличия, затем addи commitкак обычно.

Эквивалентом Mercurial является следующее:

hg diff> резервная копия
hg diff -w> изменения
hg возврат - все
hg импорт --no-commit изменения

Стив Питчерс
источник
Что такое «защищенный» вопрос? и я тоже отвечаю. Я не думаю, что это даже квалифицируется как ответ «я тоже», потому что кажется, что вопрос был вытащен из воздуха ...
Jww
4
@jww Суть вопроса оригинального плаката: «Как избежать внесения изменений в пробел только при управлении исходным кодом». В OP используется Git, но это также относится ко всем системам контроля версий, которые я когда-либо использовал. Этот ответ показывает правильную процедуру, если кто-то использует Mercurial. Я мог бы предположить, что кто-то еще мог бы предложить решение для людей, использующих Sublesion и т. Д.
Стив Питчерс
1
@jww и @ pagid: я отредактировал свой ответ, чтобы специально адресовать Git, используя тот же подход, что и мое решение для Mercurial. На мой взгляд, StackOverflow - это больше, чем просто другой форум Q + A, он также играет роль хранилища знаний. Люди, отличные от оригинального постера, могут получить пользу от полученных ответов, и их обстоятельства могут отличаться. Вот почему я считаю, что ответы, передающие общий принцип, являются действительными, а не направлены только на одну конкретную ситуацию.
Стив Питчерс
@Steve - «Я отредактировал свой ответ, чтобы специально адресовать Git ...» - почему вы не задали новый вопрос в контексте Mercurial, а затем добавили свой собственный ответ на новый вопрос ???
января
8
На самом деле это самый чистый, самый понятный и самый нерушимый из всех подходов, которые я видел.
Kzqai
12

Ответ, получивший наибольшее количество голосов, работает не во всех случаях из-за пробелов в контексте патча, согласно мнению пользователей в комментариях.

Я пересмотрел команду следующим образом:

$ git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero

Это создает патч без контекста. Не должно быть проблемой, так как патч недолговечный.

Соответствующий псевдоним, снова пересмотр того, что уже было предоставлено другими пользователями:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
void.pointer
источник
Чтобы игнорировать только изменения отступов, я должен был использовать --ignore-space-changeвместо -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
Энди
Слово с предупреждением не использовать этот прекрасный трюк без контекста, в --ignore-blank-linesпротивном случае вы обнаружите, что порции diff будут исправлены с неправильными смещениями, если некоторые из изменений «белого пространства», которые вы хотите игнорировать, являются удалением / добавлением пустой строки.
elbeardmorez
12

Добавьте следующее к вашему .gitconfig:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"

Спасибо ответу @Colin Herbert за вдохновение.

Синтаксис Объяснение

Финал #должен быть заключен в кавычки, поэтому он не рассматривается как комментарий внутри .gitconfig, а вместо этого проходит через него и рассматривается как комментарий внутри оболочки - он вставляется между концом git applyи предоставленными пользователем аргументами, которые gitавтоматически помещаются в конец командной строки. Эти аргументы здесь не нужны - мы не хотим git applyих использовать, отсюда и предыдущий символ комментария. Вы можете выполнить эту команду, GIT_TRACE=1 git anwчтобы увидеть это в действии.

В --сигналах конца аргументов и позволяют в случае , что у вас есть файл с именем -wили что - то , что будет выглядеть как переключатель на git diff.

Избегающие двойные кавычки $@необходимы для сохранения любых предоставленных пользователем аргументов в кавычках. Если "персонаж не экранирован, он будет использован .gitconfigсинтаксическим анализатором и не достигнет оболочки.

Примечание: .gitconfigпсевдоним разбор не признает одинарные кавычки ничего особенного - его только специальные символы ", \, \n, и ;(за пределы "-quoted строки). Вот почему "всегда нужно экранировать символ a , даже если он выглядит так, как будто он внутри строки в одинарных кавычках (о чем git совершенно не знает агностики).

Это важно, например. если у вас есть удобный псевдоним для выполнения bashкоманды в корне рабочего дерева. Неправильная формулировка:

sh = !bash -c '"$@"' -

Хотя правильным является:

sh = !bash -c '\"$@\"' -
Том Хейл
источник
Превосходно. Это позволило мне добавить один файл за раз. Кроме добавления корневого каталога для аргумента, есть ли способ заставить эту работу работать как 'git add -A'?
Чаки
7

Как насчет следующего:

git add `git diff -w --ignore-submodules |grep "^[+][+][+]" |cut -c7-`

Команда внутри обратных кавычек получает имена файлов, которые не имеют пробелов.

karmakaze
источник
2
или просто git add `git diff -w |grep '^+++' |cut -c7-`если субмодули не используются
karmakaze
-1

Сначала вы должны рассмотреть, является ли конечный пробел намеренным. Многие проекты, в том числе ядро ​​Linux, Mozilla, Drupal и Kerberos (и многие другие на странице Википедии о стиле), запрещают использовать пробелы в конце. Из документации по ядру Linux:

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

В вашем случае проблема в другом: предыдущие коммиты (и, возможно, текущие) не следовали этому руководству.

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

Вместо того, чтобы пытаться перенастроить git для игнорирования проблемы или отключить другие желательные функции в вашем редакторе, я бы начал с публикации в списке рассылки проекта, объясняющего проблему. Многие редакторы (и сам git) могут быть настроены для работы с конечными пробелами.

Кевин Вермеер
источник
16
Это не намеренно, но я не могу изменить способ, которым думают более 100 человек, которые вносят свой вклад в проект. Они не возражают против этого и не примут патчи с более чем 1000 изменений, которые касаются только пробельных пробелов. Они знают о проблеме и решили ее игнорировать. Эта дискуссия уже произошла в списке и была закрыта. В этом случае я должен адаптироваться к ним.
Эду Фелипе
19
Затем настройте ваш редактор так, чтобы он не урезал конечные пробелы при работе с кодом этого проекта.
jamessan
-2

Я нашел 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"
  done
  exit
cmcginty
источник
4
Этот вопрос спрашивает, как сохранить конечные пробелы.
Дуглас
@Douglas: возможно, можно использовать этот ответ, чтобы создать коммит во временной ветке, зафиксировать реальный патч там и каким-то образом выбрать вишню только в рабочую ветку ...
Тобиас Кинцлер