Como posso verificar um script Bash se meu repositório Git local tiver alterações?

176

Existem alguns scripts que não funcionam corretamente se eles verificarem alterações.

Eu tentei assim:

VN=$(git describe --abbrev=7 HEAD 2>/dev/null)

git update-index -q --refresh
CHANGED=$(git diff-index --name-only HEAD --)
if [ ! -z $CHANGED ];
    then VN="$VN-mod"
fi

Existe algum tipo de verificação booleana se houve alterações desde a última confirmação ou como posso realmente testar se há novas alterações no meu repositório local?

Estou fazendo tudo isso para um script de criação de versão (que encontrei em algum lugar aqui).

kmindi
fonte
3
O que há de errado git status?
28411 karlphillip
4
@karlphillip: Faz muito processamento que você realmente não precisa.
Cascabel
1
@karlphillip é um comando de "porcelana", o que significa: não é apropriado para uso em roteiro porque a saída é projetado para ser lido por seres humanos e podem mudar (entre as versões ou devido a localização)
Andrew Spencer

Respostas:

201

O que você está fazendo quase funcionará: você deve citar $CHANGEDcaso esteja vazio e -ztestar se está vazio, o que significa que não há alterações. O que você quis dizer foi:

if [ -n "$CHANGED" ]; then
    VN="$VN-mod"
fi

Uma citação de Git GIT-VERSION-GEN:

git update-index -q --refresh
test -z "$(git diff-index --name-only HEAD --)" ||
VN="$VN-dirty"

Parece que você estava copiando isso, mas acabou de esquecer os detalhes das citações.

Obviamente, você também pode fazer isso:

if git diff-index --quiet HEAD --; then
    # No changes
else
    # Changes
fi

Ou se você se importa apenas com o caso "algo mudou":

if ! git diff-index --quiet HEAD --; then
    VN="$VN-mod"
fi

O uso --quiettem o benefício de que o Git pode parar o processamento assim que encontrar uma única diferença, portanto, talvez não seja necessário verificar toda a sua árvore de trabalho.

Cascabel
fonte
2
Olá, essa foi uma das melhores respostas em uma pergunta, você acabou de me fornecer todas as informações de que eu precisava e que outras pessoas também: :). Só vou precisar do último, "algo mudou" :) e você estava certo, eu o copiei.
kmindi
9
O incrível script de conclusão do bash parece ser usado git diff --no-ext-diff --quiet --exit-codepara determinar o estado sujo.
MJS
3
@mjs: Esse é realmente um ótimo lugar para procurar coisas assim! A --no-ext-diffopção é boa para segurança (caso alguém tenha configurado um driver diff externo), embora isso --exit-codenão seja necessário, pois está implícito --quiet.
Cascabel
4
Isso não funciona para mim como não relata arquivos untracked
Biscoito
1
git diff-indexrelata alterações mesmo que apenas os tempos de modificação do arquivo tenham sido alterados (e não seu conteúdo). Se você é touchum arquivo, ele relatará uma modificação que a execução git statusserá redefinida. Usando git statuscomo abaixo é melhor.
Sampo
303

Usando git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
ryanmoon
fonte
15
A melhor resposta, uma pena, as respostas dos 'arquitetos shell e git' são mais votadas.
jwg 26/09/14
4
Isso é ótimo, pois leva em conta arquivos não-versionados e também o uso de porcelana; deve ser mais compatível com diferentes versões do git.
Jayd
25
Para ignorar arquivos não rastreados: if [[ git status --porcelain --untracked-files=no]]; então
storm_m2138
4
Para verificar alterações locais -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos
3
O que esse código faz: Execute, git status --porcelainveja se a saída não está vazia. Nesse caso, isso significa que há alterações presentes.
bhathiya-perera
18

Embora a resposta de Jefromi seja boa, estou postando isso apenas para referência.

No código-fonte Git, há um shscript 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
}
Arrowmaster
fonte
Para referência, e se eu leio direito, $1é uma string que nomeia uma tarefa que você deseja executar e $2é opcionalmente uma string que contém uma mensagem de erro personalizada em caso de falha. Por exemplo, chame-o comorequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella
6

Eu tive um problema semelhante, mas também precisei verificar os arquivos adicionados. Então eu fiz o seguinte:

cd /local/repo
RUN=0
git diff --no-ext-diff --quiet --exit-code || RUN=1
if [ $RUN = 0 ]; then
    RUN=`git ls-files --exclude-standard --others| wc -l`
fi

if [ $RUN = 0 ]; then
    exit 0
fi
Leon Waldman
fonte
5

git status é seu amigo

Mude para o diretório Git para git statustrabalhar:

cd c:/path/to/.git

Defina uma variável para definir a árvore de trabalho para não receber o erro 'Esta operação deve ser executada em uma árvore de trabalho':

WORKTREE=c:/path/to/worktree

Capture a git statussaída em uma variável Bash

Use --porcelainqual garante estar em um formato padrão e analisável:

CHANGED=$(git --work-tree=${WORKTREE} status --porcelain)

Se -n (não nulo), temos alterações.

if [ -n "${CHANGED}" ]; then
  echo 'changed';

else
  echo 'not changed';
fi
AndrewD
fonte
1
Isso tem o benefício de detectar arquivos não rastreados também.
Luiz C.
2

Isso também funciona:

if [ $(git status --porcelain | wc -l) -eq "0" ]; then
  echo "  🟢 Git repo is clean."
else
  echo "  🔴 Git repo dirty. Quit."
  exit 1
fi
blackjacx
fonte
1

Isso funciona bem. Também listará os arquivos afetados:

if git diff-index --name-status --exit-code HEAD;
then
    echo Git working copy is clean...;
else
    echo ;
    echo ERROR: Git working copy is dirty!;
    echo Commit your changes and try again.;
fi;
robertberrington
fonte
1
se você não quer ver o diff git diff --no-ext-diff --quiet --exit-codetambém faz o trabalho.
20418 Alex
1

Aqui está um bom conjunto de funções de script do Bash que verificam se há um diff, imprimem ao usuário e solicitam ao usuário se eles desejam confirmar alterações antes da implantação. Ele foi criado para um aplicativo Heroku e Python, mas precisa de poucas alterações para qualquer outro aplicativo.

commit(){
    echo "Please enter a commit message..."
    read msg
    git add . --all
    git commit -am $msg
}

check_commit(){
    echo ========== CHECKING FOR CHANGES ========
    changes=$(git diff)
    if [ -n "$changes" ]; then
        echo ""
        echo "*** CHANGES FOUND ***"
        echo "$changes"
        echo ""
        echo "You have uncomitted changes."
        echo "Would you like to commit them (y/n)?"
        read n
        case $n in
            "y") commit;;
            "n") echo "Changes will not be included...";;
            *) echo "invalid option";;
        esac
    else
        echo "... No changes found"
    fi
}

deploy(){
    check_commit
    echo ========== DEPLOYING TO HEROKU ========
    git push heroku master
    heroku run python manage.py syncdb
}

Você pode copiar do Gists em: https://gist.github.com/sshadmand/f33afe7c9071bb725105

Sean
fonte
1

A pergunta do OP tem mais de 9 anos agora. Não sei o que man git-statusdisse então, mas aqui está o que diz agora:

--porcelain[=<version>]  
Give the output in an easy-to-parse format for scripts. This is similar to the 
short output, but will remain stable across Git versions and regardless of user 
configuration. See below for details.  

The version parameter is used to specify the format version. This is optional and 
defaults to the original version v1 format.  

Isso sugere que o --porcelain argumento é adequado para testar o status de um repositório para alterações.

Escreva a pergunta do OP: "Existe algum tipo de verificação booleana se houver alterações desde a última confirmação ou como posso realmente testar se há novas alterações no meu repositório local?"

Eu não acho que bashtenha tipos de dados booleanos por si só , mas isso pode ser próximo o suficiente:

[ -z "`git status --porcelain`" ] && echo "NULL-NO DIFFS" || echo "DIFFS EXIST"

Isso pode ser relançado como um if-then-elseformulário para um script ou executado como está na CLI enquanto estiver na pasta git repo . Caso contrário, use a -Copção com uma especificação de caminho para o repositório de interesse:

git -C ~/path/to/MyGitRepo status --porcelain 

Termo aditivo:

  1. Alguns aconselham o uso da -u, --untracked-fileopção para evitar o status de relatórios nos arquivos que se deseja ignorar. Observe que isso traz um efeito colateral lamentável : os arquivos adicionados recentemente também não têm status. A opção é útil em algumas situações , mas considere-a cuidadosamente antes de usar.
Seamus
fonte
0

Aqui está como eu faço isso ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi
Jader Feijo
fonte
3
Eu gosto de usar o status git, mas é melhor usar --porcelain nos scripts e comparar o resultado com uma string vazia sem alterações, pois é garantido que não será alterado de maneira incompatível entre as versões.
Paul Chernoch
0
nano checker_git.sh

cole isso

#!/bin/bash

echo "First arg: $1"

cd $1

bob="Already up-to-date."
echo $bob

echo $(git pull) > s.txt
cat s.txt
if [ "$(cat s.txt)" == "$bob" ]
then
echo "up"
else
echo "not up"

fi
rm -rf st.txt

corre sh checker_git.sh gitpath

Robert A
fonte
0

Baseado em @ storm_m2138 comente a resposta de @ RyanMoon ( link ), estou usando o seguinte em Powershell.

function hasChanges($dir="."){ $null -ne (iex "git -C $dir status --porcelain --untracked-files=no") }

gci -Directory | ?{ hasChanges $_ } | %{ Write-Host "$_ has changes" }
gci -Directory | ?{ hasChanges $_ } | %{ iex "git -C $_ add -u"; iex "git -C $_ commit -m"Somemthing" }
Dennis
fonte