Stellen Sie fest, ob das Git-Arbeitsverzeichnis in einem Skript sauber ist

83

Ich habe ein Skript, das rsyncmit einem Git-Arbeitsverzeichnis als Ziel ausgeführt wird. Ich möchte, dass das Skript ein anderes Verhalten aufweist, je nachdem, ob das Arbeitsverzeichnis sauber ist (keine Änderungen zum Festschreiben) oder nicht. Wenn zum Beispiel die Ausgabe von wie git statusfolgt ist, möchte ich, dass das Skript beendet wird:

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

Wenn das Verzeichnis nicht sauber ist, möchte ich, dass es weitere Befehle ausführt.

Wie kann ich in einem Shell-Skript nach Ausgaben wie den oben genannten suchen?

brentwpeterson
quelle
Wäre es hier hilfreich, den Status des letzten Befehls zu überprüfen? ($?)
UVV
Könnten Sie bitte nähere Angaben machen? Was ist die Hauptidee für Ihr Skript?
Tachomi
@ Tachomi Ich fügte den Kontext in der Bearbeitung
Brentwpeterson
Sie könnten einfach annehmen, dass es nicht sauber ist, und dann machen Sie ein, git reset --hard origin/branchwenn Sie das wollen ... zum Beispiel, wenn Sie versuchen, nach dem Kompilieren von etwas usw.
aufzuräumen
1
@ SnakeDoc Sie könnten, aber ich gehe davon aus, dass der umgekehrte Fall häufiger vorkommt, dh, Sie beenden, wenn das Arbeitsverzeichnis verschmutzt ist, um lokale Änderungen nicht zu beschädigen. In beiden Fällen wäre die Frage für zukünftige Leser nützlicher.
Thomas Nyman

Antworten:

134

Das Parsen der Ausgabe von git statusist eine schlechte Idee, da die Ausgabe für den Menschen lesbar und nicht maschinenlesbar sein soll. Es gibt keine Garantie dafür, dass die Ausgabe in zukünftigen Versionen von Git oder in anders konfigurierten Umgebungen gleich bleibt.

UVVs Kommentar ist auf dem richtigen Weg, aber leider git statusändert sich der Rückkehrcode von nicht, wenn nicht festgeschriebene Änderungen vorliegen. Es bietet jedoch die --porcelainOption, dass die Ausgabe git status --porcelainin einem leicht zu analysierenden Format für Skripte formatiert wird und in allen Git-Versionen und unabhängig von der Benutzerkonfiguration stabil bleibt.

Wir können die leere Ausgabe von git status --porcelainals Indikator dafür verwenden, dass keine Änderungen festgeschrieben werden müssen:

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

Wenn uns nicht verfolgte Dateien im Arbeitsverzeichnis egal sind, können wir diese mit der --untracked-files=noOption ignorieren:

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

Um dies robuste gegenüber Bedingungen , die tatsächlich dazu führen , git statuszum Scheitern verurteilt , ohne Ausgang stdout, können wir den Scheck verfeinern:

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

Es ist auch erwähnenswert, dass, obwohl git statuses keinen aussagekräftigen Exit-Code gibt, wenn das Arbeitsverzeichnis unsauber ist, git diffdie --exit-codeOption zur Verfügung steht, mit der es sich ähnlich wie das Dienstprogramm diff verhält , dh mit dem Status beendet wird, 1wenn es Unterschiede gab und 0wenn keine gefunden wurden.

Auf diese Weise können wir nach nicht bereitgestellten Änderungen suchen:

git diff --exit-code

und inszenierte, aber nicht festgeschriebene Änderungen mit:

git diff --cached --exit-code

Obwohl git diffnicht verfolgte Dateien in Submodulen über entsprechende Argumente an gemeldet werden können --ignore-submodules, scheint es leider keine Möglichkeit zu geben, nicht verfolgte Dateien im tatsächlichen Arbeitsverzeichnis zu melden. Wenn nicht verfolgte Dateien im Arbeitsverzeichnis relevant sind, git status --porcelainist dies wahrscheinlich die beste Wahl.

Thomas Nyman
quelle
4
ughhh git status --porcelainwird mit Code 0 beendet, auch wenn Änderungen für festgeschriebene und nicht überwachte Dateien nicht gespeichert wurden.
Alexander Mills
Ich war daran interessiert, im Voraus zu bestimmen, ob git stashetwas unternommen werden soll (es wird kein nützlicher Rückkehrcode ausgegeben). Ich musste hinzufügen, --ignore-submodulesda sonst git statusSubmoduländerungen angezeigt würden, die git stashignoriert werden.
Devin Lane
1
@AlexanderMills: Ich habe dasselbe beobachtet. Aber dann habe if [ -zich nachgesehen, was passiert ist. Das -zbedeutet, wenn die folgende Zeichenfolge leer ist, wird das if zu ausgewertet true. Mit anderen Worten, wenn dies git status --porcelainzu keiner Zeichenfolge führt, ist das Repo sauber. Wenn nicht, werden die geänderten / hinzugefügten / entfernten Dateien aufgelistet und es handelt sich nicht mehr um eine leere Zeichenfolge. Der ifwertet dann auf false.
Adeynack
19

Verwenden:

git diff-index --quiet HEAD

Der Rückkehrcode gibt den Status des Arbeitsverzeichnisses wieder (0 = sauber, 1 = schmutzig). Nicht überwachte Dateien werden ignoriert.

André van Herk
quelle
6
Gibt 0 zurück, wenn sich im aktuellen Verzeichnis nicht verfolgte Dateien befinden.
Adam Parkin
2
Wenn Dateien berühren / überschrieben worden sind ansonsten aber identisch mit dem Index, müssen Sie zuerst Lauf git update-index --refreshvor git diff-index HEAD. Weitere Informationen: stackoverflow.com/q/34807971/1407170
sffc
@AdamParkin Ich füge einfach alle Dateien mit hinzu, git add .bevor ich sie ausstelle. Normalerweise wird es in einem Skript so verwendet
ceztko
Das ist toll. Beachten Sie, dass ein Rückkehr- / Beendigungscode ungleich Null auch als "Fehler" interpretiert wird. Wenn Sie sich in einem Skript mit set -e befinden, wird Ihr Skript beendet, wenn es "unsauber" ist. Dies kann vermieden werden, indem Sie dies set +evor dem Aufruf von tun gitund set -enach der Auswertung erneut hinzufügen $?.
Orion Elenzil
1

Geringfügige Erweiterung von André's exzellenter Antwort .

Dies ist eine Möglichkeit, die Ergebnisse auszuwerten und auch eine Falle zu vermeiden, wenn Sie sich in einem Skript befinden, das zuvor set -e ausgegeben hat .

Nicht überwachte Dateien werden ignoriert.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
orion elenzil
quelle