2

次のようなロジックを実行するpre-receive フックを設定したかったのです。

  • マージコミットがあるかどうかを確認します
  • 新しいブランチがプッシュされたかどうかを確認します
  • 複数のコミットがあるかどうかを確認します。

上記のいずれかが存在する場合は、ユーザーとやり取りして、変更をプッシュするかどうかを確認し、そうであれば変更をプッシュし、そうでない場合はドロップします。

gitの事前受信フックで上記を達成する可能性はありますか?

4

1 に答える 1

3

ユーザーが「ssh」を実行している他のマシンにいる可能性があり、標準入力がユーザーに送信されないため、少なくとも一般的には、 pre-receive フックでユーザーとやり取りすることはできません。(uid やユーザー名などを調べて、ユーザーを "コールバック" するスクリプトにエスケープすることで、何かを作り上げることができるかもしれません。その方向での実験はあなたに任せます。) これらすべてのチェックを行うことができます。 、 けれど。

pre-receive フックは (stdin で) 一連の行を取得します。

<old-value> SP <new-value> SP <ref-name> LF

(から引用githooks(5))。ref-name の形式が次の場合:

refs/heads/<branchname>

次に、指定されたブランチが作成、削除、または更新されています。<old-value>400秒の場合、ブランチが作成されています。<new-value>400秒の場合は削除されています。それ以外の場合は更新されています。(refs/heads/* 以外のものもあります。完全なリストについては、git-send-email フックを参照してください。)

ブランチが更新されている場合、 は<old-value>それが指していたコミット ID であり、 は更新が許可されている場合に指し示すコミット ID です (これは pre-receive フック<new-value>update フックの両方に依存します)。

これは、2 番目と 3 番目のケース (および削除) を単純に検出するフックです。マージがあるかどうかを調べるには、各リビジョンを$between調べて、マージ (つまり、複数の親がある) があるかどうかを確認する必要があります。コミットを停止するには、0 を返す代わりにゼロ以外で終了します。

#! /bin/sh

check()
{
    local old=$1 new=$2 longref=$3
    local between rev

    if expr $old : '^00*$' >/dev/null; then
        echo creating new branch ${longref#refs/heads/}
        return 0
    fi
    if expr $new : '^00*$' >/dev/null; then
        echo removing branch ${longref#refs/heads/}
        return 0
    fi
    between=$(git rev-list $old..$new)
    case "$between" in
    *$'\n'*)
        echo at least two revs
        for rev in $between; do git log -1 --oneline $rev; done
        return 0
    esac
    echo only one rev
    return 0
}

while read old new longref; do
    case $longref in
    refs/heads/*) check $old $new $longref;;
    esac
done

$between(拒否する場合でも、リモートリポジトリに既に送信されている ため、すべてのリビジョンを調べることができます)。


更新: ssh トランスポートを使用している場合でも、これを機能させる方法を見つけました。これにより、余分なデータを密輸することはできません。

関数を変更 (および名前を変更) しましたcheck。新しい名前である の下では、get_confirmationデフォルトでプッシュを許可したくないケースを把握 (および 0 を返す) することを意図しています (許可する場合は 1 を返します)。

次に、メインループでこれを行うことができます:

case $longref in
refs/heads/*)
    if get_confirmation $old $new $longref; then
        case $PWD in
        *.allow.git)
            echo 'allowed via alternate path'
            ;;
        *.git)
            echo "denied ... push to ${PWD%.git}.allow.git to allow"
            exit 1
            ;;
        *)
            echo "denied, don't know where I am"
            exit 1
            ;;
        esac
    fi
    ;;
# add more cases here if desired
esac

--bareこれは、 に存在するクローンにプッシュすることを前提としています/some/path/to/repo.git

プッシュを許可する同等のリポジトリを作成するには、名前が で終わる実際の並列ディレクトリを作成する必要がありますrepo.allow.gitHEADこのディレクトリには、プレーン リポジトリからコピーされた1 つの実際のファイルが含まれている必要があります。ディレクトリ内の他のすべてのものは、へのシンボリックリンクにすることができます../repo.git/<same>

cd /some/path/to
mkdir repo.allow.git
cd repo.allow.git
ln -s ../repo.git/* .
rm HEAD; cp ../repo.git/HEAD .

HEAD通常のファイルでなければならない理由は、そうでなければgit pushこれを有効なレポとして扱わないからです (それがブランチへのリンクである場合にのみシンボリックリンクにすることが許可され、それがシンボリック参照を行う「古い方法」です) .

通常git pushは失敗し、代わりgit push remotehost:/some/path/to/repo.allow.gitにそれを通過させることができます。もちろん、あなたのユーザーは、このハッキングの目的全体を回避するために、常にそれを行う習慣を身につけるかもしれませんが、そうではないかもしれません。

もちろん、これはすべて、リモート リポジトリが Unix ライクなホストである (つまり、シンボリック リンクをサポートしている) ことを前提としています。

于 2012-05-01T04:09:25.760 に答える