Проверка на наличие грязного индекса или неотслеживаемых файлов с помощью Git - PullRequest
232 голосов
/ 17 апреля 2010

Как я могу проверить, есть ли какие-либо незафиксированные изменения в моем git-хранилище:

  1. Изменения добавлены в индекс, но не зафиксированы
  2. неотслеживаемых файлов

из скрипта?

git-status, кажется, всегда возвращает ноль в git версии 1.6.4.2.

Ответы [ 14 ]

385 голосов
/ 17 апреля 2010

Ключом к надежному «написанию сценариев» 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, если вы хотите рассматривать только неотслеживаемые и незарегистрированные файлы.

164 голосов
/ 17 апреля 2010

Отличное время! Я написал пост в блоге именно об этом несколько дней назад, когда выяснил, как добавить информацию о состоянии git в мою подсказку.

Вот что я делаю:

  1. Для грязного статуса:

    # Returns "*" if the current git branch is dirty.
    function evil_git_dirty {
      [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*"
    }
    
  2. Для неотслеживаемых файлов (обратите внимание на флаг --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

132 голосов
/ 21 апреля 2011

Предполагается, что вы находитесь на Git 1.7.0 или более поздней версии ...

Прочитав все ответы на этой странице и немного поэкспериментировав, я думаю, что метод, который находит правильное сочетание правильности и краткости:

test -n "$(git status --porcelain)"

Хотя git учитывает много нюансов между тем, что отслеживается, игнорируется, не отслеживается, но игнорируется и т. Д., Я считаю, что типичный вариант использования - для автоматизации сценариев сборки, где вы хотите остановить все, если проверка не чиста .

В этом случае имеет смысл смоделировать действия программиста: введите git status и посмотрите на вывод. Но мы не хотим полагаться на показ определенных слов, поэтому мы используем режим --porcelain, представленный в 1.7.0; при включении чистый каталог не приводит к выводу.

Затем мы используем test -n, чтобы увидеть, был ли какой-либо вывод или нет.

Эта команда вернет 1, если рабочий каталог чистый, и 0, если есть изменения, которые нужно зафиксировать. Вы можете изменить -n на -z, если хотите обратное. Это полезно для связывания этого с командой в сценарии. Например:

test -z "$(git status --porcelain)" || red-alert "UNCLEAN UNCLEAN"

Это фактически говорит: «либо нет изменений, которые необходимо внести, либо отключите сигнал тревоги»; эта однострочная строка может быть предпочтительнее оператора if в зависимости от сценария, который вы пишете.

13 голосов
/ 05 августа 2014

Реализация от VonC ответа:

if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi
6 голосов
/ 02 сентября 2016

Посмотрел несколько из этих ответов ... (и имел различные проблемы с * 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 за публикацию

4 голосов
/ 17 апреля 2010

Возможность "Сделай сам", обновлена ​​в соответствии с предложением 0xfe

#!/bin/sh
exit $(git status --porcelain | wc -l) 

Как заметил Крис Джонсен , это работает только на Git 1.7.0 или новее.

4 голосов
/ 17 апреля 2010

Почему бы не инкапсулировать 'git status сценарием, который:

  • проанализирует вывод этой команды
  • вернет соответствующий код ошибки в зависимости от того, что вам нужно

Таким образом, вы можете использовать этот «расширенный» статус в вашем скрипте.


Как 0xfe упоминает в своем превосходном ответе , git status --porcelain полезен в любом решении на основе сценариев

--porcelain

Дайте вывод в стабильном, простом для анализа формате для сценариев.
В настоящее время это идентично --short output, но гарантированно не изменится в будущем, что делает его безопасным для сценариев.

2 голосов
/ 27 марта 2019

@ 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, устанавливается, только если команда выполнена успешно.

, после чего мы можем проверить оба свойства и установить переменную (или любую другую).

2 голосов
/ 03 марта 2019

Мне часто требовался простой способ завершить сборку, если в конце выполнения есть какие-либо измененные отслеживаемые файлы или неотслеживаемые файлы, которые не игнорируются.

Это очень важно для избежания случая, когда сборки производят остатки.

Пока лучшая команда, которую я использовал в итоге, выглядит так:

 test -z "$(git status --porcelain | tee /dev/fd/2)" || \
     {{ echo "ERROR: git unclean at the end, failing build." && return 1 }}

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

  • без вывода и успешного завершения кода выхода, если все в порядке
  • код выхода 1 в случае сбоя
  • сообщение об ошибке на stderr, объясняющее причину сбоя
  • показать список файлов, вызвавших сбой, снова stderr.
1 голос
/ 12 августа 2016

Вы также можете сделать

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-странице, он заботится только о рабочем дереве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...