Как программно определить, есть ли незафиксированные изменения?

В Makefile я хотел бы выполнить определенные действия, если есть незафиксированные изменения (либо в рабочем дереве, либо в индексе). Какой самый чистый и эффективный способ сделать это? Команда, которая выходит с возвратным значением нуля в одном случае и не равна нулю в другом, будет соответствовать моим целям.

Я могу запустить git status и передать вывод через grep, но я чувствую, что должен быть лучший способ.

Ответ 1

ОБНОВЛЕНИЕ: ОП Даниэль Штутцбах указывает в комментариях, что эта простая команда git diff-index работает для него:

git diff-index --quiet HEAD --

Затем вы можете увидеть "Как проверить, успешно ли выполнена команда?", если вы используете ее в сценарии bash:

git diff-index --quiet HEAD -- || echo "untracked"; // do something about it

Примечание: как прокомментировал Энтони Соттиль

git diff-index HEAD ... потерпит неудачу в ветки, в которой нет коммитов (например, в недавно инициализированном хранилище).
Я нашел один обходной путь git diff-index $(git write-tree) ...

И haridsv указывает в комментариях, что git diff-files для нового файла не определяет его как diff.
Похоже, более безопасный подход - сначала запустить git add для спецификации файла, а затем использовать git diff-index, чтобы увидеть, было ли что-либо добавлено в индекс перед запуском git commit.

git add ${file_args} && \
git diff-index --cached --quiet HEAD || git commit -m '${commit_msg}'

И 6502 сообщает в комментариях:

Одна проблема, с которой я столкнулся, заключается в том, что git diff-index скажет, что есть различия, когда их действительно нет, за исключением временных отметок файлов.
Запуск git diff однажды решает проблему (достаточно удивительно, что git diff действительно меняет содержимое песочницы, что означает здесь .git/index)


Оригинальный ответ:

"Программно" означает, что никогда не полагается на фарфоровые команды.
Всегда полагайтесь на сантехнические команды.

См. также "Проверка наличия грязного индекса или неотслеживаемых файлов с помощью Git" для поиска альтернатив (например, git status --porcelain)

Вы можете черпать вдохновение из новой функцииrequire_clean_work_tree, которая написана так, как мы говорим ;) (начало октября 2010 года)

require_clean_work_tree () {
    # Update the index
    git update-index -q --ignore-submodules --refresh
    err=0

    # Disallow unstaged changes in the working tree
    if ! git diff-files --quiet --ignore-submodules --
    then
        echo >&2 "cannot $1: you have unstaged changes."
        git diff-files --name-status -r --ignore-submodules -- >&2
        err=1
    fi

    # Disallow uncommitted changes in the index
    if ! git diff-index --cached --quiet HEAD --ignore-submodules --
    then
        echo >&2 "cannot $1: your index contains uncommitted changes."
        git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
        err=1
    fi

    if [ $err = 1 ]
    then
        echo >&2 "Please commit or stash them."
        exit 1
    fi
}

Ответ 2

В то время как другие решения очень тщательны, если вы хотите что-то действительно быстрое и грязное, попробуйте что-то вроде этого:

[[ -z $(git status -s) ]]

Он просто проверяет, есть ли какой-либо вывод в сводке состояния.

Ответ 3

git diff --exit-code будет возвращать ненулевое значение, если есть какие-либо изменения; git diff --quiet то же самое без вывода. Поскольку вы хотите проверить рабочее дерево и индекс, используйте

git diff --quiet && git diff --cached --quiet

или

git diff --quiet HEAD

Либо кто-нибудь скажет вам, есть ли незавершенные изменения, которые поставлены или нет.

Ответ 4

Расширение ответа @Nepthar:

if [[ -z $(git status -s) ]]
then
  echo "tree is clean"
else
  echo "tree is dirty, please commit changes before running this"
  exit
fi

Ответ 5

Как указано в другом ответе, так просто, как такая команда достаточно:

git diff-index --quiet HEAD --

Если вы опустите последние две тире, команда завершится неудачно, если у вас есть файл с именем HEAD.

Пример:

#!/bin/bash
set -e
echo -n "Checking if there are uncommited changes... "
trap 'echo -e "\033[0;31mFAILED\033[0m"' ERR
git diff-index --quiet HEAD --
trap - ERR
echo -e "\033[0;32mAll set!\033[0m"

# continue as planned...

Осторожно: эта команда игнорирует невоспроизводимые файлы.

Ответ 6

Я создал несколько удобных псевдонимов git, чтобы вывести список неподготовленных и промежуточных файлов:

git config --global alias.unstaged 'diff --name-only'
git config --global alias.staged 'diff --name-only --cached'

Тогда вы можете легко сделать такие вещи, как:

[[ -n "$(git unstaged)" ]] && echo unstaged files || echo NO unstaged files
[[ -n "$(git staged)" ]] && echo staged files || echo NO staged files

Вы можете сделать его более читабельным, создав скрипт где-нибудь в вашем PATH именем git-has:

#!/bin/bash
[[ $(git "[email protected]" | wc -c) -ne 0 ]]

Теперь приведенные выше примеры можно упростить до:

git has unstaged && echo unstaged files || echo NO unstaged files
git has staged && echo staged files || echo NO staged files

Для полноты здесь аналогичные псевдонимы для неотслеживаемых и игнорируемых файлов:

git config --global alias.untracked 'ls-files --exclude-standard --others'
git config --global alias.ignored 'ls-files --exclude-standard --others --ignored'

Ответ 7

С Python и GitPython:

git.Repo(path).is_dirty(untracked_files=True)

Возвращает True, если репозиторий не очищен

Ответ 8

Вот лучший, самый чистый способ.

function git_dirty {
    text=$(git status)
    changed_text="Changes to be committed"
    untracked_files="Untracked files"

    dirty=false

    if [[ ${text} = *"$changed_text"* ]];then
        dirty=true
    fi

    if [[ ${text} = *"$untracked_files"* ]];then
        dirty=true
    fi

    echo $dirty
}