2012-09-07 7 views
13

Internet jest całkowicie zaśmiecony niepoprawnymi i nieidealnymi odpowiedziami na to pytanie. Jest to niefortunne, ponieważ można by pomyśleć, że to zwykła rzecz, którą chciałbyś zrobić.Testowanie tego, co ma się stać w haku przed popełnieniem przestępstwa

Problem: Po uruchomieniu haka pre-commit repozytorium może nie być czyste. Więc jeśli naiwnie przeprowadzisz swoje testy, nie będą one przeciwko temu, co popełniasz, ale jakikolwiek brud jest w twoim drzewie pracy.

Oczywistym rozwiązaniem jest git stash --keep-index --include-untracked na początku pre-commit i git pop przy wyjściu. W ten sposób testujesz przeciwko (czystemu) indeksowi, który jest tym, czego chcemy.

Niestety, generuje to znaczniki konfliktów scalających, jeśli używasz git add --patch (szczególnie jeśli edytujesz porcje), ponieważ zawartość [email protected]{0} może nie być zgodna z drzewem roboczym po zatwierdzeniu.

Innym powszechnym rozwiązaniem jest sklonowanie repozytorium i przeprowadzenie testów w nowym tymczasowym. Są na to dwa problemy: Po pierwsze, nie mamy jeszcze zobowiązania, więc nie możemy łatwo uzyskać kopii repozytorium w stanie, w którym mamy zamiar dokonać (jestem pewien, że jest sposób na zrobienie tego , ale nie jestem zainteresowany, ponieważ :). Po drugie, moje testy mogą być wrażliwe na lokalizację bieżącego katalogu roboczego. Na przykład ze względu na konfigurację lokalnego środowiska.

A więc: Jak mogę przywrócić moje drzewo robocze do stanu, w jakim było przed git stash --keep-index --include-untracked, bez wprowadzania znaczników konfliktu korespondencji seryjnej i bez modyfikowania po zatwierdzeniu HEAD?

+0

skrypt pre-commit odbiera dane popełniane jako wejście. Dlaczego musisz patrzeć na cokolwiek innego? Być może to, co próbujesz zrobić, najlepiej zrobić w czymś innym niż haczyk przed popełnieniem błędu. Jakie testy chcesz wykonać, które wymagają dostępu do pełnego repozytorium? –

+0

@WilliamPursell: Co rozumiesz przez "dane, które zostały popełnione?". Skrypt przed zatwierdzeniem działa w moim drzewie roboczym (to jest w bazie źródłowego repozytorium). Problem polega na tym, że jeśli wprowadzisz jakieś zmiany do repozytorium i tylko wykonasz kilka z nich (np. Dodasz niektóre pliki, ale nie inne), to nie przetestujesz zatwierdzenia, zanim się to stanie (co chcę zrobić), będziesz testować wszystko, co masz w swoim katalogu roboczym. – pwaller

+0

Łatka, którą popełniasz, jest dostępna na stdin do haka przed zatwierdzeniem. Co testujesz, jeśli nie łatka, która jest zatwierdzana? Celem haka przed zatwierdzeniem jest weryfikacja poprawki. –

Odpowiedz

2

Jeśli klonowanie całego repozytorium jest zbyt kosztowne, być może potrzebujesz tylko kopii katalogu roboczego. Sporządzenie kopii byłoby prostsze niż rozwiązywanie konfliktów. Na przykład:

#!/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 

Będzie to zrzucić stan drzewa, jak to będzie po popełnić w $ TMPD, więc można uruchomić tam swoje badania. Powinieneś uzyskać katalog tymczasowy w sposób bardziej bezpieczny niż tutaj, ale aby ostatnia dyferencjał działał (lub uprościł skrypt i cd wcześniej), musi być potomkiem katalogu roboczego.

-1

W końcu znalazłem rozwiązanie, którego szukałem. Sprawdzany jest tylko stan indeksu przed zatwierdzeniem i pozostawia indeks i drzewo robocze dokładnie tak, jak przed zatwierdzeniem.

Jeśli widzisz jakieś problemy lub lepszy sposób, proszę odpowiedz, jako komentarz lub własną odpowiedź.

Zakłada się, że nic więcej nie spróbuje przechować lub w inny sposób zmodyfikować repozytorium git lub drzewa roboczego podczas jego działania. To nie jest objęte gwarancją, może być nie tak i wyrzuć kod na wiatr. UŻYWAJ OSTROŻNIE.

# 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 ... 
+0

Jak wspomniano w komentarzach w następującym poście, to nie będzie działać poprawnie w przypadku zmiany zatwierdzenia lub jeśli nie masz brudnego drzewa roboczego. http: // codeinthehole.com/writing/tips-for-using-a-git-pre-commit-hook/ – pwaller

2

Jeśli możesz sobie pozwolić, aby korzystać z katalogu tymczasowego (tj. Wykonać pełną kopię bieżącego kasie) można użyć tymczasowego katalogu tak:

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" 
... 

Jest to w zasadzie William Pursell użytkownika rozwiązanie, ale korzysta z git archive, dzięki czemu kod jest prostszy i oczekuję, że będzie szybszy.

Alternatywnie, w cd'ing pierwsze:

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

git -C wymaga GIT 1.8.5.

+0

Głosowałem za tobą, ale nie satysfakcjonuje "zostań w PWD podczas testowania", co jest konieczne w niektórych systemach, takie jak biblioteki go. – pwaller

+0

Poza obawami związanymi z pwallerem, wierzę, że to rozwiązanie nie działa, jeśli robisz 'git commit -a', ponieważ twój skrypt zakłada, że ​​wykonujemy tylko etapowe pliki. – TanguyP

1

git write-tree jest przydatny w hakach pre-commit. Zapisuje drzewo do repozytu indeksu (to drzewo zostanie ponownie wykorzystane, jeśli i kiedy zatwierdzenie zostanie sfinalizowane).

Gdy drzewo zostanie zapisane na repozytorium, można użyć git archive | tar -x do zapisania drzewa do katalogu tymczasowego .

Np .:

#!/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 
Powiązane problemy