Проверка на наличие грязных индексов или файлов без следа с помощью Git

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

  • Изменения добавлены в индекс, но не совершены
  • Отслеживаемые файлы

из script?

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

Ответы

Ответ 1

Отличное время! Я написал сообщение в блоге об этом несколько дней назад, когда выяснил, как добавить в мою подсказку информацию о состоянии 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 -enabled Shell Prompt

Ответ 2

Ключом к надежно "scripting" Git является использование команд "сантехника".

Разработчики заботятся при изменении команд сантехники, чтобы убедиться, что они обеспечивают очень стабильные интерфейсы (т.е. заданная комбинация состояния репозитория, stdin, параметров командной строки, аргументов и т.д. будет производить одинаковый вывод во всех версиях Git где имеется команда/параметр). Новые вариации вывода в командах сантехники могут быть введены с помощью новых параметров, но это не может вызвать проблем для программ, которые уже были написаны в отношении старых версий (они не будут использовать новые параметры, так как они не существовали (или, по крайней мере, были не использовался) в момент написания script).

К сожалению, "ежедневные команды Git - это" фарфоровые команды ", поэтому большинство пользователей Git могут быть не знакомы с командами сантехники. Различие между командой фарфора и сантехники производится в основном git manpage (см. Подразделы под названием Команды высокого уровня (фарфор) и Низкоуровневые команды (сантехника).


Чтобы узнать о неуспешных изменениях, вам, скорее всего, понадобится 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). В этой же ситуации две отдельные команды будут возвращать отчеты о наличии различий.

Вы также упомянули невоспроизводимые файлы. Вы могли бы означать "untracked and unignored", или вы могли бы означать просто "без следа" (включая проигнорированные файлы). В любом случае git ls-files является инструментом для задания:

Для "untracked" (будут включены проигнорированные файлы, если они есть):

git ls-files --others

Для "untrecked and unignored":

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, чтобы вызвать ненулевой выход, если нет необработанных файлов. Это также сопряжено с риском объединения "без файлов без следа" (exit 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, если вы хотите рассмотреть только невоспроизводимые и незарегистрированные файлы.

Ответ 3

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

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

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

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

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

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

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

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

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

Ответ 4

Ответ из VonC:

if [[ -n $(git status --porcelain) ]]; then echo "repo is dirty"; fi

Ответ 5

Посмотрел несколько из этих ответов... (и у меня были разные проблемы с * nix и окнами, что было требованием, которое у меня было)... нашел, что следующее работает хорошо...

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 на этом посту для обмена

Ответ 6

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

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

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


Как 0xfe упоминается в его отличном ответе, git status --porcelain является инструментом в любом решении script

--porcelain

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

Ответ 7

Одна возможность DIY, обновленная, чтобы следовать 0xfe

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

Как отмечено Chris Johnsen, это работает только на Git 1.7.0 или новее.

Ответ 8

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

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

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

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

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

  • нет выходного кода и выходного кода succcess, если что-то в порядке
  • код выхода 1, если он не работает
  • сообщение об ошибке на stderr с объяснением причины сбоя
  • отобразить список файлов, вызвавших сбой, снова stderr.

Ответ 9

Это более дружественная оболочка для определения того, существуют ли в репозитории какие-либо файлы без следа:

# Works in bash and zsh
if [[ "$(git status --porcelain 2>/dev/null)" = *\?\?* ]]; then
  echo untracked files
fi

Это не вызывает второй процесс, grep и не нуждается в проверке, если вы находитесь в репозитории git или нет. Это удобно для подсказок оболочки и т.д.

Ответ 10

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

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.

. Предостережение: файлы без следа не считаются "грязными", потому что, как говорится в manpage, он заботится только о рабочем дереве.

Ответ 11

Ответ @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 u="$(git ls-files --others)" || exit 1 (или возврат, если это работает для используемой функции)

Таким образом, untracked_files устанавливается только в том случае, если команда выполнена успешно.

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

Ответ 12

Там может быть лучшее сочетание ответов из этой темы.. но это работает для меня... для вашего раздела .gitconfig [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

Ответ 13

Самый простой автоматический тест, который я использую для обнаружения грязного состояния= любых изменений, включая невоспроизводимые файлы:

git add --all
git diff-index --exit-code HEAD

Примечание:

  • Без add --all diff-index не замечает файлы без следа.
  • Как правило, я запускаю git reset после тестирования кода ошибки, чтобы полностью отключить все.

Ответ 14

Вот лучший, самый чистый способ. Выбранный ответ по какой-то причине не работал у меня, он не подхватил изменения, которые были новыми, которые не были зафиксированы.

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
}