Jak mogę sprawdzić w skrypcie Bash, czy moje lokalne repozytorium Git ma zmiany?

176

Istnieją skrypty, które nie działają poprawnie, jeśli sprawdzają zmiany.

Spróbowałem tak:

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

Czy jest jakiś rodzaj boolowskiego sprawdzenia, czy od ostatniego zatwierdzenia nastąpiły zmiany, lub jak mogę naprawdę sprawdzić, czy są nowe zmiany w moim lokalnym repozytorium?

Robię to wszystko dla skryptu tworzenia wersji (który gdzieś tu znalazłem).

kmindi
źródło
3
Co jest nie tak git status?
karlphillip
4
@karlphillip: Wykonuje dużo przetwarzania, którego tak naprawdę nie potrzebujesz.
Cascabel
1
@karlphillip to polecenie „porcelanowe”, co oznacza: nieodpowiednie do użycia w skrypcie, ponieważ dane wyjściowe są przeznaczone do odczytu przez ludzi i mogą ulec zmianie (między wersjami lub z powodu lokalizacji)
Andrew Spencer

Odpowiedzi:

201

To, co robisz, prawie zadziała: powinieneś cytować, $CHANGEDjeśli jest pusty, i -ztesty, jeśli jest pusty, co oznacza brak zmian. Miałeś na myśli:

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

Cytat z Gita GIT-VERSION-GEN:

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

Wygląda na to, że to kopiowałeś, ale zapomniałeś o tym fragmencie cytowania.

Oczywiście możesz też po prostu zrobić to:

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

Lub jeśli interesuje Cię tylko przypadek „coś się zmieniło”:

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

Używanie --quietma tę zaletę, że Git może zatrzymać przetwarzanie, gdy tylko napotka pojedynczy plik różnicowy, więc może nie być konieczne sprawdzanie całego drzewa roboczego.

Cascabel
źródło
2
Cześć, to była jedna z najlepszych odpowiedzi na pytanie, właśnie przekazałeś mi wszystkie informacje, których potrzebowałem, a innym też :). Potrzebuję tylko ostatniego, „coś się zmieniło” :) i miałeś rację, skopiowałem to.
kmindi
9
Wydaje się, że niesamowity skrypt uzupełniający bash używa git diff --no-ext-diff --quiet --exit-codedo określenia stanu brudnego.
mjs
3
@mjs: To naprawdę świetne miejsce do szukania takich rzeczy! Ta --no-ext-diffopcja jest dobra ze względów bezpieczeństwa (na wypadek, gdyby ktoś skonfigurował zewnętrzny sterownik różnicowy), chociaż --exit-codenie powinno być konieczne, ponieważ jest to sugerowane przez --quiet.
Cascabel,
4
To nie działa dla mnie, ponieważ nie zgłasza nieśledzonych plików
Cookie
1
git diff-indexzgłasza zmiany, nawet jeśli zmieniły się tylko czasy modyfikacji plików (a nie ich zawartość). Jeśli masz touchplik, zgłosi modyfikację, która git statuszostanie zresetowana. Używanie git statusjak poniżej jest lepsze.
Sampo
303

Używając git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
ryanmoon
źródło
15
Najlepsza odpowiedź, szkoda, że ​​odpowiedzi od „architektów powłoki i git” są wyżej oceniane.
jwg
4
Jest to świetne, ponieważ bierze pod uwagę niewersjonowane pliki, a także przy użyciu porcelany, powinno być bardziej kompatybilne z różnymi wersjami git.
Jayd
25
Aby zignorować nieśledzone pliki: if [[ git status --porcelain --untracked-files=no]]; następnie
storm_m2138
4
Aby sprawdzić lokalne zmiany -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Carlos Saltos
3
Co robi ten kod: wykonaj, git status --porcelainsprawdź, czy dane wyjściowe nie są puste. Jeśli tak, oznacza to, że istnieją zmiany.
bhathiya-perera
18

Chociaż odpowiedź Jefromi jest dobra, zamieszczam to tylko w celach informacyjnych.

Z kodu źródłowego Git jest shskrypt, który zawiera następujące elementy.

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
źródło
Dla odniesienia, i jeśli dobrze przeczytałem, $1jest to ciąg z nazwą zadania, które chcesz uruchomić i $2jest to ciąg opcjonalnie zawierający niestandardowy komunikat o błędzie w przypadku niepowodzenia. Na przykład nazwij to takrequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella
6

Miałem podobny problem, ale musiałem też sprawdzić dodane pliki. Więc zrobiłem co następuje:

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
źródło
5

git status jest twoim przyjacielem

Przejdź do katalogu Git git statusdo pracy:

cd c:/path/to/.git

Ustaw zmienną, aby ustawić drzewo robocze, aby nie pojawił się błąd „Ta operacja musi być wykonywana w drzewie roboczym”:

WORKTREE=c:/path/to/worktree

Przechwyć dane git statuswyjściowe do zmiennej Bash

Użyj, --porcelainco gwarantuje, że będzie w standardowym formacie i można je przeanalizować:

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

Jeśli -n (nie null), mamy zmiany.

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

else
  echo 'not changed';
fi
AndrewD
źródło
1
Ma to również tę zaletę, że wykrywa nieśledzone pliki.
Luiz C.
2

To też działa:

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

To działa dobrze. Zawiera również listę plików, których to dotyczy:

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
źródło
1
jeśli nie chcesz widzieć różnicy, git diff --no-ext-diff --quiet --exit-coderównież działa.
Alex
1

Oto ładny zestaw funkcji skryptowych Bash, które sprawdzają, czy istnieje różnica, drukuje je użytkownikowi i pyta użytkownika, czy chciałby zatwierdzić zmiany przed wdrożeniem. Jest zbudowany dla aplikacji Heroku i Python, ale wymaga niewielkich zmian w przypadku innych aplikacji.

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
}

Możesz skopiować z Gists na: https://gist.github.com/sshadmand/f33afe7c9071bb725105

Sean
źródło
1

Pytanie PO ma już ponad 9 lat. Nie wiem, co man git-statuswtedy powiedziało, ale oto, co mówi teraz:

--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.  

Sugeruje to, że --porcelainargument ten dobrze nadaje się do testowania stanu repozytorium pod kątem zmian.

Napisz pytanie OP: "Czy istnieje jakiś rodzaj boolowskiego sprawdzenia, czy od ostatniego zatwierdzenia nastąpiły zmiany, lub jak mogę naprawdę sprawdzić, czy są nowe zmiany w moim lokalnym repozytorium?"

Nie sądzę, że bashma logicznych typów danych na se , ale może to być wystarczająco blisko:

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

Można to przesłać ponownie jako if-then-elseformularz dla skryptu lub wykonać w wierszu poleceń CLI w folderze repozytorium git . W przeciwnym razie użyj -Copcji ze specyfikacją ścieżki do interesującego nas repozytorium:

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

Uzupełnienie:

  1. Niektórzy zalecają używanie -u, --untracked-fileopcji, aby uniknąć zgłaszania statusu plików, które chce się ignorować. Zauważ, że ma to niefortunny efekt uboczny : nowo dodane pliki również nie mają statusu. Ta opcja jest przydatna w niektórych sytuacjach , ale rozważ ją dokładnie przed użyciem.
Seamus
źródło
0

Oto jak to robię ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi
Jader Feijo
źródło
3
Lubię używać statusu git, ale lepiej jest używać --porcelain w skryptach i porównywać wynik z pustym ciągiem bez zmian, ponieważ gwarantuje się, że nie zmieni się w niekompatybilny sposób między wersjami.
Paul Chernoch,
0
nano checker_git.sh

wklej to

#!/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

biegać sh checker_git.sh gitpath

Robert A.
źródło
0

Na podstawie komentarza @ storm_m2138 do odpowiedzi @ RyanMoon ( link ) używam następującego w 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
źródło