Проверка на грязный индекс или неотслеживаемые файлы с помощью Git
Как я могу проверить, есть ли какие-либо незафиксированные изменения в моем git-репозитории:
- Изменения добавлены в индекс, но не зафиксированы
- Неотслеживаемые файлы
из скрипта?
git-status
кажется, всегда возвращает ноль с версией git 1.6.4.2.
12 ответов
Отличное время! Я написал пост в блоге именно об этом несколько дней назад, когда выяснил, как добавить информацию о состоянии git в мою подсказку.
Вот что я делаю:
Для грязного статуса:
# Returns "*" if the current git branch is dirty. function evil_git_dirty { [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*" }
Для неотслеживаемых файлов (обратите внимание на
--porcelain
флаг дляgit status
что дает хороший анализируемый вывод):# Returns the number of untracked files function evil_git_num_untracked_files { expr `git status --porcelain 2>/dev/null| grep "^??" | wc -l` }
Хотя git diff --shortstat
удобнее, вы также можете использовать git status --porcelain
для получения грязных файлов:
# Get number of files added to the index (but uncommitted)
expr $(git status --porcelain 2>/dev/null| grep "^M" | wc -l)
# Get number of files that are uncommitted and not added
expr $(git status --porcelain 2>/dev/null| grep "^ M" | wc -l)
# Get number of total uncommited files
expr $(git status --porcelain 2>/dev/null| egrep "^(M| M)" | wc -l)
Обратите внимание 2>/dev/null
отфильтровывает сообщения об ошибках, чтобы вы могли использовать эти команды в не-git каталогах. (Они просто вернутся 0
для файла имеет значение.)
Редактировать:
Вот посты:
Добавление информации о состоянии Git в вашу подсказку терминала
Ключом к надежному "написанию сценариев" Git является использование "сантехнических" команд.
При изменении сантехнических команд разработчики заботятся о том, чтобы они обеспечивали очень стабильные интерфейсы (т. Е. Заданная комбинация состояния репозитория, стандартного ввода, параметров командной строки, аргументов и т. Д. Будет производить одинаковый вывод во всех версиях Git, где команда / вариант существует). Новые варианты вывода в сантехнических командах могут быть введены с помощью новых параметров, но это не может создавать проблем для программ, которые уже были написаны для более старых версий (они не будут использовать новые параметры, так как их не было (или, по крайней мере, не используется) на момент написания сценария).
К сожалению, "каждодневные" команды Git - это "фарфоровые" команды, поэтому большинство пользователей Git могут быть не знакомы с сантехническими командами. Различия между командами фарфора и сантехники сделаны на главной странице руководства git (см. Подразделы " Команды высокого уровня (фарфор)" и " Команды низкого уровня (сантехника)".
Чтобы узнать о несвязанных изменениях, вам, скорее всего, понадобится git diff-index
(сравнить индекс (и, возможно, отслеженные биты рабочего дерева) с каким-то другим древовидным (например, HEAD
)), может быть git diff-files
(сравните рабочее дерево с индексом) и, возможно, git ls-files
(список файлов; например, список неотслеживаемых, незарегистрированных файлов).
(Обратите внимание, что в приведенных ниже командах, HEAD --
используется вместо HEAD
потому что в противном случае команда не выполняется, если существует файл с именем HEAD
.)
Чтобы проверить, что хранилище внесло изменения (еще не зафиксировано), используйте это:
git diff-index --quiet --cached HEAD --
- Если это выходит с
0
тогда не было различий (1
значит были различия).
Чтобы проверить, есть ли в рабочем дереве изменения, которые можно поставить:
git diff-files --quiet
- Код выхода такой же, как для
git diff-index
(0
== нет различий;1
== различия).
Чтобы проверить, изменяется ли комбинация индекса и отслеживаемых файлов в рабочем дереве относительно HEAD
:
git diff-index --quiet HEAD --
- Это как комбинация двух предыдущих. Одно из главных отличий состоит в том, что он по-прежнему будет сообщать об отсутствии различий, если у вас есть поэтапное изменение, которое вы "отменили" в рабочем дереве (вернемся к содержимому, которое находится в
HEAD
). В этой же ситуации обе отдельные команды будут возвращать отчеты о "существующих различиях".
Вы также упомянули неотслеживаемые файлы. Вы можете иметь в виду "неотслеживаемый и игнорируемый", или вы можете иметь в виду просто "неотслеживаемый" (включая игнорируемые файлы). В любом случае, git ls-files
это инструмент для работы:
Для "неотслеживаемых" (включая игнорируемые файлы, если они есть):
git ls-files --others
Для "неотслеживаемых и неопознанных":
git ls-files --exclude-standard --others
Моя первая мысль - просто проверить, есть ли у этих команд вывод:
test -z "$(git ls-files --others)"
- Если это выходит с
0
тогда нет неотслеживаемых файлов. Если это выходит с1
тогда есть неотслеживаемые файлы.
Существует небольшая вероятность того, что это будет переводить аномальные выходы из git ls-files
в отчеты "нет неотслеживаемых файлов" (оба результата приводят к ненулевым выходам вышеуказанной команды). Более надежная версия может выглядеть так:
u="$(git ls-files --others)" && test -z "$u"
- Идея та же, что и в предыдущей команде, но она допускает непредвиденные ошибки
git ls-files
размножаться. В этом случае ненулевой выход может означать "есть неотслеживаемые файлы" или это может означать, что произошла ошибка. Если вы хотите, чтобы результаты "ошибка" совмещались с результатом "нет неотслеживаемых файлов", используйтеtest -n "$u"
(где выход0
означает "некоторые неотслеживаемые файлы", а ненулевое значение означает ошибку или "нет неотслеживаемых файлов").
Другая идея заключается в использовании --error-unmatch
вызывать ненулевой выход, когда нет неотслеживаемых файлов. Это также может привести к конфликту "нет неотслеживаемых файлов" (выход 1
) с "произошла ошибка" (выход ненулевой, но, вероятно, 128
). Но проверка на 0
против 1
ненулевые коды выхода, вероятно, достаточно устойчивы:
git ls-files --others --error-unmatch . >/dev/null 2>&1; ec=$?
if test "$ec" = 0; then
echo some untracked files
elif test "$ec" = 1; then
echo no untracked files
else
echo error from ls-files
fi
Любой из вышеперечисленных git ls-files
примеры могут взять --exclude-standard
если вы хотите рассмотреть только неотслеживаемые и незарегистрированные файлы.
Предполагая, что вы находитесь на Git 1.7.0 или более поздней версии...
Прочитав все ответы на этой странице и немного поэкспериментировав, я думаю, что метод, который находит правильное сочетание правильности и краткости:
test -n "$(git status --porcelain)"
Хотя git учитывает много нюансов между тем, что отслеживается, игнорируется, не отслеживается, но игнорируется и т. Д., Я полагаю, что типичным вариантом использования является автоматизация сценариев сборки, когда вы хотите остановить все, если ваша проверка не чиста.
В этом случае имеет смысл смоделировать действия программиста: type git status
и посмотрите на вывод. Но мы не хотим полагаться на конкретные слова, появляющиеся, поэтому мы используем --porcelain
режим введен в 1.7.0; при включении чистый каталог не приводит к выводу.
Тогда мы используем test -n
чтобы увидеть, был ли какой-либо вывод или нет.
Эта команда вернет 1, если рабочий каталог чистый, и 0, если есть изменения, которые нужно зафиксировать. Вы можете изменить -n
к -z
если хочешь наоборот. Это полезно для связывания этого с командой в скрипте. Например:
test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"
Это фактически говорит: "либо нет никаких изменений, которые нужно внести, либо выведите сигнал тревоги"; эта однострочная строка может быть предпочтительнее оператора if в зависимости от сценария, который вы пишете.
Реализация из ответа VonC:
if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi
Посмотрел несколько из этих ответов... (и имел различные проблемы с *nix и windows, что было моим требованием)... нашел, что следующее работает хорошо...
git diff --no-ext-diff --quiet --exit-code
Чтобы проверить код выхода в *nix
echo $?
#returns 1 if the repo has changes (0 if clean)
Проверить код выхода в окне $
echo %errorlevel%
#returns 1 if the repos has changes (0 if clean)
Получено с https://github.com/sindresorhus/pure/issues/115 Благодаря @paulirish на этом посту, для обмена
Почему бы не заключить в капсулу? git status
со скриптом, который:
- проанализирует вывод этой команды
- вернет соответствующий код ошибки в зависимости от того, что вам нужно
Таким образом, вы можете использовать этот "расширенный" статус в вашем скрипте.
Как 0xfe упоминает в своем превосходном ответе, git status --porcelain
играет важную роль в любом решении на основе сценариев
--porcelain
Дайте вывод в стабильном, простом для анализа формате для скриптов.
В настоящее время это идентично--short output
, но гарантированно не изменится в будущем, что делает его безопасным для сценариев.
Возможность "Сделай сам", обновлена в соответствии с предложением 0xfe
#!/bin/sh
exit $(git status --porcelain | wc -l)
Как отметил Chris Johnsen, это работает только на Git 1.7.0 или новее.
Вы также можете сделать
git describe --dirty
, Он добавит слово "-dirty" в конце, если обнаружит грязное рабочее дерево. В соответствии с git-describe(1)
:
--dirty[=<mark>]
Describe the working tree. It means describe HEAD and appends <mark> (-dirty by default) if
the working tree is dirty.
, Предостережение: неотслеживаемые файлы не считаются "грязными", потому что, как говорится на man-странице, он заботится только о рабочем дереве.
Мне часто требовался простой способ завершить сборку, если в конце выполнения есть какие-либо измененные отслеживаемые файлы или неотслеживаемые файлы, которые не игнорируются.
Это очень важно для избежания случая, когда сборки производят остатки.
Пока что лучшая команда, которую я использовал в итоге, выглядит так:
test -z "$(git status --porcelain | tee /dev/fd/2)" || \
{{ echo "ERROR: git unclean at the end, failing build." && return 1 }}
Это может выглядеть немного сложным, и я был бы признателен, если бы кто-нибудь нашел короткий вариант, который поддерживает желаемое поведение
- нет выходных данных и выходного кода succcess, если что-то в порядке
- код выхода 1, если он не работает
- сообщение об ошибке на stderr с объяснением причины сбоя
- отобразить список файлов, вызвавших сбой, снова stderr.
Это более дружественный вариант для оболочки, позволяющий выяснить, существуют ли в хранилище неотслеживаемые файлы:
# Works in bash and zsh
if [[ "$(git status --porcelain 2>/dev/null)" = *\?\?* ]]; then
echo untracked files
fi
Это не форк второй процесс, grep
и не нуждается в проверке того, находитесь ли вы в git-репозитории или нет. Что удобно для подсказок оболочки и т. Д.
Ответ @eduard-wirch был довольно полным, но, так как я хотел проверить оба одновременно, вот мой последний вариант.
set -eu
u="$(git ls-files --others)"
if ! git diff-index --name-only --quiet HEAD -- || [ -z "${u:-}" ]; then
dirty="-dirty"
fi
Когда мы не выполняем команду set -e или эквивалентную, мы можем вместо u="$(git ls-files --others)" || exit 1
(или вернуть, если это работает для используемой функции)
Таким образом, untracked_files устанавливается только в том случае, если команда выполнена успешно.
после чего мы можем проверить оба свойства и установить переменную (или любую другую).
Самый простой автоматический тест, который я использую, чтобы обнаружить грязное состояние = любые изменения, включая неотслеживаемые файлы:
git add --all
git diff-index --exit-code HEAD
НОТА:
- Без
add --all
diff-index
не замечает неотслеживаемых файлов. - Обычно я бегу
git reset
после проверки кода ошибки, чтобы отменить все назад.
Там может быть лучшая комбинация ответов из этой темы.. но это работает для меня... для вашего .gitconfig
"s [alias]
раздел...
# git untracked && echo "There are untracked files!"
untracked = ! git status --porcelain 2>/dev/null | grep -q "^??"
# git unclean && echo "There are uncommited changes!"
unclean = ! ! git diff --quiet --ignore-submodules HEAD > /dev/null 2>&1
# git dirty && echo "There are uncommitted changes OR untracked files!"
dirty = ! git untracked || git unclean
Вот лучший, самый чистый способ. Выбранный ответ по какой-то причине у меня не сработал, он не собирал инсценированные изменения, которые были новыми файлами, которые не были зафиксированы.
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
}