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

226

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

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

из script?

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

  • 3
    Состояние git вернет 1, если есть неизмененные измененные файлы. Но в целом я нахожу, что инструменты git не особенно тщательны с состоянием возврата. Например, git diff возвращает 0 независимо от того, есть ли различия.
  • 11
    @intuited: если вам нужен diff чтобы указать на наличие или отсутствие различий, а не на успешное выполнение команды, тогда вам нужно использовать --exit-code или --quiet . Команды git, как правило, очень согласуются с возвратом нулевого или ненулевого кода завершения, чтобы указать успешность команды.
Показать ещё 3 комментария
Теги:

14 ответов

171
Лучший ответ

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

  • 8
    Стоит отметить, что завершение git bash поставляется с функцией оболочки для выполнения почти всего, что вы делаете с вашим приглашением - __git_ps1 . Он показывает имена веток, включая особый режим, если вы находитесь в процессе ребазирования, am-apply, merge или bisect. И вы можете установить переменную среды GIT_PS1_SHOWDIRTYSTATE чтобы получить звездочку для неустановленных изменений и плюс для поэтапных изменений. (Я думаю, что вы также можете получить его, чтобы указать неотслеживаемые файлы, и дать вам вывод git-describe description)
  • 8
    Предупреждение: git diff --shortstat выдаст ложный отрицательный результат, если изменения уже внесены в индекс.
Показать ещё 10 комментариев
380

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

  • 5
    Я хотел бы отметить, что git ls-files --others выдает локальные неотслеживаемые файлы, в то время как git status --porcelain принятого ответа выдает все неотслеживаемые файлы, которые находятся в репозитории git. Я не тот, который хотел оригинальный плакат, но разница между ними интересна.
  • 0
    В Ubuntu 10.04 git ls-files --other --error-unmatch --exclude-standard всегда возвращает 0, независимо от того, есть ли у меня неотслеживаемые файлы. Я что-то пропустил?
Показать ещё 10 комментариев
128

Предполагая, что вы находитесь на 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, который вы пишете.

  • 0
    Для меня все другие команды давали разные результаты в одном и том же повторении между Linux и Windows. Эта команда дала мне одинаковый вывод в обоих.
  • 5
    Спасибо за то, что ответили на вопрос и не болтаете снова и снова, никогда не давая четкого ответа.
13

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

4

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

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

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

  • 5
    Проблема в том, что вы не можете с уверенностью ожидать, что строка «рабочий каталог будет чистой» в будущих версиях. Флаг --porcelain предназначался для разбора, поэтому лучшим решением было бы: exit $ (git status --porcelain | wc -l)
  • 0
    @ 0xfe - Вы знаете, когда был добавлен флаг --porcelain ? Не работает с 1.6.4.2.
Показать ещё 4 комментария
4

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

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

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


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

--porcelain

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

  • 0
    Потому что я ленивый, наверное. Я подумал, что для этого есть встроенная программа, так как это выглядит довольно часто встречающимся вариантом использования.
  • 0
    Я разместил решение на основе вашего предложения, хотя я не очень доволен им.
2

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

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

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

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

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

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

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

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

1

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

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

1

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

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

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

0

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

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

Примечание:

  • Без add --all diff-index не замечает файлы без следа.
  • Как правило, я запускаю git reset после тестирования кода ошибки, чтобы полностью отключить все.
  • 0
    Вопрос, в частности, «из сценария» ... не очень хорошая идея менять индекс только для проверки на грязное состояние.
  • 0
    @ ScottJ, когда это решает проблему, не все строго следят за тем, можно ли изменить индекс или нет. Рассмотрим случай автоматической работы, который касается автоматических исправлений источников с номером версии и создания тега - все, что вам нужно, это убедиться, что никакие другие локальные модификации не останутся на пути (независимо от того, присутствуют они в индексе или нет). До сих пор это был надежный тест на любые изменения, включая неотслеживаемые файлы .
0

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

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

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
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню