7

ステージングされたファイルを変更するpre-commitフックの場合、フックの実行後に何をステージングする必要があるかを理解する必要があります。

pre-commitフックは、コミットする必要のあるファイルにきれいな印刷を適用します。フックは次のタスクを実行します。

  • タブをスペースに置き換えます
  • 行末の末尾の空白を削除します
  • 二重の空行を削除します*)
  • 欠落している場合は、ファイルの最後に空の行を追加します*)

*)でマークされたアクションは、以下に説明する問題を引き起こしているアクションです。

これらが行われた後、フックはを使用して変更されたファイルをインデックスに追加しますgit add $filename。そうすることで、ファイル全体がステージングされ、変更されたファイルのごく一部(つまりハンク)のみをコミットできなくなります。

もちろん、git add --no-verifyフックをバイパスすることはできますが、を使用する場合はそのオプションを使用できませんgit gui。また、ファイルのステージングされた行にきれいな印刷を適用したいので、フックをバイパスすることは私の目的ではありません。

ファイル全体をステージングする代わりに、フックの実行後に適切なコンテンツをステージングできるように、きれいな印刷が適用された後にインデックスに何を追加する必要があるかを見つける方法はありますか?


編集1:David Brigadaによって提供され
答えは有望に見えますが、機能しません:ステージングされた変更をそのまま残します(これは良い部分です)が、(少なくともmsysgitでは)すべての変更を(ステージングされていないものだけでなく)隠し場所に入れますもの)。これにより、ステージングされた行が変更される可能性があるため、スタッシュをWCに戻すときにマージの競合が発生します。git stash --keep-index


編集2: gitがスタッシュをダーティなWCにマージすることを拒否しているため、 David
の更新された回答は成功しません。


編集3:larsksから
答えは、の使用法を示しています。一見すると、これは正しいように見えますが、チェックインされたバージョンがフィルターを介して実行され、WCがチェックインされたバージョンとは異なることは非常に混乱しています。少なくとも、それは私の経験であり、GitBookの次のコメントによって裏付けられています。.gitattributes

これらの変更をコミットしてファイルを再度チェックアウトすると、キーワードが適切に置き換えられていることがわかります

ファイルを削除してからもう一度チェックアウトして、フィルターによって適用された変更を確認する必要がありますか?とんでもない!これ以上のヒントはありますか?

4

5 に答える 5

2

コミット用にステージングしていない変更を pre-commit スクリプトの stash に配置し、完了したらそれらをポップすることができます。この--keep-indexオプションは、インデックスに追加していない (git add on を実行していない) 変更のみを隠し、--quietオプションは、隠し場所を作成および破棄しているという通知を抑制します。

あなたが説明した方法は、標準的な「gitの方法」ではありません。私が見たほとんどのプレコミットスクリプトは、間違ったスタイルをチェックするだけで、何かが見つかった場合はコミットからエラーになります。そうすれば、コミット前のスクリプトで、何らかの理由 (テスト データなど) でタブが含まれているはずのファイルを誤って書き換えることがなくなります。エラーがそこにあるはずだったと確信しています。

ここに簡単なプリコミット スクリプトがあります。ほとんどの場合、git の diff-index --check の基本的なテストを実行するだけですが、git stash を使用して、必要な変更のみを処理します。

#!/bin/sh
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

git stash save --keep-index --quiet
git diff-index --check --cached $against --
RETVAL=$?
git stash pop --quiet
exit $RETVAL

アップデート:

コミットするコンテンツを変更するスクリプトでこの戦略を使用しようとすると、マージの競合が発生します。pre-commit スクリプトから stash pop を取り出し、post-commit スクリプトで明示的なマージを実行する必要があります。

#!/bin/sh

git stash save --keep-index --quiet
# Add your script to prettify the code here ...
git add .

コミット後のスクリプトは次のようになります

#!/bin/sh

git merge stash@{0} -s recursive -Xtheirs
git stash drop --quiet stash@{0}

パラメータとのrecursiveマージではtheirs、可能な場合はスクリプトが行った変更を保持する必要があります。

于 2011-12-15T01:48:35.527 に答える
2

この種の作業を実行するのに pre-commit フックが適切な場所であるかどうかはわかりません。.gitattributesGit には、 ;を介してコミット/チェックアウト フィルターをドキュメントに適用できるフィルター メカニズムがあります。Pro Git Bookには、このフィルタリング メカニズムを使用してindentプログラムを C ソース ファイルに自動的に適用する例が含まれています。

于 2011-12-15T02:26:06.383 に答える
0

必要なのは、説明したアクションを実行する pre-commit フックだけでなく commit-msg フックも用意することです。

これは私が問題を解決する方法です:

  1. 次の pre-commit フックを追加します。

    #!/bin/sh
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 YOUR_SMUDGE_SCRIPT
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    
  2. 次の commit-msg フックを追加します。

    #!/bin/sh
    awk '!/^[[:space:]]*(#|$)/{exit f++}END{exit !f}' "$1" && exit
    # NOTREACHED unless commit was aborted
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 YOUR_CLEAN_SCRIPT
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    

YOUR_CLEAN_SCRIPT と YOUR_SMUDGE_SCRIPT を、実際のクリーン/スマッジ ルーチンを実行する実行したいコマンド名に置き換えるように注意してください。スマッジ ルーチンはキーワードを展開し (コミット前に発生)、クリーン ルーチンは変更を元に戻す必要があります (コミットが中止されたときに発生)。これらのスクリプトには、変更が必要なファイルが渡されます (以前は別の編集用にステージングされていました)。

于 2015-10-22T01:13:53.407 に答える
0

私の目標は達成できないように見えるので、別の解決策を手伝ってくれました:

にいるときは、ファイルの一部のみをステージングしますgit gui。幸いなことに、git guiではカスタム コマンドをToolsメニューに追加できます。または のいずれかhooks.shの 1 つのパラメーターを受け取る、というスクリプトをセットアップしました。このスクリプトは、一度は で、一度はパラメータとして、メニューに追加されます。enabledisableToolsenabledisable

そうすれば、ファイルの一部だけをステージングするときにフックを無効にし、コミットしてからフックを再度有効にすることができました。

どうぞ:

#!/bin/sh

################################################################################
# hooks.sh                                                                     #
# enable or disable git hooks.                                                 #
################################################################################

THEHOOK=pre-commit                  # which hook to work with
ENABLEDFILE=./.git/hooks/$THEHOOK   # original name of the file
DISABLEDFILE=$ENABLEDFILE.disabled  # disabled name of the file
OLDFILE=''                          # for mv command: old name
NEWFILE=''                          # for mv command: new name

# parse parameters
case $1 in
  disable)
    OLDFILE=$ENABLEDFILE
    NEWFILE=$DISABLEDFILE
    ;;
  enable)
    OLDFILE=$DISABLEDFILE
    NEWFILE=$ENABLEDFILE
    ;;
  *)
    echo -e "operation:\n    $0 enable  to enable the hook\n    $0 disable to disable the hook"
    exit 1
    ;;
esac

if [ -e $OLDFILE ]
then
  mv $OLDFILE $NEWFILE
else
  echo "nothing to do"
fi
于 2012-07-06T08:33:09.660 に答える
0

目標を 100% 理解しているとは言えませんが、おそらく次のアプローチを試すことができます。変更をディスクに書き込む代わりに、フックにパッチを生成させます。(たとえば、difflibモジュールを使用して Python でこれを行うか、一時ファイルを書き込んで にシェルを実行できますdiff。) 次に、git apply(--cached必要に応じて およびその他のフラグを使用して) パッチをインデックスと作業ツリーに適用します (フックによって手動または自動で)。

于 2011-12-23T20:38:35.293 に答える