Wie kann ich überprüfen, ob in der aktuellen Filiale nichts festgeschrieben werden kann?

172

Ziel ist es, einen eindeutigen Status zu erhalten, der in einem Shell-Befehl ausgewertet werden kann.

Ich habe es versucht, git statusaber es gibt immer 0 zurück, auch wenn Elemente festgeschrieben werden müssen.

git status
echo $?  #this is always 0

Ich habe eine Idee, aber ich denke, es ist eher eine schlechte Idee.

if [ git status | grep -i -c "[a-z]"> 2 ];
then
 code for change...
else
  code for nothing change...
fi

auf andere Weise?


Update mit folgender Lösung, siehe Mark Longairs Beitrag

Ich habe es versucht, aber es verursacht ein Problem.

if [ -z $(git status --porcelain) ];
then
    echo "IT IS CLEAN"
else
    echo "PLEASE COMMIT YOUR CHANGE FIRST!!!"
    echo git status
fi

Ich erhalte den folgenden Fehler [: ??: binary operator expected

Jetzt schaue ich den Mann an und probiere den Git Diff aus.

=================== Code für meine Hoffnung und hoffentlich bessere Antwort ======================

#if [ `git status | grep -i -c "$"` -lt 3 ];
# change to below code,although the above code is simple, but I think it is not strict logical
if [ `git diff --cached --exit-code HEAD^ > /dev/null && (git ls-files --other --exclude-standard --directory | grep -c -v '/$')` ];
then
        echo "PLEASE COMMIT YOUR CHANGE FIRST!!!"
    exit 1

else
    exit 0
fi
9nix00
quelle
4
Im aktualisierten Abschnitt scheinen Sie nicht das zu tun, was eckes in seiner Antwort vorschlägt - wie er sagt, müssen Sie doppelte Anführungszeichen um das setzen $(git status --porcelain). Wenn Sie Ausrufezeichen in Ihre Nachricht einfügen möchten, müssen Sie einfache Anführungszeichen anstelle von doppelten Anführungszeichen verwenden - dh es sollte echo 'PLEASE COMMIT YOUR CHANGE FIRST!!!'stattdessen sein
Mark Longair
4
wie Mark sagt: Sie müssen doppelte Anführungszeichen setzen$(git status --porcelain) , so wie ich es Ihnen gesagt habe!
eckes
1
Diese Fragen wären viel nützlicher, wenn sie keine Teile der Antworten enthalten würden.
Oberlies
@ 9nix00 tun Sie, was Ihnen gesagt wurde, und bearbeiten und beheben Sie den Fehler in Ihrem Shell-Skript oben: BUG: if [-z $ (ein Befehl)] FIX: if [-z "$ (ein Befehl)"]
MarcH

Antworten:

231

Eine Alternative zum Testen, ob die Ausgabe von git status --porcelainleer ist, besteht darin, jede Bedingung, die Sie interessiert, separat zu testen. Es ist beispielsweise nicht immer wichtig, ob die Ausgabe von nicht verfolgte Dateien enthält git status.

Um beispielsweise festzustellen, ob lokale nicht bereitgestellte Änderungen vorliegen, können Sie den Rückkehrcode von:

git diff --exit-code

Um zu überprüfen, ob Änderungen bereitgestellt, aber nicht festgeschrieben wurden, können Sie den Rückkehrcode verwenden:

git diff --cached --exit-code

Wenn Sie wissen möchten, ob sich in Ihrem Arbeitsbaum nicht verfolgte Dateien befinden, die nicht ignoriert werden, können Sie testen, ob die Ausgabe des folgenden Befehls leer ist:

git ls-files --other --exclude-standard --directory

Update: Sie fragen unten, ob Sie diesen Befehl ändern können, um die Verzeichnisse in der Ausgabe auszuschließen. Sie können leere Verzeichnisse durch Hinzufügen ausschließen --no-empty-directory, aber um alle Verzeichnisse in dieser Ausgabe auszuschließen, müssen Sie die Ausgabe filtern, z. B.:

git ls-files --other --exclude-standard --directory | egrep -v '/$'

Das -vto egrepbedeutet, nur Zeilen auszugeben, die nicht mit dem Muster übereinstimmen, und das Muster stimmt mit jeder Zeile überein, die mit a endet /.

Mark Longair
quelle
Ich habe diese Tipps gelernt und habe ein Problem. Verwenden Sie also git ls-files --other --exclude-standard --directory, um die Liste include-Verzeichnisse abzurufen. Gibt es eine Möglichkeit, dieses Verzeichnis auszuschließen?
9nix00
Ja, das will ich. und ich aktualisiere meinen Beitrag für neuen Skriptcode. Ich denke, dein Vorschlag ist strenger logisch, obwohl mehr Code lol ... und hoffe, dass bessere Antworten erscheinen.
9nix00
3
@albfan: Es steht in der git-diff-Manpage : "Beenden Sie das Programm mit Codes, die diff (1) ähnlich sind. Das heißt, es wird mit 1 beendet, wenn es Unterschiede gab, und 0 bedeutet keine Unterschiede."
Mark Longair
Nur um darauf hinzuweisen, es ist mindestens seit 2007 da 13da0fc0 , sehr praktisch für Shell-Skripte und voll kompatibel mit älteren Versionen von git
albfan
9
--quiet(was impliziert --exit-code) bringt auch die Ausgabe zum Schweigen, für diejenigen, die nur den Exit-Code wollen.
Phs
113

Der Rückgabewert von gibt git statusnur den Exit-Code von an git status, nicht, ob Änderungen festgeschrieben werden müssen.

Wenn Sie eine computerlesbarere Version der git statusAusgabe wünschen , versuchen Sie es

git status --porcelain

git statusWeitere Informationen hierzu finden Sie in der Beschreibung von .

Beispielverwendung (Skript testet einfach, ob git status --porcelaineine Ausgabe vorliegt, keine Analyse erforderlich):

if [ -n "$(git status --porcelain)" ]; then
  echo "there are changes";
else
  echo "no changes";
fi

Bitte beachten Sie, dass Sie die zu testende Zeichenfolge angeben müssen, dh die Ausgabe von git status --porcelain. Weitere Hinweise zu Testkonstrukten finden Sie im Advanced Bash Scripting Guide (Abschnitt Zeichenfolgenvergleich ).

eckes
quelle
Hallo, du gibst einen guten Vorschlag, ich habe es versucht, aber im Skript verursacht es ein Problem, ich habe es verbessert, wenn wir dies verwenden, wenn [-z $ (git status --porcelain)]; es wird ein Fehler angezeigt, [: ??: Binäroperator erwartet Ich finde das Handbuch und benutze dieses, wenn [-z $ (git status --short)]; das kann funktionieren, danke!
9nix00
Entschuldigung, es gibt immer noch ein Problem. wenn das Commit sauber ist. Verwenden Sie Porzellan und kurz beide ok. aber wenn Commit nicht sauber ist. es wird einen Fehler verursachen. [: ??: Binäroperator erwartet. Ich denke, vielleicht sollten wir versuchen, base64 zu verwenden, um es zu codieren. Lass es mich versuchen! Herunterladen von Base64-Befehlstools .... lol
9nix00
Es verursacht ein Problem, wenn das Commit nicht sauber ist.
9nix00
1
Tolle Lösung. Für zusätzliche Robustheit können Sie || echo nodie Befehlsersetzung anhängen , damit der Arbeitsbereich nicht fälschlicherweise als sauber gemeldet wird, wenn er git statusgrundlegend fehlschlägt. Außerdem ist Ihr Code (lobenswerterweise) POSIX-kompatibel. Da Sie jedoch einen Link zu einem Bash-Handbuch erstellen , möchte ich hinzufügen, dass Sie die Befehlsersetzung nicht doppelt zitieren müssen , wenn Sie Bashs [[ ... ]]anstelle von POSIX-kompatibel verwenden [ ... ]schadet nicht) : [[ -z $(git status --porcelain) ]].
mklement0
@eckes Ich habe vor ein paar Tagen ein neues Repo gestartet. Ich habe ein Commit hinzugefügt. Ich habe versucht, einen Pre-Commit-Hook zu schreiben und zu überprüfen, was festgeschrieben werden soll und was Sie sagen, hat in meinem Fall nicht funktioniert.
Alinsoar
33

Wenn Sie wie ich sind, möchten Sie wissen, ob es:

1) Änderungen an vorhandenen Dateien 2) neu hinzugefügte Dateien 3) gelöschte Dateien

und insbesondere nicht über 4) nicht verfolgte Dateien wissen wollen.

Dies sollte es tun:

git status --untracked-files=no --porcelain

Hier ist mein Bash-Code zum Beenden des Skripts, wenn das Repo sauber ist. Es wird die Kurzversion der Option für nicht verfolgte Dateien verwendet:

[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;
Moodboom
quelle
4
+1 für —untracked-files=no; Ich denke, Ihr Test kann vereinfacht werden [[ -z $(git status --untracked-files=no --porcelain) ]]. git statussollte nicht auf stderr schreiben, es sei denn , etwas Grundsätzliches schief geht - und dann Sie tun wollen , dass Ausgabe zu sehen. (Wenn Sie in diesem Fall ein robusteres Verhalten wünschen, hängen Sie es || echo noan die Befehlsersetzung an, damit der Test auf Sauberkeit immer noch fehlschlägt.) -zZeichenfolgenvergleiche / Der Operator kann mit mehrzeiligen Zeichenfolgen umgehen - keine Notwendigkeit tail.
mklement0
2
danke @ mklement0, noch ein bisschen kürzer:[[ -z $(git status -u no --porcelain) ]]
Moodboom
1
Korrektur: Meine kürzere Version überprüft tatsächlich nur den Status der Datei mit dem Namen "no"! Bzzt. Sollte sein: [[ -z $(git status -uno --porcelain) ]]
Moodboom
2
Ich schätze das Follow-up; Das ist ein subtiler Fehler - die Lektion ist, dass bei kurzen Optionen mit optionalen Argumenten das Argument direkt angehängt werden muss , ohne Leerzeichen dazwischen. Wie wäre es, wenn Sie die korrigierte Kurzversion direkt in Ihre Antwort aufnehmen?
mklement0
9

Es ist möglich, git status --porcelainmit einem einfachen grepTest durchzuführen.

if git status --porcelain | grep .; then
    echo Repo is dirty
else
    echo Repo is clean
fi

Ich benutze dies manchmal als einfachen Einzeiler:

# pull from origin if our repo is clean
git status --porcelain | grep . || git pull origin master

Fügen Sie -qsIhren grep-Befehl hinzu, um ihn stumm zu schalten.

Steve Prentice
quelle
2
+1 für Eleganz; leichte Einschränkung: git statusSollte ein tödlicher Fehler auftreten (z. B. ein beschädigtes Repo), meldet Ihr Test fälschlicherweise einen sauberen Arbeitsbereich. Eine Option ist die Verwendung git status --porcelain 2>&1, aber das würde die Fehlermeldung "auffressen", wenn Sie grep mit verwenden -q. (Mit dem Umgang verlöre die Eleganz: (git status --porcelain || echo err) | grep -q .)
mklement0
alternativ kann man schreiben:test -z "$(git status --porcelain)" || git pull origin master
VasiliNovikov
5

Aus dem Git-Quellcode gibt es ein sh-Skript, das Folgendes enthält.

require_clean_work_tree () {
    git rev-parse --verify HEAD >/dev/null || exit 1
    git update-index -q --ignore-submodules --refresh
    err=0

    if ! git diff-files --quiet --ignore-submodules
    then
        echo >&2 "Cannot $1: You have unstaged changes."
        err=1
    fi

    if ! git diff-index --cached --quiet --ignore-submodules HEAD --
    then
        if [ $err = 0 ]
        then
            echo >&2 "Cannot $1: Your index contains uncommitted changes."
        else
            echo >&2 "Additionally, your index contains uncommitted changes."
        fi
        err=1
    fi

    if [ $err = 1 ]
    then
        test -n "$2" && echo >&2 "$2"
        exit 1
    fi
}

Dieses Sniplet zeigt, wie es verwendet werden kann git diff-filesund git diff-indexob Änderungen an zuvor bekannten Dateien vorgenommen wurden. Sie können jedoch nicht herausfinden, ob dem Arbeitsbaum eine neue unbekannte Datei hinzugefügt wurde.

Pfeilmeister
quelle
Dies funktioniert gut, außer für neue Dateien. wir können dies hinzufügen. wenn ! git ls-files --other --exclude-standard --directory | grep -c -v '/ $' dann beenden Sie 0 else echo "Bitte schreiben Sie Ihre neue Datei fest. Wenn Sie sie nicht hinzufügen möchten, fügen Sie sie bitte in der git-ignore-Datei hinzu." Ausfahrt 1 fi
9nix00
Nur if [ -n "$(git ls-files --others --exclude-standard)" ]ohne zusätzliche Piping oder Greping sollte ausreichen, um nicht verfolgte Dateien zu erkennen.
Arrowmaster
5

Ich würde einen Test dazu machen:

git diff --quiet --cached

oder dies um explizit zu sein:

git diff --quiet --exit-code --cached

wo:

--exit-code

Beenden Sie das Programm mit Codes ähnlich wie bei diff (1). Das heißt, es wird mit 1 beendet, wenn es Unterschiede gab, und 0 bedeutet keine Unterschiede.

--ruhig

Deaktivieren Sie alle Ausgaben des Programms. Impliziert --exit-Code

archf
quelle
3

Ich bin etwas spät in der Diskussion, aber wenn Sie nur einen Exit-Code von 0 benötigen, wenn git status --porcelainnichts zurückgegeben wird und! = 0, versuchen Sie Folgendes:

exit $( git status --porcelain | wc -l )

Dadurch wird die Anzahl der Zeilen zum Exit-Code. Bei mehr als 255 Zeilen besteht die Gefahr von Problemen. So

exit $( git status --porcelain | head -255 | wc -l )

wird das erklären;)

Skeeve
quelle
1
Dies ist grundsätzlich undefiniert, wenn mehr als 255 Ausgabezeilen vorhanden sind.
Tripleee
Gut gesehen, danke!
Skeeve
2

Ich benutze dies in einem Skript, um:

  • 0 wenn alles sauber ist
  • 1, wenn es diff oder nicht verfolgte Dateien gibt

    [-z "$ (Git-Status - Porzellan)"]

gröber
quelle
Verwenden if ! git diff --quiet; thenist sauberer und performanter (denke ich). Mit anderen Worten, verwenden Sie den Exit-Code, nicht den Standard-Code.
Alexander Mills
1
@AlexanderMills git diff --quietverhält sich anders als git status --porcelainbei zwischengespeicherten Änderungen.
Martin von Wittich
0

Nicht schön, funktioniert aber:

git status | grep -qF 'working directory clean' || echo "DIRTY"

Sie sind sich nicht sicher, ob die Nachricht vom Gebietsschema abhängig ist LANG=C.

Tilman Vogel
quelle