Determinar se o diretório de trabalho do Git está limpo a partir de um script

84

Eu tenho um script que é executado rsynccom um diretório de trabalho Git como destino. Quero que o script tenha um comportamento diferente, dependendo se o diretório de trabalho estiver limpo (sem alterações a confirmar) ou não. Por exemplo, se a saída de git statusfor como abaixo, quero que o script saia:

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

Se o diretório não estiver limpo, eu gostaria que ele executasse mais alguns comandos.

Como posso verificar a saída como a acima em um script de shell?

brentwpeterson
fonte
Verificar um status do último comando ajudaria aqui? ($?)
UVV
Você poderia dar mais detalhes, por favor? Qual é a principal idéia do seu script?
precisa saber é o seguinte
@tachomi eu adicionei o contexto na edição
brentwpeterson
você poderia apenas assumir que não é limpo e fazer uma git reset --hard origin/branchse é isso que você está indo para ... como se você está tentando limpeza depois de compilar algo, etc.
SnakeDoc
1
@SnakeDoc Você poderia, mas presumo que o caso inverso seria mais comum, por exemplo, sair se o diretório de trabalho estiver sujo para evitar alterações locais alteradas. Considerando os dois casos, a pergunta seria mais útil para futuros leitores.
Thomas Nyman

Respostas:

135

Analisar a saída de git statusé uma péssima idéia, pois a saída deve ser legível por humanos, não legível por máquina. Não há garantia de que a saída permaneça a mesma nas versões futuras do Git ou em ambientes configurados de maneira diferente.

O comentário dos UVVs está no caminho certo, mas, infelizmente, o código de retorno de git statusnão muda quando há alterações não confirmadas. No entanto, ele fornece a --porcelainopção, que faz com que a saída git status --porcelainseja formatada em um formato fácil de analisar para scripts e permanecerá estável nas versões do Git e independentemente da configuração do usuário.

Podemos usar a saída vazia git status --porcelaincomo um indicador de que não há alterações a serem confirmadas:

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

Se não nos importamos com arquivos não rastreados no diretório de trabalho, podemos usar a --untracked-files=noopção para desconsiderar aqueles:

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

Para tornar isso mais robusto em relação às condições que realmente causam git statusfalhas sem saída stdout, podemos refinar a verificação para:

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

Também é importante notar que, embora git statusnão dê um código de saída significativo quando o diretório de trabalho é impuro, git difffornece a --exit-codeopção, o que o comporta de maneira semelhante ao utilitário diff , ou seja, sair com status 1quando houve diferenças e 0quando nenhuma foi encontrada.

Usando isso, podemos verificar se há alterações em estágios com:

git diff --exit-code

e preparadas, mas não confirmadas, alterações com:

git diff --cached --exit-code

Embora git diffpossa relatar arquivos não rastreados em sub-módulos por meio de argumentos apropriados --ignore-submodules, infelizmente parece que não há como reportar arquivos não rastreados no diretório de trabalho real. Se arquivos não rastreados no diretório de trabalho forem relevantes, git status --porcelainprovavelmente é a melhor aposta.

Thomas Nyman
fonte
4
ughhh git status --porcelainserá encerrado com o código 0, mesmo que haja alterações não preparadas para arquivos de confirmação e não rastreados.
Alexander Mills
Eu estava interessado em determinar com antecedência se git stashfaria alguma coisa (não gera um código de retorno útil). Eu tive que adicionar --ignore-submodules, caso contrário git statusindicaria alterações no sub-módulo que git stashignoram.
Devin Pista
1
@ AlexMills: observei o mesmo. Mas então verifiquei o que if [ -zestava fazendo. O -zque significa que, se a seguinte sequência estiver vazia, o if será avaliado como true. Em outras palavras, se isso git status --porcelainresultar em nenhuma sequência, o repositório estará limpo. Caso contrário, ele lista os arquivos modificados / adicionados / removidos e não é mais uma sequência vazia. O ifentão avalia como false.
Adeynack
19

Usar:

git diff-index --quiet HEAD

O código de retorno reflete o estado do diretório ativo (0 = limpo, 1 = sujo). Arquivos não rastreados são ignorados.

André van Herk
fonte
6
Retorna 0 quando houver arquivos não rastreados no diretório atual.
12118 Adam Parkin
2
Se os arquivos foram tocados / substituídos, mas são idênticos ao índice, é necessário primeiro executar git update-index --refreshantes git diff-index HEAD. Mais informações: stackoverflow.com/q/34807971/1407170
sffc
@AdamParkin Acabei de adicionar todos os arquivos git add .antes de emiti-lo. Geralmente é a maneira de usá-lo em um script
ceztko
Isso é ótimo. Observe que um código de retorno / saída diferente de zero também é interpretado como um 'erro', que se você estiver em um script com set -e , seu script sairá se estiver 'sujo'. Isso pode ser evitado fazendo set +eantes da chamada gite adicionando set -enovamente após a avaliação $?.
orion elenzil 29/03
1

Pequena extensão à excelente resposta de André .

Essa é uma maneira de avaliar os resultados e também evitar uma armadilha se você estiver em um script que emitiu anteriormente set -e .

Arquivos não rastreados são ignorados.

set +e
git diff-index --quiet HEAD

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