2012-09-07 28 views
13

互聯網絕對存在着對此問題的不正確和非理想答案。這很不幸,因爲你會認爲這是你想做的事。測試預先提交的內容中的內容

問題:當pre-commit鉤子運行時,存儲庫可能不乾淨。所以如果你天真地運行你的測試,他們不會反對你所做的事情,但不管你的工作樹中有什麼污垢。

顯而易見的事情是在pre-commitgit pop的出口處開始時的git stash --keep-index --include-untracked。這樣,你正在測試(純)索引,這是我們想要的。

不幸的是,如果您使用git add --patch(尤其是如果您編輯區塊),則會生成合並衝突標記,因爲[email protected]{0}的內容可能與提交後的工作樹不匹配。

另一個常見的解決方案是克隆存儲庫並在新的臨時文件中運行測試。這有兩個問題:一個是我們還沒有完成任務,所以我們不能輕易地在我們即將提交的狀態下獲取存儲庫的副本(我確信有一種方法可以做到這一點,但我不感興趣,因爲:)。其次,我的測試可能對當前工作目錄的位置很敏感。例如由於本地環境配置。

所以:如何在不引入合併衝突標記的情況下將工作樹恢復到git stash --keep-index --include-untracked之前的狀態,並且不需要修改提交後的HEAD

+0

預commit腳本接收數據被提交作爲輸入。你爲什麼需要看別的東西?也許你試圖做的事情最好在除了預先提交鉤子以外的其他事情上完成。你想做什麼樣的測試需要訪問完整的存儲庫? –

+0

@WilliamPursell:「承諾的數據」是什麼意思?預提交腳本在我的工作樹中運行(即源存儲庫的基礎)。問題是,如果您對存儲庫進行了一些更改並僅對其中的一部分進行了更改(例如,添加了一些文件但不添加其他文件),那麼在發生提前(我想要執行的操作)之前,您不會對提交進行測試,你會測試你工作目錄中的任何東西。 – pwaller

+0

你正在提交的補丁在標準輸入提供給pre-commit鉤子。如果不是正在提交的補丁程序,你在測試什麼?預提交鉤的目的是驗證補丁。 –

回答

2

如果克隆整個回購過於昂貴,也許您只需要一份工作目錄的副本。製作副本比嘗試處理衝突更簡單。例如:

#!/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中提交後,因此您可以在那裏運行測試。您應該以比此處更安全的方式獲取臨時目錄,但爲了使最終差異工作(或者更早地簡化腳本和CD),它必須是工作目錄的子節點。

-1

我終於找到了我正在尋找的解決方案。只有提交之前的索引狀態被檢查,並且它會像提交之前一樣離開索引和工作樹。

如果您發現任何問題或更好的方法,請回復,作爲評論或您自己的答案。

這假設沒有別的東西會在運行時嘗試存儲或修改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 ... 
+0

正如以下文章中的評論所述,如果修改提交,或者如果您沒有髒工作樹,這將無法正常工作。 HTTP:// codeinthehole。com/writing/tips-for-using-a-git-pre-commit-hook/ – pwaller

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

這基本上是威廉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。

+0

我已經投票給你了,但它不滿足「在測試中停留在$ PWD中」,這對於某些系統是必需的,如去圖書館。 – pwaller

+0

除了pwaller的擔憂之外,如果您正在執行'git commit -a',我相信此解決方案不起作用,因爲您的腳本假定我們僅提交了暫存文件。 – TanguyP

1

git write-treepre-commit掛鉤中很有用。它將樹寫入索引的回購(如果提交完成後此樹將被重用)。

樹一旦寫入回購,您可以使用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