Як я можу перевірити сценарій Bash, чи змінився мій локальний сховище Git?


176

Є деякі сценарії, які не працюють належним чином, якщо вони перевіряють зміни.

Я спробував це так:

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

Чи є якась булева перевірка, чи відбулися зміни з моменту останнього введення, або як я дійсно можу перевірити, чи є нові зміни в моєму локальному сховищі?

Я роблю все це для створення сценарію версії (що я знайшов десь тут).


3
Що не так git status?
karlphillip

4
@karlphillip: Це дуже багато обробки, яка вам насправді не потрібна.
Каскабель

1
@karlphillip це "порцелянова" команда, це означає: не підходить для використання в сценарії, оскільки вихід призначений для читання людьми і може змінюватися (між версіями або через локалізацію)
Ендрю Спенсер

Відповіді:


201

Те, що ви робите, майже не спрацює: вам слід навести $CHANGEDвипадок, якщо він порожній, і -zтести для порожнього, а це означає, що змін немає. Що ви мали на увазі:

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

Цитата з Гіта GIT-VERSION-GEN:

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

Схоже, ви це копіювали, але ви просто забули цю деталь цитування.

Звичайно, ви могли також просто так:

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

Або якщо ви дбаєте лише про справу "щось змінилося":

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

Використання --quietмає ту перевагу, що Git може припинити обробку, як тільки зіткнеться з однією різницею, тому можливо не доведеться перевіряти все робоче дерево.


2
Привіт, це була одна з найкращих відповідей на запитання, ти просто дав мені всю інформацію, яка мені потрібна, а також, що мені потрібні й інші :). Мені просто потрібен останній, "щось змінилося" :) і ти мав рацію, я скопіював це.
kmindi

9
Дивовижний сценарій завершення башти, здається, використовується git diff --no-ext-diff --quiet --exit-codeдля визначення брудного стану.
міс

3
@mjs: Це справді чудове місце для пошуку таких речей! Цей --no-ext-diffваріант корисний для безпеки (у випадку, якщо хтось налаштував зовнішній драйвер різниці), хоча це --exit-codeне повинно бути необхідним, оскільки це мається на увазі --quiet.
Каскабель

4
Це не працює для мене, оскільки він не повідомляє про незаймані файли
Cookie

1
git diff-indexзвіти змінюються, навіть якщо змінилися лише часи модифікації файлів (а не їх вміст). Якщо ви touchфайл, він повідомить про модифікацію, що запущена система git statusбуде скинута. Використовувати git statusяк нижче, краще.
Сампо

303

Використання git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi

15
Найкраща відповідь, ганьба, відповіді від «архітекторів оболонок і ґіт» є більш високими.
jwg

4
Це чудово, оскільки він враховує неперевершені файли, а також, використовуючи фарфор, він повинен бути більш сумісним з різними версіями git.
Джейд

25
Ігнорувати відслідковувані файли: if [[ git status --porcelain --untracked-files=no]]; потім
storm_m2138

4
Для перевірки місцевих змін -> if [[ $(git status --porcelain | wc -l) -gt 0 ]]; then echo CHANGED else echo NOT CHANGED locally fi
Карлос Сальтос

3
Що робить цей код: Виконати, git status --porcelainпобачити, чи вихід не порожній. Якщо так, це означає, що є зміни.
bhathiya-

18

Хоча відповідь Джефромі хороша, я публікую це лише для довідки.

З вихідного коду Git є shскрипт, який включає в себе наступне.

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
}

Для довідки, і якщо я прочитав це право, $1це рядок, що називає завдання, яке ви хочете виконати, і $2це рядок, необов'язково, що містить власне повідомлення про помилку при відмові. Наприклад, назвіть це такrequire_clean_work_tree deploy "Skipping deploy, clean up your work tree or run dev-push"
Umbrella

6

У мене була подібна проблема, але мені довелося також перевірити, чи не додано файлів. Тому я зробив наступне:

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

5

git status твій друг

Змініть каталог Git для git statusроботи:

cd c:/path/to/.git

Встановіть змінну для встановлення робочого дерева, щоб ви не отримували помилку "Ця операція повинна бути запущена в робочому дереві":

WORKTREE=c:/path/to/worktree

Зробіть git statusрезультат у змінній Bash

Використовуйте, --porcelainякі гарантують, що вони повинні бути у стандартному форматі та прохідних:

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

Якщо -n (не null), ми маємо зміни.

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

else
  echo 'not changed';
fi

1
Це має перевагу і в тому, щоб виявляти відслідковувані файли.
Луїз C.

2

Це також працює:

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

1

Це прекрасно працює. Він також перелічить файли, на які впливає:

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;

1
якщо ви не хочете бачити відмінності, це git diff --no-ext-diff --quiet --exit-codeтакож спрацьовує.
Олексій

1

Ось приємний набір функцій скрипту Bash, який перевіряє, чи є розбіжність, друкує його для користувача та підказує користувачеві, чи хотіли б вони внести зміни перед розгортанням. Він створений для додатків Heroku та Python, але для будь-якого іншого додатка його потрібно мало змінити.

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
}

Ви можете скопіювати з Gists на: https://gist.github.com/sshadmand/f33afe7c9071bb725105


1

Питання ОП вже більше 9 років. Я не знаю, що man git-statusтоді говорив, але ось, що це говорить зараз:

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

Це говорить про те, що --porcelainаргумент добре підходить для тестування статусу репо на зміни.

Запишіть питання ОП: "Чи є якась булева перевірка, чи відбулися зміни з моменту останнього введення, або як я дійсно можу перевірити, чи є нові зміни в моєму локальному сховищі?"

Я не думаю, що такі bashбулеві типи даних самі по собі є , але це може бути досить близько:

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

Це може бути перетворено у if-then-elseвигляді форми для сценарію, або виконано як-є fm CLI, перебуваючи в папці git repo . В іншому випадку скористайтеся -Cопцією зі специфікацією шляху до репортажу, що цікавиться:

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

Додаток:

  1. Деякі радить використовувати -u, --untracked-fileопцію, щоб уникнути статусу звітування про файли, які хочеться проігнорувати. Зауважте, що це має невдалий побічний ефект : файли, які нещодавно додаються, також не статуруються. Цей варіант корисний у деяких ситуаціях , але ретельно продумайте його перед використанням.

0

Ось як я це роблю ...

CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
    # do stuff here
else
    echo "You have uncommitted changes..."
fi

3
Мені подобається використовувати статус git, але краще використовувати --porcelain у скриптах і порівнювати результат із порожнім рядком без змін, оскільки гарантовано не змінюватись несумісним чином у різних версіях.
Пол Черноч

0
nano checker_git.sh

вставте це

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

бігати sh checker_git.sh gitpath


0

На основі коментаря @ storm_m2138 до відповіді @ RyanMoon ( посилання ) я використовую наступне в 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" }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.