Dodaj tylko zmiany niebiałe

343

Mam edytor tekstu, który automatycznie przycina końcowe białe znaki po zapisaniu pliku, i uczestniczę w projekcie open source, który ma poważne problemy z końcowymi białymi znakami.

Za każdym razem, gdy próbuję przesłać poprawkę, najpierw muszę ręcznie zignorować wszystkie zmiany tylko w białych znakach, aby wybrać tylko odpowiednie informacje. Nie tylko to, ale kiedy biegam git rebase, zwykle mam z tego powodu kilka problemów.

Jako taki chciałbym móc dodawać do indeksu tylko zmiany niebiałe, w podobny git add -psposób, ale bez konieczności wybierania wszystkich zmian osobiście.

Czy ktoś wie jak to zrobić?

EDYCJA: Nie mogę zmienić sposobu, w jaki działa projekt, i postanowili, po omówieniu go na liście mailingowej, zignorować to.

Edu Felipe
źródło

Odpowiedzi:

395

Rozwiązanie @Frew nie było dokładnie tym, czego potrzebowałem, więc oto alias, który stworzyłem dla tego samego problemu:

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

Lub możesz po prostu uruchomić:

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

Aktualizacja

Dodano opcje -U0i --unidiff-zeroodpowiednio do problemów z dopasowaniem kontekstu obejścia, zgodnie z tym komentarzem .

Zasadniczo stosuje łatkę, którą można zastosować addbez zmian białych znaków. Zauważysz, że po wprowadzeniu git addnw your/filezmian nieustawionych pozostaną białe spacje.

Opcja --no-color nie jest wymagana, ale ponieważ mam zawsze ustawione kolory, muszę z niej korzystać. W każdym razie lepiej być bezpiecznym niż przykro.

Colin Hebert
źródło
7
Działa to dla mnie dobrze, jednak musiałem użyć git apply --ignore-whitespacetej poprawki, ponieważ łatka nie miałaby zastosowania z oczywistych powodów.
jupp0r,
106
Naprawdę powinna istnieć opcja dodania git, tak jak git add -wto zrobiło.
Jarl
7
Daje mi to problemy patch does not applyi error while searching for... Jakieś pomysły?
DTI-Matt
18
nie działało dla mnie. Wystąpił patch does not applybłąd.
Jerry Saravia,
13
Jeśli otrzymujesz „plaster failed” z powodu spacji w kontekście jak zaznacza @bronson działa to poprawione Command (Generuje patch bez kontekstu) git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. Nie jest to ryzykowne, ponieważ indeks jest już tak aktualny, jak to tylko możliwe, dlatego jest wiarygodną bazą dla łatki.
void.pointer
36

To działa dla mnie:

Jeśli chcesz zachować zapas, to działa

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

Nie lubię skrytek, ale natknąłem się na błąd w git + cygwin, w którym tracę zmiany, więc aby upewnić się, że rzeczy trafiły do ​​dziennika, przynajmniej skonfigurowałem następujące ustawienia:

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

Zasadniczo tworzymy różnicę, która nie obejmuje zmian przestrzeni, cofamy wszystkie nasze zmiany, a następnie stosujemy różnicę.

Frew Schmidt
źródło
1
+1. Możesz zrobić git stashzamiast kasowania, aby mieć kopię zapasową zmian, przynajmniej do czasu przetestowania.
Paŭlo Ebermann
1
Skończysz z wieloma zapasami i zasadniczo nie musisz tak naprawdę robić tego wszystkiego. Działa, ale myślę, że jest trochę niechlujny
Colin Hebert
3
Zgadzam się z Colinem. Jeśli skrypt działa, nie powinno być potrzeby tworzenia skrytki. Warto jednak wziąć pod uwagę ukrywanie, a następnie ukrywanie popu. Wyskakujące skrytki można odzyskać, jeśli to konieczne, ale w przeciwnym razie nie skończy się wiele skrytek. Pozostawia to również dodatkowy plik
Casebash,
Co powiesz na pomijanie plików binarnych? Podczas próby zastosowania powyższego fragmentu pojawiają się błędy, że poprawki nie można zastosować bez pełnej linii indeksu! Uderza mnie to, że nawet nie dotknąłem tych plików / plików binarnych!
tver3305
1
Myślę, że na końcu pierwszego polecenia „git rm foo.patch” powinno być po prostu „rm foo.patch”. W przeciwnym razie bardzo pomocne dzięki.
Jack Casey
33

Utwórz plik łatki zawierający tylko rzeczywiste zmiany (z wyłączeniem linii z jedynie zmianami białych znaków), następnie wyczyść swój obszar roboczy i zastosuj ten plik łatki:

git diff> kopia zapasowa
git diff -w> zmiany
git reset --hard
patch <zmiany

Przejrzyj pozostałe różnice, a następnie addi commitjak zwykle.

Odpowiednikiem Mercurial jest:

hg diff> kopia zapasowa
hg diff -w> zmiany
hg przywróć - wszystkie
import hg - brak zatwierdzenia zmian

Steve Pitchers
źródło
Co to jest „chronione” pytanie? i Ja też odpowiedzi. Nie sądzę, że to nawet kwalifikuje się jako odpowiedź dla mnie, ponieważ wydaje się, że pytanie zostało wyciągnięte z powietrza ...
jww 23'15
4
@jww Sedno pytania oryginalnego plakatu brzmi: „jak uniknąć popełniania jedynie białych zmian w kontroli źródła”. Zdarza się, że OP używa Git, ale dotyczy to również każdego systemu kontroli źródła, z którego kiedykolwiek korzystałem. Ta odpowiedź pokazuje prawidłową procedurę, jeśli ktoś używa Mercurial. Wyobrażam sobie, że ktoś inny może również wnieść rozwiązanie dla osób korzystających z Sublesionu itp.
Steve
1
@jww i @ pagid: Zredagowałem swoją odpowiedź, aby konkretnie adresować Git, stosując to samo podejście, co moje rozwiązanie dla Mercurial. Moim zdaniem StackOverflow to coś więcej niż kolejne forum Q + A - pełni również rolę repozytorium wiedzy. Osoby inne niż oryginalny plakat mogą skorzystać z udzielonych odpowiedzi, a ich sytuacja jest różna. Dlatego uważam, że odpowiedzi przekazujące ogólną zasadę są prawidłowe, a nie dotyczą tylko jednej konkretnej sytuacji.
Steve Pitchers
@ Steve - „Zredagowałem moją odpowiedź, aby konkretnie adresować Git ...” - dlaczego nie zadałeś nowego pytania w kontekście rtęci, a następnie nie dodałeś własnej odpowiedzi do nowego pytania ???
jww
8
Jest to właściwie najczystsze, najbardziej zrozumiałe i niezniszczalne z podejść, jakie widziałem.
Kzqai
12

Odpowiedź najczęściej głosowana nie działa we wszystkich przypadkach, ze względu na spacje w kontekście łatki według użytkowników w komentarzach.

Zmodyfikowałem polecenie w następujący sposób:

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

To generuje łatkę bez kontekstu. Nie powinno to stanowić problemu, ponieważ łatka jest krótkotrwała.

Odpowiedni alias, ponownie wersja tego, co już udostępnili inni użytkownicy:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
void.pointer
źródło
Aby zignorować tylko zmiany wcięć, musiałem --ignore-space-changezamiast tego użyć -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
Andy,
Słowo ostrzeżenia, aby nie używać tej uroczej sztuczki bez kontekstu, --ignore-blank-linesponieważ w przeciwnym razie fragmenty różnic zostaną załatane przy nieprawidłowych przesunięciach, jeśli niektóre zmiany „białych znaków”, które chcesz zignorować, to usunięcie / dodanie pustych linii.
elbeardmorez
12

Dodaj następujące elementy do .gitconfig:

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

Dzięki odpowiedzi @Colin Herbert za inspirację.

Wyjaśnienie składni

Końcowy #musi być cytowany, aby nie był traktowany jako komentarz wewnątrz .gitconfig, ale zamiast tego przechodzi i jest traktowany jako komentarz wewnątrz powłoki - jest wstawiany między końcem git applyargumentów dostarczonych przez użytkownika i gitautomatycznie umieszcza koniec wiersza poleceń. Te argumenty nie są tu potrzebne - nie chcemy git applyich konsumować, stąd poprzedzający znak komentarza. Możesz uruchomić to polecenie, GIT_TRACE=1 git anwaby zobaczyć, jak działa.

Te --sygnały końca argumentów i pozwala na razie, że masz plik o nazwie -wlub coś, co wygląda jak do przełącznika git diff.

W $@celu zachowania wszelkich argumentów cytowanych przez użytkownika wymagane są specjalne znaki cudzysłowu . Jeśli "postać nie ucieknie, zostanie pochłonięta przez .gitconfiganalizator składni i nie dotrze do powłoki.

Uwaga: .gitconfigalias parsowanie nie rozpoznaje apostrofy jako coś specjalnego - jego postacie są tylko specjalne ", \, \n, i ;(poza z "-quoted ciąg). Właśnie dlatego "należy zawsze uciekać, nawet jeśli wygląda na to, że znajduje się w ciągu pojedynczego cudzysłowu (którego git jest całkowicie agnostyczny).

Jest to ważne, np. jeśli masz przydatny alias do wykonania bashpolecenia w katalogu głównym drzewa roboczego. Niepoprawne sformułowanie to:

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

Prawidłowy to:

sh = !bash -c '\"$@\"' -
Tom Hale
źródło
Świetny. To pozwoliło mi dodać jeden plik na raz. Czy oprócz dodania katalogu głównego argumentu istnieje sposób, aby działał jak „git add -A”?
Chucky
7

Co powiesz na następujące:

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

Polecenie w cudzysłowach pobiera nazwy plików, które mają zmiany spoza białych znaków.

karmakaze
źródło
2
lub po prostu, git add `git diff -w |grep '^+++' |cut -c7-`jeśli submoduły nie są używane
karmakaze
-1

Należy najpierw rozważyć, czy końcowe białe znaki są zamierzone. Wiele projektów, w tym jądro Linuksa, Mozilla, Drupal i Kerberos (żeby wymienić tylko kilka ze strony Wikipedii na temat stylu) zabrania spacji. Z dokumentacji jądra Linux:

Zdobądź przyzwoity edytor i nie zostawiaj białych znaków na końcu linii.

W twoim przypadku problem jest odwrotny: poprzednie zatwierdzenia (i być może obecne) nie były zgodne z tą wytyczną.

Założę się, że nikt tak naprawdę nie chce końcowej spacji, a naprawienie problemu może być mile widzianą zmianą. Inni użytkownicy mogą również doświadczać tego samego problemu. Możliwe jest również, że autorzy dodający końcowe białe znaki nie wiedzą, że to robią.

Zamiast próbować ponownie skonfigurować git, aby zignorować problem lub wyłączyć pożądaną w inny sposób funkcję edytora, zacznę od wpisu na liście mailingowej projektu wyjaśniającego problem. Wiele edytorów (i samego gita) można skonfigurować do obsługi białych znaków.

Kevin Vermeer
źródło
16
Nie jest to zamierzone, ale nie mogę zmienić sposobu myślenia ponad 100 osób, które wspierają projekt. Nie przeszkadza im to i nie akceptują łat z ponad 1000 zmianami, które dotyczą tylko końcowych białych znaków. Wiedzą o problemie i postanowili go zignorować. Ta dyskusja odbyła się już na liście i została zamknięta. W tym przypadku to ja muszę się do nich dostosować.
Edu Felipe,
19
Następnie skonfiguruj edytor tak, aby nie przycinał końcowych białych znaków podczas pracy nad kodem tego projektu.
jamessan
-2

Znalazłem hak wstępnego zatwierdzenia git, który usuwa końcowe białe znaki . Jeśli jednak nie możesz zmusić innych do korzystania z tego, może to nie być prawidłowe rozwiązanie.

  #!/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
źródło
4
To pytanie dotyczy sposobu zachowania końcowych białych znaków.
Douglas,
@Douglas: prawdopodobnie można użyć tej odpowiedzi, aby utworzyć zatwierdzenie w tymczasowej gałęzi, zatwierdzić prawdziwą łatkę i wybrać jakoś różnicę tylko do działającej gałęzi ...
Tobias Kienzler