Как я могу проверить в 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

Есть ли какая-то логическая проверка, были ли изменения с момента последнего коммита, или как я могу действительно проверить, есть ли новые изменения в моем локальном репозитории?

Я делаю все это для сценария создания версии (который я нашел где-то здесь).

kmindi
источник
3
Что не так с git status?
karlphillip
4
@karlphillip: он делает много обработки, которая вам не нужна.
Каскабель
1
@karlphillip - это «фарфоровая» команда, что означает: не подходит для использования в сценарии, поскольку выходные данные предназначены для чтения людьми и могут изменяться (между версиями или из-за локализации)
Эндрю Спенсер

Ответы:

201

То, что вы делаете, почти сработает: вы должны цитировать, $CHANGEDесли оно пустое, и -zпроверять на пустое, что означает отсутствие изменений. То, что вы имели в виду, было:

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

Цитата из Git's 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 может прекратить обработку, как только он встретит один diff, поэтому может не потребоваться проверка всего дерева работы.

Cascabel
источник
2
Привет, это был один из лучших ответов на вопрос, ты только что дал мне всю информацию, которая мне была нужна, а остальным моя потребность :). Мне просто нужно последнее, «что-то изменилось» :) и вы были правы, я скопировал это.
Кинди
9
Удивительный скрипт завершения bash, похоже, использует git diff --no-ext-diff --quiet --exit-codeдля определения грязного состояния.
mjs
3
@mjs: это действительно отличное место, чтобы искать такие вещи! --no-ext-diffВариант хорош для безопасности (в случае , если кто - то настроил внешний драйвер дифф), хотя --exit-codeне должно быть необходимым, так как это подразумевается --quiet.
Каскабель
4
Это не работает для меня, так как не сообщает о неотслеживаемых файлах
Cookie
1
git diff-indexсообщает об изменениях, даже если изменились только времена изменения файлов (а не их содержимое). Если вы touchфайл, он сообщит о модификации, которая git statusбудет сброшена. Использование git statusкак ниже, лучше.
Сампо
304

Использование git status:

cd /git/directory
if [[ `git status --porcelain` ]]; then
  # Changes
else
  # No changes
fi
ryanmoon
источник
15
Лучший ответ, позор, ответы от «архитекторов оболочки и git» более высоко оценены.
JWG
4
Это замечательно, поскольку учитывает неверсионные файлы, а также использует фарфор, он должен быть более совместимым с различными версиями git.
Jayd
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
Carlos Saltos
3
Что делает этот код: Выполните, чтобы git status --porcelainувидеть, не является ли вывод не пустым. Если это так, значит, есть изменения.
бхатия-перера
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
}
Arrowmaster
источник
Для справки, и если я правильно понял, $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 (не ноль), у нас есть изменения.

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

else
  echo 'not changed';
fi
AndrewD
источник
1
Это также помогает обнаруживать неотслеживаемые файлы.
Луис С.
2

Это тоже работает:

if [ $(git status --porcelain | wc -l) -eq "0" ]; then
  echo "  🟢 Git repo is clean."
else
  echo "  🔴 Git repo dirty. Quit."
  exit 1
fi
blackjacx
источник
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;
robertberrington
источник
1
если вы не хотите видеть, дифференциал git diff --no-ext-diff --quiet --exit-codeтоже работает.
Алекс
1

Вот хороший набор функций скрипта Bash, которые проверяют, есть ли diff, распечатывает его пользователю и предлагает пользователю, если они хотели бы зафиксировать изменения перед развертыванием. Он построен для приложений 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формы для скрипта или выполнено как есть в 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 status, но лучше использовать --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" }
Деннис
источник