Determina se la directory di lavoro di Git è pulita da uno script


84

Ho uno script che gira rsynccon una directory di lavoro Git come destinazione. Voglio che lo script abbia un comportamento diverso a seconda che la directory di lavoro sia pulita (nessuna modifica da confermare), oppure no. Ad esempio, se l'output di git statusè come di seguito, voglio che lo script esca:

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

Se la directory non è pulita, vorrei che eseguisse altri comandi.

Come posso verificare l'output come sopra in uno script di shell?


Controllare uno stato dall'ultimo comando sarebbe utile qui? ($?)
UVV

Potresti fornire maggiori dettagli per favore? Qual è l'idea principale per la tua sceneggiatura?
Tachomi,

@tachomi Ho aggiunto il contesto nella modifica
brentwpeterson,

potresti semplicemente supporre che non sia pulito e fare un git reset --hard origin/branchse questo è quello che stai cercando ... come se stai cercando di ripulire dopo aver compilato qualcosa, ecc.
SnakeDoc

1
@SnakeDoc Potresti, ma presumo che il caso inverso sarebbe più comune, cioè esci se la directory di lavoro è sporca per evitare di alterare le modifiche locali. Considerare entrambi i casi renderebbe la domanda più utile per i futuri lettori.
Thomas Nyman,

Risposte:


135

Analizzare l'output di git statusè una cattiva idea perché l'output deve essere leggibile dall'uomo, non leggibile dalla macchina. Non esiste alcuna garanzia che l'output rimarrà lo stesso nelle versioni future di Git o in ambienti configurati in modo diverso.

Il commento di UVV è sulla buona strada, ma sfortunatamente il codice di ritorno di git statusnon cambia quando ci sono cambiamenti non impegnati. Fornisce, tuttavia, l' --porcelainopzione, che consente di git status --porcelainformattare l'output di in un formato facile da analizzare per gli script e rimarrà stabile tra le versioni di Git e indipendentemente dalla configurazione dell'utente.

Possiamo usare l'output vuoto di git status --porcelaincome indicatore che non ci sono cambiamenti da impegnare:

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

Se non ci interessa i file non tracciati nella directory di lavoro, possiamo usare l' --untracked-files=noopzione per ignorare quelli:

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

Per renderlo più robusto rispetto alle condizioni che in realtà causano un git statuserrore senza output stdout, possiamo perfezionare il controllo per:

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

Vale anche la pena notare che, sebbene git statusnon fornisca un codice di uscita significativo quando la directory di lavoro è sporca, git difffornisce l' --exit-codeopzione, che la rende simile all'utilità diff , ovvero uscire con lo stato 1quando c'erano differenze e 0quando non ne sono state trovate.

Usando questo, possiamo verificare le modifiche non messe in scena con:

git diff --exit-code

e messo in scena modifiche ma non impegnate con:

git diff --cached --exit-code

Sebbene git diffpossa riferire sui file non tracciati nei sottomoduli tramite argomenti appropriati --ignore-submodules, sfortunatamente sembra che non ci sia modo di farlo riportare sui file non tracciati nella directory di lavoro effettiva. Se i file non tracciati nella directory di lavoro sono rilevanti, git status --porcelainè probabilmente la scommessa migliore.


4
ughhh git status --porcelainuscirà con il codice 0 anche se ci sono modifiche non messe in scena per i file di commit e non tracciati.
Alexander Mills,

Ero interessato a determinare in anticipo se git stashfarebbe qualsiasi cosa (non genera un utile codice di ritorno). Ho dovuto aggiungere --ignore-submodulescome altrimenti git statusindicherebbe modifiche al sottomodulo che git stashignora.
Devin Lane,

1
@AlexanderMills: ho osservato lo stesso. Ma poi ho controllato cosa if [ -zstava facendo. Ciò -zsignifica che se la stringa seguente è vuota, if restituisce true. In altre parole, se ciò non git status --porcelaincomporta alcuna stringa, il repository è pulito. In caso contrario, elenca i file modificati / aggiunti / rimossi e non è più una stringa vuota. La ifvaluta quindi a false.
Adeynack,

19

Uso:

git diff-index --quiet HEAD

Il codice di ritorno riflette lo stato della directory di lavoro (0 = pulito, 1 = sporco). I file non tracciati vengono ignorati.


6
Restituisce 0 quando ci sono file non tracciati nella directory corrente.
Adam Parkin,

2
Se i file sono stati toccati / sovrascritti ma sono altrimenti identici all'indice, è necessario eseguirli git update-index --refreshprima git diff-index HEAD. Maggiori informazioni: stackoverflow.com/q/34807971/1407170
SFFC

@AdamParkin Ho appena aggiunto tutti i file git add .prima di emetterlo . Di solito è il modo di usarlo in uno script
ceztko,

Questo è fantastico Si noti che un codice di ritorno / uscita diverso da zero viene anche interpretato come un "errore", che se ci si trova in uno script con set -e, lo script uscirà se "sporco". Questo può essere evitato facendo set +eprima della chiamata a gite aggiungendo di set -enuovo dopo aver valutato $?.
Orion Elenzil,

1

Minor ampliamento dell'ottima risposta di André .

Questo è un modo per valutare i risultati ed evitare anche una trappola se ti trovi in ​​uno script che in precedenza ha emesso set -e .

I file non tracciati vengono ignorati.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.