Czy git może zignorować określoną linię?

115

Używam git do synchronizacji z phonegap podczas testowania w natywnej przeglądarce telefonu. Jako taki mam następujący wiersz:

var isPhoneGap = false;

Oczywiście zmieniam to podczas budowania, ale czy istnieje sposób, aby skonfigurować git tak, aby ignorował tę jedną linię, czy też muszę umieścić go w swoim własnym pliku i zignorować w ten sposób?

Używam Gitx i terminala na OSX 10.6.

Iolo
źródło
@ user494461: Filtry Git to sposób na zrobienie tego: stackoverflow.com/a/16244970/520162
eckes
1
Czy nie możesz wykryć środowiska i użyć pliku konfiguracyjnego środowiska?
exussum
W skrócie: nie. Ale napisałem skrypt preprocesora, który szuka pewnego zakomentowanego kodu i usuwa go przed opublikowaniem do gita. Sprawdź to tutaj: github.com/franklinchou/ahk_config/blob/master/preprocess.sh
franklin

Odpowiedzi:

104

Jeśli twój plik jest określonego typu, możesz zadeklarować sterownik filtru zawartości , który możesz zadeklarować w .gitattributespliku (jak przedstawiono w sekcji „Rozszerzanie słów kluczowych” w „ Atrybuty Git ”):

http://git-scm.com/figures/18333fig0702-tn.png

*.yourType filter=yourFilterName

(jeśli chcesz, możesz nawet ustawić ten filtr dla określonego pliku )

Wprowadzić w życie:

  • yourFilterName.smudge(uruchomione git checkout) i

    git config --global filter.yourFilterName.smudge 'sed "s/isPhoneGap = .*/isPhoneGap = true/"'
    
  • yourFilterName.clean(uruchomione git add)

    git config --global filter.yourFilterName.clean 'sed "s/isPhoneGap = .*/isPhoneGap = false/"'
    

Twój plik wyglądałby na niezmieniony w dniu git status, ale jego wyewidencjonowana wersja miałaby odpowiednią wartość dla isPhoneGap.

VonC
źródło
1
Dzięki za to. to działa. Czy wiesz, jak git diff or ustawić status gita, zignoruj ​​jednak filtry? Więc nadal widzę, co się zmieniło? Mój przypadek użycia dotyczy dzienników debugowania ... które ostatecznie chcę usunąć ... @jthill @VonC
timh
1
@timh jeśli filtr działa, status git lub git diff nie powinny nic pokazywać.
VonC
1
szkoda, że ​​w GIT nie ma prostszej drogi. git ignore <nazwa pliku> <numer linii> byłby bardzo przydatny!
kiedysktos
22
@kiedysktos numer bielizny? Co się stanie, jeśli zmieni się numer linii?
VonC
To działało świetnie, ale użytkownicy Windowsa uważajcie, gitconfig jest bardzo wybredny, jeśli chodzi o to, które znaki chce przechowywać, więc podczas wypróbowywania niektórych pół-egzotycznych wyrażeń regularnych możesz napotkać kłopoty. Skończyło się na umieszczeniu wywołań sed w osobnym pliku helper.sh, który wywołałem z mojego gitconfig, sh ".git/helper.sh"upewniając się, że przekazałem wszystkie parametry do seda "$@"(zakładam, że przekazana jest tylko ścieżka pliku ).
ohaal
43

Możesz użyć

git update-index --assume-unchanged [file]

ignorować zmiany jednego pliku, którego nie chcesz śledzić. Używam tego rozwiązania, gdy potrzebuję mieć plik w repozytorium, ale ten plik ma pewne części, które się zmieniają, że nie zawsze potrzebuję śledzenia.

Gdy plik zawiera ważne zmiany, musisz to zrobić:

git update-index --no-assume-unchanged [file]

Zobacz także Git doc update-index dla --[no-]assume-unchangedparametru.

Gdy te flagi są określone, nazwy obiektów zarejestrowane dla ścieżek nie są aktualizowane. Zamiast tego opcje te ustawiają i usuwają bit „zakładaj niezmieniony” dla ścieżek. Kiedy bit "zakładaj niezmieniony" jest włączony, git przestaje sprawdzać pliki drzewa roboczego pod kątem możliwych modyfikacji, więc musisz ręcznie usunąć ten bit, aby poinformować git o zmianie pliku drzewa roboczego.

Carlos
źródło
2
Czy zrobienie tego nadal spowoduje pobranie nowej wersji, gdy wykonam polecenie git pull?
Saad Rehman Shah
1
@Caffeine Kiedy wykonasz git pull, otrzymasz ostatnią zatwierdzoną wersję, będzie to ostatnia przed zmianą pliku na „--assume-unchanged”.
Carlos
1
Uważam, że --skip-worktreejest to lepsza opcja do większości zastosowań. stackoverflow.com/a/39583010/4233593
Jeff Puckett
3
to ignoruje aktualizacje całego pliku, a nie określonej linii
nikoss,
6

Gitx powinien pozwolić ci na zatwierdzenie lub zignorowanie poszczególnych linii (możesz to już wiedzieć), ale musiałbyś to robić za każdym razem, gdy zatwierdzasz. Myślę, że byłoby lepiej mieć plik konfiguracyjny dla każdego celu wdrożenia (możesz je wersjonować) i jakiś parametr wykonawczy dla tego, jak uruchamiasz serwer (jak ./myserver --config=whatever.js).

Andy Ray
źródło
5

Kontynuując https://stackoverflow.com/a/20574486/4935114 , @Mike zaproponował utworzenie pre-commithaka, który będzie grepw plikach pomostowych dla linii, które można zignorować. Haczyk sprawdza, czy te linie zostały ustawione. Jeśli tak, jest to echoostrzeżenie i exitzawiera kod, 1więc proces zatwierdzania nie będzie kontynuowany.

Zainspirowany odpowiedzią @ Mike'a , znalazłem być może używam ulepszonej wersji jego haka, który automatycznie reset (z -pflagą) łączy się z określoną linią, którą chcemy zignorować.

Nie jestem pewien, czy ten punkt zaczepienia zadziała w sytuacji, gdy masz wiele plików z tą linią do zignorowania, ale ten pre-commitpunkt zaczepienia szuka zmiany w tym wierszu w określonym pliku buildVars.java. Skrypt przechwytujący wyglądał tak, kiedy testowałem go na moim komputerze.

#!/bin/sh

# this hook looks for lines with the text `var isPhoneGap = false;` in the file `buildVars.java` and it resets these lines to the previous state before staged with `reset -p`

if [[ $(git diff --no-ext-diff --cached buildVars.java | grep --count -e "var\ isPhoneGap[\ ]*=[\ ]*") -ne 0 ]]; then
    cat <<EOW
WARNING: You are attempting to commit changes which are not supposed to be commited according to this \`pre-commit\` hook
This \`pre-commit\` hook will reset all the files containing this line to it's previous state in the last commit.
EOW
    echo /$'\n'isPhoneGap$'\n'y$'\n'q | git reset -p
    # BONUS: Check if after reseting, there is no actual changes to be commited and if so, exit 1 so the commit process will abort.
    if [[ $(git diff --no-ext-diff --cached | wc -l) -eq 0 ]]; then
        echo there are no actual changes to be commited and besides the change to the variable \'isPhoneGap\' so I won\'t commit.
        exit 1
    fi
fi

Wyjaśnienie

To, co zrobiłem, to powtórzenie sekwencji kontrolnych, które wyszukują wyrażenie regularne isPhoneGappodczas resetprocesu interaktywnego . W ten sposób emulując użytkownika, który naciska, /aby wyszukać isPhoneGap, naciska, ygdy pyta, czy chce odrzucić tę poprawkę, a na koniec naciska, qaby wyjść z aplikacji interaktywnej reset.

Interaktywny proces odwróconej łatki jest udokumentowany tutaj: https://git-scm.com/docs/git-add#git-add-patch


UWAGA: Powyższy skrypt przy założeniu, że zmienną interactive.singleKeyjest false. Jeśli skonfigurowałeś swoje true, usuń je $'\n'z echopolecenia zaraz po ostrzeżeniu.

Doron Behar
źródło
3

Oto jak możesz to zrobić za pomocą filtrów git :

  1. Utwórz / otwórz plik gitattributes:
    • <project root> /. gitattributes (zostaną zatwierdzone w repozytorium)
      LUB
    • <Project root> /. git / info / attributes (nie zostaną zatwierdzone w repozytorium)
  2. Dodaj linię definiującą pliki do filtrowania:
    • *.rb filter=gitignore, tj. uruchom filtr o nazwie gitignorena wszystkich *.rbplikach
  3. Zdefiniuj gitignorefiltr w gitconfig:
    • $ git config --global filter.gitignore.clean "sed '/#gitignore$/'d", tj. usuń te wiersze
    • $ git config --global filter.gitignore.smudge catczyli nic nie rób podczas ściągania pliku z repozytorium

Uwagi:
Oczywiście dotyczy to plików ruby, stosowanych, gdy linia kończy się na #gitignore, stosowanych globalnie w ~/.gitconfig. Zmodyfikuj to, jak potrzebujesz do swoich celów.

Ostrzeżenie!!
To sprawia, że ​​plik roboczy różni się od repozytorium (oczywiście). Każde wymeldowanie lub zmiana bazy będzie oznaczać, że te linie zostaną utracone! Ta sztuczka może wydawać się bezużyteczna, ponieważ te wiersze są wielokrotnie gubione podczas sprawdzania, ponownego bazowania lub ściągania, ale mam określony przypadek użycia, aby z niego skorzystać.

Tylko wtedy, git stash save "proj1-debug" gdy filtr jest nieaktywny (po prostu tymczasowo go wyłącz, gitconfigczy coś). W ten sposób mój kod debugowania może być zawszegit stash apply dołączony do mojego kodu w dowolnym momencie bez obawy, że te wiersze zostaną przypadkowo zatwierdzone.

Mam pomysł na rozwiązanie tych problemów, ale spróbuję go zrealizować innym razem.

Podziękowania dla Rudiego i jw013 za wspomnienie o filtrach git i atrybutach git.

Kache
źródło
2

Sterownik filtru treści nie jest dobrym rozwiązaniem. Możesz ukryć tę linię przed git status/ etc, ale tak naprawdę nie jest ona ignorowana. Jak tylko zmienisz wartość, twój katalog roboczy zostanie oznaczony jako brudny, nawet jeśli zmiana może nie być widoczna.

Jeśli naprawdę chcesz, aby ta linia była poza kontrolą wersji, zmiana jej na argument wiersza poleceń lub umieszczenie w ignorowanym pliku dołączania lub kompilacji może być jedynym praktycznym sposobem.

patricktokeeffe
źródło
„Jak tylko zmienisz wartość, twój katalog roboczy zostanie oznaczony jako brudny, nawet jeśli zmiana może nie być widoczna”: nie powinno, jeśli czysty skrypt nadal przywraca plik w jego oryginalnej zawartości.
VonC
Tak właśnie myślałem. Używam GitExtensions i pokaże plik jako zmodyfikowany, nawet jeśli okienko różnic jest puste. Prawdopodobnie dlatego, że czysty filtr nie jest uruchamiany do momentu wpisania pliku. Prawdopodobnie dlatego, że jest to msysgit, a nie git-git. IDK, YMMV
patricktokeeffe
1

Domyślam się, że może to być coś, co pojawia się w więcej niż jednej linii twojego źródła.

Myślę, że najczystszym byłoby posiadanie jakiegoś pliku .userbuildconfig, który po prostu dołączasz i wpisujesz wartości domyślne. Następnie możesz skorzystać z sugestii Carlosa, aby oznaczyć ten plik jako niezmieniony. W ten sposób inne zmiany w pliku, w których musisz sprawdzić ustawienie, nie zostaną pominięte.

Może to pozwolić na lokalne dostrojenie makr preprocesora (lub w przypadku java, coś takiego jak to https://stackoverflow.com/a/1813873/1270965 ).

johnb003
źródło
-1

Nie. Możesz ignorować tylko pojedyncze pliki (i więcej), ponieważ wiersze w .gitignore pasują do nazw plików, a nie do zawartości pliku. Wspomniałeś już o rozwiązaniu tego problemu, tj. Zignoruj ​​pojedynczy plik zawierający zawartość, którą chcesz zignorować.

ralphtheninja
źródło
Zgadzam się, w moim przypadku wystarczyło i możliwe było po prostu włączenie tej treści z ignorowanego pliku. Więc przenoszę wszystkie ignorowane ustawienia. tam. Utworzyłem również kopię tego zignorowanego pliku i dodałem go do indeksu jako odniesienie, więc informacje o tym, co ma być w tym pliku, nie zostaną utracone.
Urs