11

updateサブモジュールがサブモジュールのアップストリーム リポジトリに存在しないコミット ID に更新されている場合にバウンスする gitのフックを作成しようとしています。別の言い方をすれば、サブモジュール ポインターに変更をプッシュする前に、サブモジュール リポジトリに変更をプッシュするようユーザーに強制したいと考えています。

1 つの警告:

  • 親リポジトリと同じサーバーに存在する裸のアップストリームリポジトリを持つサブモジュールのみをテストしたい。そうしないと、git フック内から 'git clone' や 'git fetch' を呼び出すなどのクレイジーなことをしなければならなくなり、楽しくありません。

私はアイデアをいじっていますが、これを行うためのより良い方法があるに違いないと感じています. 更新フックで私が計画していたことは次のとおりです。

  1. フックに渡された refname をチェックして、 の下で何かを更新しているかどうかを確認しrefs/heads/ます。そうでない場合は、早めに終了してください。
  2. git rev-listプッシュされているリビジョンのリストを取得するために使用します。
  3. 各リビジョンについて:
    1. git show <revision_id>サブモジュールが更新されたかどうかを調べる正規表現を呼び出して使用します (「+Subproject commit [0-9a-f]+」を検索して)。
    2. このコミットがサブモジュールを変更した場合は、.gitmodulesその特定のコミットで見られるファイルの内容を取得します ( git show <revision_id>:.gitmodules)。
    3. 3.1 と 3.2 の結果を使用して、サブモジュールの URL とそれらの更新されたコミット ID のリストを取得します。
    4. 3.3 で作成されたこのリストを、サブモジュールの URL をファイルシステム上のローカルの裸の git リポジトリにマップする外部ファイルと照合して確認してください。
    5. cd3.4 で見つかったパスに移動し、実行git rev-parse --quiet --verify <updated_submodule_commit_id>してそのコミットがそのリポジトリに存在するかどうかを確認します。そうでない場合は、ゼロ以外のステータスで終了します。

git rev-parse --quiet --verify <revision_id>:.gitmodules(注: 3.2 の結果は、リビジョン間で出力が変わらない限り、リビジョン間でキャッシュできる可能性があると思います。ソリューションを単純化するために、この部分は省略しました。)

ええ、これはかなり複雑に思えます。私の生活をずっと楽にしてくれる内部の git コマンドがあるのではないかと思わずにはいられません。それとも、問題について考える別の方法があるのでしょうか?

4

2 に答える 2

3

これがgitupdateフックでの私の小さな試みです。他の人に役立つように、ここに文書化します。既知の注意点は、「0000...」の特殊なケースは処理されないということです。

#!/bin/bash

REF=$1
OLD=$2
NEW=$3

# This update hook is based on the following information:
# http://stackoverflow.com/questions/3418674/bash-shell-script-function-to-verify-git-tag-or-commit-exists-and-has-been-pushe

# Get a list of submodules
git config --file <(git show $NEW:.gitmodules) --get-regexp 'submodule..*.path' | while read key path
do
    url=$(git config --file <(git show $NEW:.gitmodules) --get "${key/.path/.url}")
    git diff "$OLD..$NEW" -- "$path" | grep -e '^+Subproject commit ' |
    cut -f3 -d ' ' | while read new_rev
    do
        LINES=$(GIT_DIR="$url" git branch --quiet --contains "$new_rev" 2>/dev/null | wc -l)
        if [ $LINES == 0 ]
        then
            echo "Commit $new_rev not found in submodule $path ($url)" >&2
            echo "Please push that submodule first" >&2
            exit 1
        fi
    done || exit 1
done || exit 1

exit 0
于 2012-07-02T14:43:55.207 に答える
3

後で編集: Git 1.7.7 の時点で、サブモジュールのコミットがリモートにプッシュされていない場合、親プロジェクトのプッシュを拒否するオプションgit-pushが追加されました。--recurse-submodules=check対応する構成push.recurseSubmodulesパラメーターがまだ追加されていないようです。もちろん、これで問題が完全に解決されるわけではありません - 無知なユーザーがチェックなしでプッシュする可能性があります - しかし、これは非常に重要です!

個々のコミットを調べるのではなく、プッシュされたすべてのコミットの差分を確認するのが最善の方法だと思います: git diff <old> <new>. ただし、実際には差分全体を見たくはありません。それは巨大かもしれません。.gitmodules残念ながら、git-submodule porcelain コマンドはベア リポジトリでは機能しませんが、パス (およびおそらく URL) のリストを取得するためにすばやく調べることができるはずです。それぞれについてgit diff <old> <new> -- path、差分がある場合は、新しいサブモジュールのコミットを取得できます。(そして、000000 の古いコミットの可能性が心配な場合はgit show、新しいコミットで使用できると思います。)

すべての処理が完了すると、問題は特定のコミットが特定のリモート リポジトリに存在するかどうかを確認することになります。残念ながら、お気付きのようですが、少なくとも私の知る限り、それは簡単ではありません。ローカルの最新のクローンを維持することがおそらく最善の策であり、それは得意なようです。

ちなみに、更新フックは ref ごとに 1 回なので、キャッシュはここでは関係ないと思います。はい、標準入力のすべての参照を取得する pre-receive フックでこれを行うことができますが、なぜもっと多くの作業を行う必要があるのか​​ わかりません。高価な操作になることはありません。更新フックを使用すると、プッシュされているさまざまなブランチを個別に受け入れるか拒否することができます。

トラブルを回避したい場合は、おそらく gitmodules ファイルの解析を避け、リストをフックにハードコードすることをお勧めします。サブモジュールのリストが頻繁に変更されるとは思えないので、自動化されたものを作成するよりも、それを維持する方がおそらく安価です。

于 2011-01-21T23:12:25.467 に答える