Como verificar se não há nada a ser confirmado no ramo atual?

172

O objetivo é obter um status inequívoco que possa ser avaliado em um comando shell.

Eu tentei, git statusmas sempre retorna 0, mesmo se houver itens a serem confirmados.

git status
echo $?  #this is always 0

Eu tenho uma ideia, mas acho que é uma péssima idéia.

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

qualquer outra maneira?


atualizar com a seguinte solução, consulte a publicação de Mark Longair

Eu tentei isso, mas isso causa um problema.

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

Estou tendo o erro a seguir [: ??: binary operator expected

agora, estou olhando para o homem e tente o diff do git.

=================== código para minha esperança, e espero uma resposta melhor ======================

#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
fonte
4
Na seção atualizada, parece que você não está realmente fazendo o que sugere sugestões em sua resposta - como ele diz, você precisa colocar aspas duplas em volta do $(git status --porcelain). Além disso, se você quiser colocar pontos de exclamação em sua mensagem, você vai precisar usar aspas simples em vez de aspas duplas - ou seja, ele deve ser echo 'PLEASE COMMIT YOUR CHANGE FIRST!!!'em vez
Mark Longair
4
como Mark diz: você precisa colocar aspas duplas em volta do$(git status --porcelain) , como eu disse!
Eckes
1
Essas perguntas seriam muito mais úteis, se não incluíssem partes das respostas.
Oberlies
@ 9nix00 faça o que lhe foi dito, edite e corrija o erro no seu script shell acima: BUG: if [-z $ (some command)] CORRECÇÃO: if [-z "$ (some command)"]
MarcH

Respostas:

232

Uma alternativa para testar se a saída de git status --porcelainestá vazia é testar cada condição com a qual você se preocupa separadamente. Nem sempre se pode importar, por exemplo, se houver arquivos não rastreados na saída de git status.

Por exemplo, para verificar se há alguma mudança local não faseada, é possível consultar o código de retorno de:

git diff --exit-code

Para verificar se há alguma mudança preparada, mas não confirmada, você pode usar o código de retorno de:

git diff --cached --exit-code

Por fim, se você quiser saber se existem arquivos não rastreados em sua árvore de trabalho que não são ignorados, você pode testar se a saída do seguinte comando está vazia:

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

Atualização: Você pergunta abaixo se pode alterar esse comando para excluir os diretórios na saída. Você pode excluir diretórios vazios adicionando --no-empty-directory, mas para excluir todos os diretórios dessa saída, acho que você terá que filtrar a saída, como por exemplo:

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

O -vto egrepsignifica apenas linhas de saída que não correspondem ao padrão, e o padrão corresponde a qualquer linha que termine com a /.

Mark Longair
fonte
Inclinei-me para essas dicas e tenho um problema. ou seja, use git ls-files --other --exclude-standard --directory para obter os diretórios de inclusão da lista. existe alguma maneira excluir esses diretórios?
9nix00
sim, é isso que eu quero. e atualizo minha postagem para um novo código de script. Eu acho que a sua sugestão é mais rigorosa lógica, embora mais código lol ... e espero que melhores respostas apareçam.
9nix00
3
@albfan: está na página do manual git-diff : "Faça o programa sair com códigos semelhantes ao diff (1). Ou seja, ele sai com 1 se houver diferenças e 0 significa que não há diferenças."
Mark Longair 8/11
Apenas para salientar, está lá pelo menos desde 2007 13da0fc0 , realmente útil para scripts shell, e totalmente compatível com versões mais antigas do git
albfan
10
--quiet(o que implica --exit-code) também silencia a saída, para quem deseja apenas o código de saída.
Phd #
113

O valor de retorno git statusapenas informa o código de saída de git status, não se houver alguma modificação a ser confirmada.

Se você deseja uma versão mais legível por computador da git statussaída, tente

git status --porcelain

Veja a descrição de git statuspara mais informações sobre isso.

Exemplo de uso (o script simplesmente testa se git status --porcelain há saída, sem necessidade de análise):

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

Observe que você deve citar a string para testar, ou seja, a saída de git status --porcelain. Para obter mais dicas sobre construções de teste, consulte o Advanced Bash Scripting Guide ( comparação de cadeias de seção ).

eckes
fonte
oi, você deu uma boa sugestão, eu tentei, mas no script, causa algum problema, eu melhorei, se usarmos isso se [-z $ (git status --porcelain)]; receberá algum erro, [: ??: operador binário esperado, encontro o manual e o uso se [-z $ (git status --short)]; isso pode funcionar, obrigado!
9nix00 28/02
desculpe, ainda há causar problema. quando o commit estiver limpo. use porcelana e curta ambos ok. mas quando o commit não está limpo. isso causará erro. [: ??: operador binário esperado. Acho que talvez devêssemos usar o base64 para codificá-lo. deixe-me tentar! baixando ferramentas de comando base64 .... lol
9nix00 28/02
causa problema quando o commit não está limpo.
9nix00 28/02
1
Ótima solução. Para maior robustez, você pode anexar || echo noà substituição de comando para que o espaço de trabalho não seja reportado por engano como limpo se git statusfalhar fundamentalmente. Além disso, seu código é (louvável) compatível com POSIX, mas como você vincula um guia do bash, deixe-me acrescentar que se você usar o bash em [[ ... ]]vez do compatível com o POSIX [ ... ], não será necessário citar duas vezes a substituição do comando (embora não faz mal): [[ -z $(git status --porcelain) ]].
mklement0
@eckes Comecei um novo repositório há alguns dias, adicionei um commit, tentei escrever um gancho pré-commit e verifique o que deve ser comprometido e o que você diz não funcionou no meu caso.
Alinsoar 9/18
33

Se você é como eu, quer saber se existem:

1) alterações nos arquivos existentes 2) arquivos adicionados recentemente 3) arquivos excluídos

e, especificamente, não deseja saber sobre 4) arquivos não rastreados.

Isso deve servir:

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

Aqui está o meu código bash para sair do script se o repositório estiver limpo. Ele usa a versão curta da opção de arquivos não rastreados:

[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;
humor
fonte
4
+1 para —untracked-files=no; Eu acho que seu teste pode ser simplificado [[ -z $(git status --untracked-files=no --porcelain) ]]. git statusnão deve escrever para stderr, a menos que algo fundamental der errado - e então você não quer ver essa saída. (Se você queria um comportamento mais robusto nesse evento, acrescente || echo noa substituição de comando para que o teste de limpeza ainda falhe). Comparações de strings / o -zoperador pode lidar com strings de várias linhas - sem necessidade tail.
mklement0
2
obrigado @ mklement0, ainda um pouco mais curto:[[ -z $(git status -u no --porcelain) ]]
moodboom
1
correção: minha versão mais curta, na verdade, apenas verifica o status no arquivo chamado "não"! Bzzt. Deve ser: [[ -z $(git status -uno --porcelain) ]]
moodboom
2
Agradeço o acompanhamento; isso é um bug sutil - a lição é que opções curtas com argumentos opcionais devem ter o argumento anexado diretamente , sem espaços em branco no meio. Que tal incorporar a versão curta corrigida diretamente em sua resposta?
precisa saber é o seguinte
9

É possível combinar git status --porcelaincom um simples greppara executar o teste.

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

Às vezes, uso isso como uma linha simples:

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

Adicione -qsao seu comando grep para silenciá-lo.

Steve Prentice
fonte
2
+1 para elegância; uma pequena advertência: se git statusfalhar fatalmente (por exemplo, um repositório corrompido), seu teste relatará por engano um espaço de trabalho limpo . Uma opção é usar git status --porcelain 2>&1, mas isso 'consumiria' a mensagem de erro se você usasse grep -q. (Lidar com isso perderia a elegância: (git status --porcelain || echo err) | grep -q .)
mklement0
alternativamente, pode-se escrever:test -z "$(git status --porcelain)" || git pull origin master
VasiliNovikov
5

No código-fonte git, existe um script sh que inclui o seguinte.

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
}

Este sniplet mostra como é possível usar git diff-filese git diff-indexdescobrir se há alguma alteração nos arquivos conhecidos anteriormente. No entanto, não permite descobrir se um novo arquivo desconhecido foi adicionado à árvore de trabalho.

Arrowmaster
fonte
isso funciona bem, exceto para novos arquivos. podemos adicionar isso. E se ! git ls-files --other --exclude-standard --directory | grep -c -v '/ $' e saia de 0 else echo "por favor confirme seu novo arquivo, se você não quiser adicioná-lo, por favor, adicione-o no arquivo git-ignore." saída 1 fi
9nix00 01/01
Apenas if [ -n "$(git ls-files --others --exclude-standard)" ]sem nenhuma tubulação ou greping adicional deve ser suficiente para detectar arquivos não rastreados.
Arrowmaster 01/03
5

eu faria um teste sobre isso:

git diff --quiet --cached

ou isso seja explícito:

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

Onde:

--exit-code

Faça o programa sair com códigos semelhantes ao diff (1). Ou seja, sai com 1 se houver diferenças e 0 significa que não há diferenças.

--quieto

Desative toda a saída do programa. Implica --exit-code

archf
fonte
3

Estou um pouco atrasado na discussão, mas se for necessário apenas um código de saída 0 se git status --porcelainnão retornar nada e! = 0 mais, tente o seguinte:

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

Isso fará com que o número de linhas seja o código de saída, correndo o risco de ocorrer problemas quando houver mais de 255 linhas. assim

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

será responsável por isso;)

Skeeve
fonte
1
Basicamente, isso será indefinido se houver mais de 255 linhas de saída.
tripleee
Bem manchado, obrigado!
Skeeve
2

Estou usando isso em um script para ter:

  • 0 quando tudo estiver limpo
  • 1 quando há arquivos diff ou não rastreados

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

mais grosseiro
fonte
usar if ! git diff --quiet; thené mais limpo e com melhor desempenho (eu acho). Em outras palavras, use o código de saída, não o stdout.
Alexander Mills
1
O @AlexanderMills git diff --quietse comporta de maneira diferente das git status --porcelainalterações em cache.
Martin von Wittich
0

Não é bonito, mas funciona:

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

Não tenho certeza se a mensagem depende da localidade, então talvez coloque uma LANG=Cna frente.

Tilman Vogel
fonte