Как программно определить, есть ли незафиксированные изменения?
В Makefile я хотел бы выполнить определенные действия, если есть незафиксированные изменения (либо в рабочем дереве, либо в индексе). Какой самый чистый и эффективный способ сделать это? Команда, которая завершается с возвращаемым значением ноль в одном случае и ненулевое значение в другом, подойдет моим целям.
я могу бежать git status
и передать вывод через grep
Но я чувствую, что должен быть лучший способ.
11 ответов
ОБНОВЛЕНИЕ: ОП Daniel Stutzbach указывает в комментариях, что эта простая команда git diff-index
работал на него
git diff-index --quiet HEAD --
Затем вы можете увидеть " Как проверить, успешно ли выполнена команда?", Если вы используете ее в скрипте bash:
git diff-index --quiet HEAD -- || echo "untracked"; // do something about it
Примечание: как прокомментировал Anthony Sottile
git diff-index HEAD ...
потерпит неудачу на ветке, которая не имеет коммитов (например, недавно инициализированный репозиторий).
Один из обходных путей, который я нашел, этоgit diff-index $(git write-tree) ...
"Программно" означаетникогда не полагаться на фарфоровые команды.
Всегда полагайтесь на сантехнические команды.
Смотрите также " Проверка на наличие грязного индекса или неотслеживаемых файлов с помощью 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
}
Хотя другие решения очень основательны, если вы хотите что-то очень быстрое и грязное, попробуйте что-то вроде этого:
[[ -z $(git status -s) ]]
Он просто проверяет, есть ли какие-либо выходные данные в сводке состояния.
git diff --exit-code
вернет ненулевое значение, если будут какие-либо изменения; git diff --quiet
то же самое без вывода. Поскольку вы хотите проверить рабочее дерево и индекс, используйте
git diff --quiet && git diff --cached --quiet
Или же
git diff --quiet HEAD
Любой из них скажет вам, есть ли незавершенные изменения, которые поставлены или нет.
Расширяя ответ @Nepthar:
if [[ -z $(git status -s) ]]
then
echo "tree is clean"
else
echo "tree is dirty, please commit changes before running this"
exit
fi
Некоторые ответы одновременно чрезмерно усложняют дело и не приводят к достижению желаемого результата. Например, в принятом ответе отсутствуют неотслеживаемые файлы.
Используйте предоставленные git status --porcelain
который предназначен для машинного анализа, несмотря на то, что некоторые люди (неправильно) говорят об обратном в комментариях. Если что-то появится вgit status
, тогда я считаю рабочий каталог грязным. Так что проверяю на чистоту тестом[ -z "$(git status --porcelain=v1 2>/dev/null)" ]
, который также пройдет, если запускается вне каталога git.
Минимальный рабочий пример:
[ -z "$(git status --porcelain=v1 2>/dev/null)" ] && echo "git undirty"
Все, что появляется в git status
(на данный момент) будет запускать этот тест правильно. В=v1
bit обеспечивает согласованный выходной формат для всех версий git.
Дополнительно: подсчет грязных файлов
Вдохновленный этим ответом. Выgrep
линии git status --porcelain=v1
выход. Первые два символа в каждой строке указывают на статус конкретного файла. После grepping вы подсчитываете, сколько людей имеют этот статус, передавая вывод вwc -l
который считает количество строк.
Например, этот скрипт распечатает некоторую информацию, если он будет запущен в репозитории git.
#!/bin/sh
GS=$(git status --porcelain=v1 2>/dev/null) # Exit code 128 if not in git directory. Unfortunately this exit code is a bit generic but it should work for most purposes.
if [ $? -ne 128 ]; then
function _count_git_pattern() {
echo "$(grep "^$1" <<< $GS | wc -l)"
}
echo "There are $(_count_git_pattern "??") untracked files."
echo "There are $(_count_git_pattern " M") unstaged, modified files."
echo "There are $(_count_git_pattern "M ") staged, modified files."
fi
Рабочее дерево "чистое", если
git ls-files --deleted --modified --others --exclude-standard
ничего не возвращает.
Объяснение
-
--deleted
проверить файлы, удаленные в рабочем дереве -
--modified
проверить файлы, измененные в рабочем дереве -
--others
проверьте наличие файлов, добавленных в рабочее дерево -
--exclude-standard
игнорировать в соответствии с обычным.gitignore
,.git/info/exclude
... правила
Этот вывод пуст, если рабочее дерево чистое.
Я создал несколько удобных псевдонимов 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 "$@" | 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'
С Python и GitPython:
git.Repo(path).is_dirty(untracked_files=True)
Возвращает True, если хранилище не является чистым
Как указано в другом ответе, достаточно простой команды:
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...
Слово предостережения: эта команда игнорирует неотслеживаемые файлы.
Протестировано в терминале bash на Linux Ubuntu.
Сценарий оболочки для программной интерпретации вывода
...и сказать вам, если:
- у него была ошибка
- это показывает, что ваше рабочее дерево чистое (нет незафиксированных изменений) или
- это показывает, что ваше рабочее дерево грязное (у вас есть незафиксированные изменения).
Здесь есть отличный ответ: Unix и Llinux: определите, чист ли рабочий каталог Git из сценария . Мой ответ основан на этом.
Мы будем использовать опцию with, потому что она предназначена для анализа скриптами!
Отman git status
(выделение добавлено):
--porcelain[=<version>]
Предоставьте вывод в удобном для анализа формате для сценариев. Это похоже на краткий вывод, но останется стабильным в версиях Git и независимо от конфигурации пользователя. Подробнее см. ниже.
The
version
Параметр используется для указания версии формата. Это необязательно и по умолчанию используется исходный формат версии v1.
Итак, сделайте это:
Опция 1
if output="$(git status --porcelain)" && [ -z "$output" ]; then
echo "'git status --porcelain' had no errors AND the working directory" \
"is clean."
else
echo "Working directory has UNCOMMITTED CHANGES."
fi
Первая часть,if output=$(git status --porcelain)
потерпит неудачу и прыгнет вelse
оговорка, еслиgit status --porcelain
команда имеет ошибку. 2-я часть,&& [ -z "$output" ]
, тесты, чтобы увидеть, еслиoutput
переменная содержит пустой (z
ero-длина) строка. Если да, то все чисто и изменений нет.
Вариант 2
Однако обычно я предпочитаю отрицать тест с помощью-n
(отличное от нуля) вместо-z
(ноль) и сделайте это так:
if output="$(git status --porcelain)" && [ -n "$output" ]; then
echo "'git status --porcelain' had no errors AND the working directory" \
"is dirty (has UNCOMMITTED changes)."
# Commit the changes here
git add -A
git commit -m "AUTOMATICALLY COMMITTING UNCOMMITTED CHANGES"
fi
Вариант 3
Более детальный способ написания первого блока кода выше был бы таким:
if ! git_status_output="$(git status --porcelain)"; then
# `git status` had an error
error_code="$?"
echo "'git status' had an error: $error_code"
# exit 1 # (optional)
elif [ -z "$git_status_output" ]; then
# Working directory is clean
echo "Working directory is clean."
else
# Working directory has uncommitted changes.
echo "Working directory has UNCOMMITTED CHANGES."
# exit 2 # (optional)
fi
Я протестировал весь приведенный выше код, скопировав и вставив целые блоки в свой терминал в репозитории в разных состояниях, и он отлично работает для всех трех условий:
- ваша команда неверна или написана с ошибкой
- чистый (нет незафиксированных изменений)
-
git status
грязный (у вас есть незафиксированные изменения)
Чтобы заставить'git status' had an error
вывод, просто опечатка--porcelain
вариант, написав его как--porcelainn
или что-то в этом роде, и вы увидите этот вывод в самом конце:
'git status' had an error: 0
Вот лучший, самый чистый способ.
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
}