Определите, чист ли рабочий каталог 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

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

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


Поможет ли здесь проверка состояния из последней команды? ($?)
— 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.