Определите, чист ли рабочий каталог Git из скрипта

84

У меня есть скрипт, который работает rsyncс рабочим каталогом Git в качестве места назначения. Я хочу, чтобы скрипт работал по-разному в зависимости от того, чистый ли рабочий каталог (без изменений для фиксации) или нет. Например, если вывод git statusтакой, как показано ниже, я хочу, чтобы скрипт завершился:

git status
Already up-to-date.
# On branch master
nothing to commit (working directory clean)
Everything up-to-date

Если каталог не чистый, я бы хотел, чтобы он выполнял еще несколько команд.

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

brentwpeterson
источник
Поможет ли здесь проверка состояния из последней команды? ($?)
UVV
Не могли бы вы дать более подробную информацию, пожалуйста? Какова основная идея вашего сценария?
Тахоми
@tachomi Я добавил контекст в редактирование
brentwpeterson
Вы можете просто предположить, что он не чистый, и сделать, git reset --hard origin/branchесли это то, к чему вы
стремитесь
1
@ SnakeDoc Вы могли бы, но я предполагаю, что обратный случай будет более распространенным, то есть выход, если рабочий каталог грязный, чтобы избежать искажения локальных изменений. Рассмотрение обоих случаев сделало бы вопрос более полезным для будущих читателей.
Томас Найман

Ответы:

135

Разбор вывода git status- плохая идея, потому что вывод предназначен для чтения человеком, а не для чтения машиной. Нет гарантии, что выходные данные останутся прежними в будущих версиях Git или в средах с другой конфигурацией.

Комментарий UVVs находится на правильном пути, но, к сожалению, код возврата git statusне изменяется при наличии незафиксированных изменений. Тем не менее, он предоставляет --porcelainопцию, которая приводит к тому, что вывод git status --porcelainскриптов форматируется в простом для анализа формате и остается стабильным во всех версиях Git и независимо от конфигурации пользователя.

Мы можем использовать пустой вывод git status --porcelainкак индикатор того, что нет изменений, которые нужно зафиксировать:

if [ -z "$(git status --porcelain)" ]; then 
  # Working directory clean
else 
  # Uncommitted changes
fi

Если мы не заботимся о неотслеживаемых файлах в рабочем каталоге, мы можем использовать --untracked-files=noопцию, чтобы игнорировать их:

if [ -z "$(git status --untracked-files=no --porcelain)" ]; then 
  # Working directory clean excluding untracked files
else 
  # Uncommitted changes in tracked files
fi

Чтобы сделать это более устойчивым к условиям, которые фактически вызывают git statusсбой без вывода на stdout, мы можем уточнить проверку:

if output=$(git status --porcelain) && [ -z "$output" ]; then
  # Working directory clean
else 
  # Uncommitted changes
fi

Стоит также отметить, что, хотя git statusон и не дает значимого кода выхода, когда рабочий каталог нечист, но git diffпредоставляет --exit-codeопцию, которая делает его похожим на утилиту diff , то есть завершает работу со статусом, 1когда были различия, и 0ни один не был найден.

Используя это, мы можем проверить наличие изменений без изменений с помощью:

git diff --exit-code

и поставил, но не совершил изменения с:

git diff --cached --exit-code

Хотя он git diffможет сообщать о неотслеживаемых файлах в подмодулях с помощью соответствующих аргументов, к --ignore-submodulesсожалению, кажется, что нет способа сделать так, чтобы он сообщал о неотслеживаемых файлах в реальном рабочем каталоге. Если неотслеживаемые файлы в рабочем каталоге актуальны, git status --porcelainвозможно, это лучший выбор.

Томас Найман
источник
4
ughhh git status --porcelainзавершит работу с кодом 0, даже если есть изменения, не подготовленные для коммитов и неотслеживаемых файлов.
Александр Миллс
Я был заинтересован в том, чтобы заранее определить, git stashбудет ли что-либо предприниматься (он не выдает полезного кода возврата). Я должен был добавить, --ignore-submodulesкак иначе git statusбудет означать изменения субмодуля, который git stashигнорирует.
Девин Лейн
1
@AlexanderMills: я наблюдал то же самое. Но потом проверил, что if [ -zделал. В -zозначает , что если следующая строка пуста, если имеет значение true. Другими словами, если это git status --porcelainприводит к отсутствию строки, репо чистое. Если нет, то он перечисляет измененные / добавленные / удаленные файлы и больше не является пустой строкой. ifЗатем вычисляет false.
Адейнак
19

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

git diff-index --quiet HEAD

Код возврата отражает состояние рабочего каталога (0 = чистый, 1 = грязный). Не отслеживаемые файлы игнорируются.

Андре ван Херк
источник
6
Возвращает 0, если в текущем каталоге есть неотслеживаемые файлы.
Адам Паркин
2
Если файлы были затронуты / перезаписаны, но в остальном идентичны индексу, вам нужно сначала запустить их git update-index --refreshраньше git diff-index HEAD. Более подробная информация: stackoverflow.com/q/34807971/1407170
Sffc
@AdamParkin Я просто добавляю все файлы git add .перед его выдачей. Обычно это способ использовать его в сценарии
ceztko
Это здорово. Обратите внимание, что код возврата / выхода, отличный от нуля, также интерпретируется как «ошибка», которая, если вы находитесь в сценарии с набором -e, тогда ваш сценарий завершится, если он «грязный». Этого можно избежать, выполнив set +eперед вызовом gitи добавив еще set -eраз после оценки $?.
Орион Элензил
1

Незначительное продолжение превосходного ответа Андре .

Это один из способов оценить результаты, а также избежать ловушки, если вы находитесь в сценарии, который ранее выполнял set -e .

Не отслеживаемые файлы игнорируются.

set +e
git diff-index --quiet HEAD

if [ $? == 1 ] ; then
  set -e
  GIT_MODS="dirty"
else
  set -e
  GIT_MODS="clean"
fi
Орион Элензил
источник