あなたはよりおしゃれになる方法を知りたがっていました。受信後メール フック スクリプトを調べて、それがどのように機能するか (一部) を示します。これはかなり長くなります!:-)
OK、それで、ここにpost-receieve-email
(少し再フォーマットされた)からの重要なビットがあります:
while read oldrev newrev refname; do
prep_for_email $oldrev $newrev $refname || continue
generate_email $maxlines | send_mail
done
これにより、入力ストリームから古い SHA、新しい SHA、および ref 名が読み取られます (これは git の失敗の 1 つと考えられます。このストリームは 1 回しか読み取ることができず、その後は消えてしまいます。これにより、関連性のない多数のフックが過剰に接続されます。難しい)、2 つのシェル関数を呼び出してそれらを調べてから、それぞれを無視するか、何らかの処理を行います。
ここで、prep_for_email
シェル関数の重要な部分とコメントを示します。
prep_for_email()
{
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
ここでの rev-parse (および、コマンドライン引数を許可する上で削除したコード) を使用すると、"HEAD" や "HEAD^" などの rev-name をフィードできます。実際の post-receive フックは常に生の SHA1 を取得するため、rev-parse 呼び出しはノーオペレーションです。
# --- Interpret
# 0000->1234 (create)
# 1234->2345 (update)
# 2345->0000 (delete)
if expr "$oldrev" : '0*$' >/dev/null
then
change_type="create"
else
if expr "$newrev" : '0*$' >/dev/null
then
change_type="delete"
else
change_type="update"
fi
fi
post-receive フックで、「古い」SHA1 が0000000000000000000000000000000000000000
(すべてゼロ、つまり 40 個の 0;expr
上記はすべてゼロをチェックするだけ) の場合、これは「ref」引数が以前には存在しなかったことを意味し、現在は存在します。これは通常、ブランチ作成操作ですが、タグ作成にすることもできます。一方、「新しい」SHA1 がすべてゼロの場合、以前は「ref」引数が存在していましたが、現在は存在しません。通常はブランチ削除操作です。それ以外は、ref-nameが old-rev に解決されるために使用され、現在は new-rev に解決されます。これは通常、ブランチの更新ですが、たとえばタグの移動の場合もあります。
次に、電子メール フックには、git リポジトリ内の実際の基になるオブジェクト タイプに対して参照名をクロスチェックする、優れた「パラノイア スタイル」のプログラミングがあります。
# --- Get the revision types
newrev_type=$(git cat-file -t $newrev 2> /dev/null)
oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
case "$change_type" in
create|update)
rev="$newrev"
rev_type="$newrev_type"
;;
delete)
rev="$oldrev"
rev_type="$oldrev_type"
;;
esac
ref-name にフォームがrefs/tags/*
あり、更新が注釈付きタグに対するものである場合、これは と の両方$oldrev_type
を$newrev_type
に設定する必要がありtag
ます。(軽量タグの場合、代わりに両方になりcommit
ます。軽量タグが注釈付きタグに変わった場合、古いタイプはコミットになり、新しいタイプはタグになります。) そしてもちろん、削除する場合ブランチまたはタグの場合、「新しい」リビジョンはすべて 0 になり、単に失敗する$newrev_type
ため空の文字列になります(これが.git cat-file -t
2> /dev/null
(余談ですが、引数を引用する正当な理由git cat-file -t
はありませんし、引用しない人もいます。おそらく、通常の空の文字列の引数の問題に遭遇した後、引用に過度に満足している誰かが、1つを逃しただけです。幸い、この場合はどちらの方法でも無害です. :-) )
電子メール スクリプトには、非常に長いcase
ステートメントがあります。
case "$refname","$rev_type" in
...
esac
これにより、操作refs/heads/*
が常にcommit
. そうでない場合は、stderr にメッセージを出力します (このメッセージgit push
は、プッシュを行った人に送信され、接頭辞として が付けられremote:
、誰かがそれを見ることができます):
refs/heads/*,commit)
# branch
refname_type="branch"
short_refname=${refname##refs/heads/}
;;
...
*)
# Anything else (is there anything else?)
echo >&2 "*** Unknown type of update to $refname ($rev_type)"
echo >&2 "*** - no email generated"
return 1
;;
からの便利なビットですgenerate_email
。詳細については実際のスクリプトを参照してください。ただし、この場合、 への呼び出しとgenerate_email_header
、 を呼び出すケースのみが重要generate_update_branch_email
です。
ヘッダーは次のとおりです。
generate_email_header()
{
# --- Email (all stdout will be the email)
# Generate header
cat <<-EOF
To: $recipients
Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
X-Git-Newrev: $newrev
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "$projectdesc".
The $refname_type, $short_refname has been ${change_type}d
EOF
}
$change_type
、私たちが気にする場合、update
これは次のようなことを言います: branch zorg updated.
($describe
は からの出力git describe $newrev
、またはそれが空の場合、つまり、git describe
使用する注釈付きタグがなかった場合、 $newrev
. $recipients
、$emailprefix
、および$projectdesc
はさまざまな構成可能変数からのものです。)
では、どのコミットが削除および追加されたかをgenerate_update_branch_email
正確に計算して電子メール用に印刷する必要があるものがたくさんあります。そして、それは次で終わります:
echo "Summary of changes:"
git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
基本的に、 (すべて 0 ではない既存のものからすべて 0 ではない新しいものへ) され$refname
た形式refs/heads/*
(たとえば、refs/heads/zorg
)のブランチ参照は、その (長い形式の) ブランチ名を意味します。通常は( ) として参照されますが、その分岐先を移動しました。これは、その動きが何を意味するかを正確に理解するためのコードよりもはるかに簡単です!update
$oldrev
$newrev
$short_refname
zorg
あなたの場合、ブランチの作成について心配する必要はありません。通常の更新のように扱うことができます。ブランチの削除で何か特別なことをしたい (またはしたくない) かもしれません。ほとんどの場合、ブランチの更新だけに関心があります。必要なのは、staging
更新されている場合は、特別なステージング コピーを更新することだけです。が更新されている場合は、特別なコピーmaster
を更新します。master
いずれの場合も、更新は「新しいブランチのヒントへ」であり、非常に簡単にアクセスできます。
したがって、これらすべてをまとめると、コマンド ラインから呼び出す機能を追加した次の (テストされていませんが、かなり些細な) フックが得られます。より便利にするために、コマンドラインから呼び出されたときに、チェックアウトしたいリビジョンをどこにでも指定でき./hookscript unused master~2 refs/heads/staging
ますmaster~2
。 )。
#! /bin/bash
#
# handle updates to our two interesting branches, staging and master.
# function to dump given commit state to target directory
# arguments: $1 - rev; $2 - target dir
copy_to_dir() {
GIT_WORK_TREE="$2" git checkout -q -f "$1"
}
# function to handle an update to staging branch.
# arguments: $1 - rev to check out
update_staging() {
copy_to_dir $1 /path/to/staging/site/webroot
}
# function to handle an update to master branch.
update_master() {
copy_to_dir $1 /var/www
}
# function to handle one reference-change.
# arguments:
# $1 - old revision, or all-0s on create
# $2 - new revision, or all-0s on removal
# $3 - reference (refs/heads/*, refs/tags/*, etc)
refchange() {
local oldrev="$1" newrev="$2" ref="$3"
local deleted=false
local short_revname
if expr "$newrev" : '0*$' >/dev/null; then
deleted=true
elif ! git rev-parse "$newrev"; then
return # git rev-parse already printed an error
fi
case $ref in
refs/heads/staging|refs/heads/master)
shortref=${refname#refs/heads/};;
*)
return;;
esac
# someone pushed a change to staging branch or master branch
if $deleted; then {
echo "WARNING: you've deleted branch $shortref"
echo "are you sure you wanted to do that?"
echo "The operating copy is still operating, and"
echo "will be updated when the branch is re-created."
} 1>&2
return
fi
# update either the staging copy or the master copy
update_$shortref "$newrev"
}
# main driver: update from input stream (if no arguments) or use arguments
case $# in
3) refchange "$1" "$2" "$3";;
0) while read oldrev newrev refname; do
refchange $oldrev $newrev $refname
done;;
*) echo "ERROR: update hook called with $# arguments, expected 0 or 3" 1>&2;;
esac
私は解析済みの rev を使用しているため ( orgit checkout -q -f
のような名前ではなく)、プッシュ先のブランチが毎回「切り離された HEAD」状態になることに注意してください。さらに重要なことは、Web サーバー上の「通常の」レポからコマンドライン トリックを使用すると、現在のブランチの係留が解除されることです。これは致命的ではありませんが、簡単に迷惑になる可能性があります。これを避けるには、 の内容を.staging
master
copy_to_dir
git archive $1 | (rm -rf "$2" && mkdir "$2" && cd "$2" && tar xf -)
(通常、リポジトリにプッシュし、--bare
「現在のブランチ」の考え方を変更しても問題ありません。誰も気にしないからです。)