Подтвердить что ты не робот

Тестирование того, что должно быть зафиксировано в привязке до фиксации

Интернет абсолютно завален неправильными и неидеальными ответами на этот вопрос. Это печально, потому что вы думаете, что это будет обычная вещь, которую вы хотели бы сделать.

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

Очевидное, что нужно сделать, это git stash --keep-index --include-untracked в начале pre-commit и git pop на выходе. Таким образом, вы тестируете индекс (чистый), который мы хотим.

К сожалению, это создает маркеры конфликтов слияния, если вы используете git add --patch (особенно если вы редактируете ханки), так как содержимое [email protected]{0} может не совпадать с деревом работы после фиксации.

Другим распространенным решением является клонирование репозитория и запуск тестов в новом временном. Есть две проблемы: одна заключается в том, что мы еще не сделали этого, поэтому мы не можем легко получить копию репозитория в состоянии, которое мы собираемся совершить (я уверен, что есть способ сделать это, но меня это не интересует, потому что:). Во-вторых, мои тесты могут быть чувствительны к местоположению текущего рабочего каталога. Например, из-за конфигурации локальной среды.

Итак: как я могу восстановить свое рабочее дерево в любом состоянии, которое было до git stash --keep-index --include-untracked, без введения маркеров конфликтов слияния и без изменения пост-фиксации HEAD?

4b9b3361

Ответ 1

Если клонирование всего репо слишком дорого, возможно, вам просто нужна копия рабочего каталога. Создание копии было бы проще, чем пытаться справиться с конфликтами. Например:

#!/bin/sh -e

trap 'rm -rf $TMPD' 0
mkdir ${TMPD=$PWD/.tmpdir}
git ls-tree -r HEAD | while read mod type sha name; do
    if test "$type" = blob; then
        mkdir -p $TMPD/$( dirname "$name" ) 
        git show $sha > $TMPD/"$name";
        chmod $mod $TMPD/"$name"
    fi
done
cd $TMPD
git diff --cached HEAD | patch
# Run tests here

Это дамп состояния дерева, как и после фиксации в $TMPD, поэтому вы можете провести там свои тесты. Вы должны получить временный каталог более безопасным образом, чем это делается здесь, но для того, чтобы окончательный diff работал (или для упрощения script и cd ранее), он должен быть дочерним элементом рабочего каталога.

Ответ 2

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

tmpdir=$(mktemp -d) # Or put it wherever you like
git archive HEAD | tar -xf - -C "$tmpdir"
git diff --staged | patch -p1 -d "$tmpdir"
cd "$tmpdir"
...

Это в основном решение William Pursell, но использует git archive, что делает код более простым, и я ожидаю, что это будет быстрее.

В качестве альтернативы, сначала cd'ing:

cd somewhere
git -C path/to/repo archive HEAD | tar -xf -
git -C path/to/repo diff --staged | patch -p1
...

git -C требуется Git 1.8.5.

Ответ 3

git дерево записи полезно в pre-commit hooks. Он записывает дерево в репо индекса (это дерево будет повторно использоваться, если и когда фиксация завершена.)

Как только дерево будет записано в репо, вы можете использовать git archive | tar -x для записи дерева во временный каталог.

Например:

#!/bin/bash

TMPDIR=$(mktemp -d)
TREE=$(git write-tree)
git archive $TREE | tar -x -C $TMPDIR

# Run tests in $TMPDIR

RESULT=$?
rm -rf "$TMPDIR"
exit $RESULT

Ответ 4

Я нашел следующее полезным:

## bash declare -a files readarray -t files < <(git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^[ACM]/') # declare -a delfiles readarray -t delfiles < <(git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^D/') # declare -a huhfiles readarray -t huhfiles < <(git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^\?/')

Может быть неэффективно вызывать git status три раза, но этот код менее сложный, чем вызов один раз, сохранение в памяти и циклизация результатов. И я не думаю, что приведение результатов в файл temp и чтение его с диска три раза будет быстрее. Может быть. Я не знаю. Это был первый проход. Не стесняйтесь критиковать.

Ответ 5

Наконец-то я нашел решение, которое искал. Проверяется только состояние индекса перед фиксацией, и оно выводит индекс и рабочее дерево точно так же, как и до фиксации.

Если вы видите какие-либо проблемы или лучший способ, ответьте, пожалуйста, как комментарий или собственный ответ.

Предполагается, что ничто другое не попытается зашифровать или иным образом изменить репозиторий git или рабочее дерево во время его работы. Это не дает никаких гарантий, может быть неправильным и выбросить ваш код на ветер. ИСПОЛЬЗУЙТЕ С ПРЕДОСТЕРЕЖЕНИЕМ.

# pre-commit.sh
REPO_PATH=$PWD
git stash save -q --keep-index --include-untracked # ([email protected]{1})
git stash save -q                                  # ([email protected]{0})

# Our state at this point:
# * clean worktree
# * [email protected]{0} contains what is to be committed
# * [email protected]{1} contains everything, including dirt

# Now reintroduce the changes to be committed so that they can be tested
git stash apply [email protected]{0} -q

git_unstash() {
    G="git --work-tree \"$REPO_PATH\" --git-dir \"$REPO_PATH/.git\"" 
    eval "$G" reset -q --hard             # Clean worktree again
    eval "$G" stash pop -q [email protected]{1}      # Put worktree to original dirty state
    eval "$G" reset -q [email protected]{0} .        # Restore index, ready for commit
    eval "$G" stash drop -q [email protected]{0}     # Clean up final remaining stash
}
trap git_unstash EXIT

... tests against what is being committed go here ...