Sprawdź, czy katalog roboczy Git jest czysty ze skryptu


82

Mam skrypt, który działa rsyncz katalogiem roboczym Git jako miejscem docelowym. Chcę, aby skrypt działał inaczej w zależności od tego, czy katalog roboczy jest czysty (żadnych zmian do zatwierdzenia), czy nie. Na przykład, jeśli dane wyjściowe git statussą jak poniżej, chcę, aby skrypt zakończył działanie:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

Jeśli katalog nie jest czysty, to chciałbym, aby wykonał więcej poleceń.

Jak mogę sprawdzić wyniki w powyższym skrypcie powłoki?


Czy sprawdzenie tutaj statusu ostatniego polecenia byłoby pomocne? ($?)
UVV

Czy możesz podać więcej szczegółów? Jaki jest główny pomysł na twój skrypt?
tachomi

@ tachomi Dodałem kontekst w edycji
brentwpeterson

możesz po prostu założyć, że to nie jest czyste i zrobić, git reset --hard origin/branchjeśli to jest to, czego szukasz ... na przykład, jeśli próbujesz
oczyścić

1
@ SnakeDoc Mógłbyś, ale zakładam, że odwrotny przypadek byłby bardziej powszechny, tj. Wyjdź, jeśli katalog roboczy jest brudny, aby uniknąć zakłócenia lokalnych zmian. Rozważenie obu przypadków sprawiłoby, że pytanie byłoby bardziej przydatne dla przyszłych czytelników.
Thomas Nyman

Odpowiedzi:


133

Analiza wyniku git statusjest złym pomysłem, ponieważ jest przeznaczony do odczytu przez człowieka, a nie do odczytu maszynowego. Nie ma gwarancji, że dane wyjściowe pozostaną takie same w przyszłych wersjach Git lub w różnie skonfigurowanych środowiskach.

Komentarz UVV jest na dobrej drodze, ale niestety kod powrotu git statusnie zmienia się w przypadku niezatwierdzonych zmian. Zapewnia jednak --porcelainopcję, która powoduje, że dane wyjściowe git status --porcelainmają być sformatowane w łatwym do przeanalizowania formacie dla skryptów i pozostają stabilne w wersjach Git i niezależnie od konfiguracji użytkownika.

Możemy użyć pustych danych wyjściowych git status --porcelainjako wskaźnika, że ​​nie ma żadnych zmian do zatwierdzenia:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

Jeśli nie dbamy o nieśledzone pliki w katalogu roboczym, możemy użyć --untracked-files=noopcji, aby zignorować te:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

Aby uczynić to bardziej odpornym na warunki, które faktycznie powodują git statusawarię bez wyjścia stdout, możemy zawęzić sprawdzenie, aby:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

Warto również zauważyć, że chociaż git statusnie podaje sensownego kodu wyjścia, gdy katalog roboczy jest nieczysty, git diffudostępnia --exit-codeopcję, która sprawia, że ​​zachowuje się on podobnie do narzędzia diff , to znaczy wychodzenia ze statusem, 1gdy były różnice i 0kiedy nie znaleziono żadnego.

Korzystając z tego, możemy sprawdzić zmiany niestacjonarne za pomocą:

git diff --exit-code

i wprowadził zmiany, ale nie zatwierdził zmian w:

git diff --cached --exit-code

Chociaż git diffmoże raportować o nieśledzonych plikach w podmodułach za pomocą odpowiednich argumentów --ignore-submodules, niestety wydaje się, że nie ma możliwości, aby raportował o nieśledzonych plikach w rzeczywistym katalogu roboczym. Jeśli nieśledzone pliki w katalogu roboczym są istotne, git status --porcelainto prawdopodobnie najlepszy wybór.


4
ughhh git status --porcelainzakończy działanie z kodem 0, nawet jeśli nie zostaną wprowadzone zmiany dla plików zatwierdzania i nieśledzonych.
Alexander Mills

Byłem zainteresowany wcześniejszym określeniem, czy git stashcoś zrobię (nie wyświetla przydatnego kodu powrotu). Musiałem dodać, --ignore-submodulesponieważ inaczej git statuswskazywałoby na zmiany w podmodule, które git stashignorują.
Devin Lane

1
@AlexanderMills: Obserwowałem to samo. Ale potem sprawdziłem, co się if [ -zdzieje. Te -zśrodki, jeśli następujący ciąg jest pusty, jeśli ma wartość true. Innymi słowy, jeśli nie git status --porcelainspowoduje to powstania łańcucha, repozytorium jest czyste. Jeśli nie, wyświetla listę zmodyfikowanych / dodanych / usuniętych plików i nie jest już pustym ciągiem. ifNastępnie ocenia się false.
Adeynack

19

Posługiwać się:

git diff-index --quiet HEAD

Kod powrotu odzwierciedla stan katalogu roboczego (0 = czysty, 1 = brudny). Nieśledzone pliki są ignorowane.


6
Zwraca 0, gdy w bieżącym katalogu znajdują się pliki, które nie są śledzone.
Adam Parkin,

2
Jeśli pliki zostały dotknięte / nadpisane, ale poza tym są identyczne z indeksem, musisz najpierw uruchomić je git update-index --refreshwcześniej git diff-index HEAD. Więcej informacji: stackoverflow.com/q/34807971/1407170
sffc

@AdamParkin Po prostu dodaję wszystkie pliki git add .przed ich wydaniem. Zwykle jest to sposób użycia go w skrypcie
ceztko

To jest świetne. Zauważ, że kod powrotu / wyjścia różny od zera jest również interpretowany jako „błąd”, który jeśli jesteś w skrypcie z zestawem -e, Twój skrypt zakończy działanie, jeśli będzie „brudny”. Można tego uniknąć, robiąc to set +eprzed połączeniem giti dodając set -eponownie po dokonaniu oceny $?.
orion elenzil

1

Niewielkie rozszerzenie doskonałej odpowiedzi André .

Jest to jeden ze sposobów oceny wyników, a także uniknięcia pułapki, jeśli jesteś w skrypcie, który wcześniej wydał zestaw -e .

Nieśledzone pliki są ignorowane.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.