Come verificare se non c'è nulla da impegnare nel ramo corrente?


172

L'obiettivo è ottenere uno stato inequivocabile che può essere valutato in un comando di shell.

Ho provato git statusma restituisce sempre 0, anche se ci sono elementi da impegnare.

git status
echo $?  #this is always 0

Ho un'idea ma penso che sia piuttosto una cattiva idea.

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

qualsiasi altro modo?


aggiornare con la seguente risoluzione, vedere il post di Mark Longair

Ho provato questo ma causa un problema.

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

Ottengo il seguente errore [: ??: binary operator expected

ora sto guardando l'uomo e provo il git diff.

=================== codice per la mia speranza e spero che risponda meglio ======================

#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

4
Nella sezione aggiornata, sembra che tu non stia effettivamente facendo ciò che eckes suggerisce nella sua risposta - come dice, devi mettere due virgolette attorno al $(git status --porcelain). Inoltre, se vuoi inserire punti esclamativi nel tuo messaggio, dovrai usare virgolette singole anziché doppie - cioè dovrebbe essere echo 'PLEASE COMMIT YOUR CHANGE FIRST!!!'invece
Mark Longair,

4
come dice Mark: devi mettere le doppie virgolette intorno al$(git status --porcelain) , proprio come ti ho detto!
Controlla il

1
Questa domanda sarebbe molto più utile, se non includesse parti di risposte.
oberlies,

@ 9nix00 fai quello che ti è stato detto e modifica e correggi il bug nello script della shell sopra: ERRORE: if [-z $ (alcuni comandi)] FIX: if [-z "$ (alcuni comandi)"]
MarcH

Risposte:


232

Un'alternativa al test se l'output di git status --porcelainè vuoto è testare ogni condizione che ti interessa separatamente. Uno potrebbe non interessarsi sempre, ad esempio, se ci sono file non tracciati nell'output di git status.

Ad esempio, per vedere se ci sono modifiche locali non in scena, puoi guardare il codice di ritorno di:

git diff --exit-code

Per verificare se ci sono modifiche messe in scena ma non impegnate, è possibile utilizzare il codice di ritorno di:

git diff --cached --exit-code

Infine, se vuoi sapere se nel tuo albero di lavoro ci sono file non tracciati che non vengono ignorati, puoi verificare se l'output del seguente comando è vuoto:

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

Aggiornamento: Di seguito si chiede se è possibile modificare quel comando per escludere le directory nell'output. Puoi escludere directory vuote aggiungendo --no-empty-directory, ma per escludere tutte le directory in quell'output penso che dovrai filtrare l'output, come con:

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

Il -vto egrepsignifica solo produrre linee che non corrispondono al modello e il modello corrisponde a qualsiasi linea che termina con a /.


Ho appoggiato questi suggerimenti e ho un problema. cioè, usa git ls-files --other --exclude-standard --directory ottieni l'elenco includi directory. c'è in qualche modo escludere queste directory?
9nix00,

si, questo è quello che voglio. e aggiorno il mio post per il nuovo codice di script. Penso che il tuo suggerimento sia logico più rigoroso anche se più codice lol ... e spero che appaiano risposte migliori.
9nix00

3
@albfan: è nella pagina man git-diff : "Fai uscire il programma con codici simili a diff (1). Cioè, esce con 1 se c'erano differenze e 0 significa nessuna differenza."
Mark Longair,

Solo per sottolineare, è stato lì almeno dal 2007 13da0fc0 , davvero utile per gli script di shell e pienamente compatibile con le versioni precedenti di git
albfan,

10
--quiet(che implica --exit-code) silenzia anche l'output, per coloro che vogliono solo il codice di uscita.
phs

113

Il valore restituito di git statusindica semplicemente il codice di uscita di git status, non se ci sono modifiche da impegnare.

Se si desidera una versione più leggibile dal computer git statusdell'output, provare

git status --porcelain

Vedere la descrizione di git statusper ulteriori informazioni al riguardo.

Esempio di utilizzo (lo script verifica semplicemente se git status --porcelainfornisce alcun output, non è necessaria l'analisi):

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

Nota che devi citare la stringa da testare, ovvero l'output di git status --porcelain. Per ulteriori suggerimenti sui costrutti di test, consultare la Guida agli script avanzati di Bash ( Confronto di stringhe di sezione ).


ciao, dai un buon suggerimento, l'ho provato, ma negli script, causa qualche problema, l'ho migliorato, se lo usiamo se [-z $ (git status --porcelain)]; otterrà qualche errore, [: ??: operatore binario previsto, trovo il manuale e lo uso se [-z $ (git status --short)]; questo può funzionare, grazie!
9nix00,

scusa, c'è ancora un problema di causa. quando il commit è pulito. usare porcellana e short entrambi ok. ma quando commit non è pulito. causerà errore. [: ??: operatore binario previsto. Penso che forse dovremmo provare a usare Base64 per codificarlo. fammi provare! download degli strumenti di comando base64 .... lol
9nix00,

causa problemi quando il commit non è pulito.
9nix00,

1
Ottima soluzione Per maggiore robustezza, è possibile aggiungere || echo noalla sostituzione dei comandi in modo che lo spazio di lavoro non venga erroneamente riportato pulito se git statusfallisce fondamentalmente. Inoltre, il tuo codice è (lodevolmente) conforme a POSIX, ma poiché ti colleghi a una guida di bash, lasciami aggiungere che se usi bash [[ ... ]]invece che compatibile con POSIX [ ... ], non è necessario citare due volte la sostituzione del comando (sebbene non fa male): [[ -z $(git status --porcelain) ]].
mklement0

@eckes Ho iniziato un nuovo repository alcuni giorni fa, ho aggiunto un commit, ho provato a scrivere un po 'di pre-commit-hook e controllare cosa devo impegnare e cosa dici non ha funzionato sul mio caso.
alinsoar

33

Se sei come me, vuoi sapere se ci sono:

1) modifiche ai file esistenti 2) file appena aggiunti 3) file eliminati

e in particolare non si desidera conoscere 4) file non monitorati.

Questo dovrebbe farlo:

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

Ecco il mio codice bash per uscire dallo script se il repository è pulito. Utilizza la versione breve dell'opzione file non tracciati:

[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;

4
+1 per —untracked-files=no; Penso che il tuo test possa essere semplificato [[ -z $(git status --untracked-files=no --porcelain) ]]. git statusnon dovrebbe scrivere a stderr, a meno che qualcosa di fondamentale va storto - e poi non vuoi vedere che in uscita. (Se si desidera un comportamento più solido in quell'evento, aggiungere || echo noalla sostituzione del comando in modo che il test per la pulizia fallisca ancora). Confronti di stringhe / l' -zoperatore può gestire stringhe a più righe - non è necessario tail.
mklement0

2
grazie @ mklement0, anche un po 'più corto:[[ -z $(git status -u no --porcelain) ]]
moodboom,

1
correzione: la mia versione più corta in realtà controlla solo lo stato sul file chiamato "no"! Bzzt. Dovrebbe essere: [[ -z $(git status -uno --porcelain) ]]
moodboom

2
Apprezzo il seguito; questo è un bug sottile - la lezione è che le opzioni brevi con argomenti opzionali devono avere l'argomento aggiunto direttamente , senza spazi bianchi in mezzo. Che ne dici di incorporare la versione corta corretta direttamente nella tua risposta?
mklement0

9

È possibile combinare git status --porcelaincon un semplice grepper eseguire il test.

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

A volte lo uso come semplice linea singola:

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

Aggiungi -qsal tuo comando grep per renderlo silenzioso.


2
+1 per eleganza; leggero avvertimento: dovrebbe git statusfallire fatalmente (ad esempio, un repository corrotto), il test segnalerà erroneamente un'area di lavoro pulita . Un'opzione è usare git status --porcelain 2>&1, ma ciò "mangerebbe" il messaggio di errore se si usasse grep con -q. (Trattare con che perderebbe l'eleganza: (git status --porcelain || echo err) | grep -q .)
mklement0

in alternativa, si può scrivere:test -z "$(git status --porcelain)" || git pull origin master
VasiliNovikov

5

Dal codice sorgente di git c'è uno script sh che include quanto segue.

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
}

Questo sniplet mostra come è possibile utilizzarlo git diff-filese git diff-indexscoprire se sono state apportate modifiche a file precedentemente noti. Tuttavia, non consente di scoprire se un nuovo file sconosciuto è stato aggiunto all'albero di lavoro.


funziona benissimo tranne che per i nuovi file. possiamo aggiungere questo. Se ! git ls-files --other --exclude-standard --directory | grep -c -v '/ $' quindi esci da 0 else echo "per favore commetti il ​​tuo nuovo file, se non vuoi aggiungerlo, per favore aggiungilo nel file git-ignore." uscita 1 fi
9nix00

Solo if [ -n "$(git ls-files --others --exclude-standard)" ]senza ulteriori tubazioni o greping dovrebbe essere sufficiente per rilevare file non tracciati.
Arrowmaster

5

farei un test su questo:

git diff --quiet --cached

o questo per essere esplicito:

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

dove:

--exit-code

Fai uscire il programma con codici simili a diff (1). Cioè, esce con 1 se c'erano differenze e 0 significa nessuna differenza.

--silenzioso

Disabilita tutto l'output del programma. Implica --exit-code


3

Sono un po 'in ritardo nella discussione, ma se è solo che devi avere un codice di uscita di 0 se git status --porcelainnon restituisce nulla e! = 0 altrimenti, prova questo:

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

Ciò renderà il numero di righe il codice di uscita, a rischio di problemi quando sono presenti più di 255 righe. Così

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

lo spiegherò;)


1
Questo sarà sostanzialmente indefinito se ci sono più di 255 linee di output.
Tripleee

Ben individuato, grazie!
Skeeve

2

Sto usando questo in uno script per avere:

  • 0 quando tutto è pulito
  • 1 in presenza di file diff o non tracciati

    [-z "$ (git status --porcelain)"]


l'utilizzo if ! git diff --quiet; thenè più pulito e performante (penso). In altre parole, utilizzare il codice di uscita, non lo stdout.
Alexander Mills,

1
@AlexanderMills si git diff --quietcomporta diversamente dalle git status --porcelainmodifiche memorizzate nella cache.
Martin von Wittich,

0

Non carino, ma funziona:

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

Non sono sicuro se il messaggio dipende dalle impostazioni locali, quindi forse metti un LANG=Cin primo piano.

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.