ここでは、Linux カーネルの IA64 アーキテクチャのメンテナンスを担当している Tony Luck の git の利用方法を紹介します。
彼は2つの公開リポジトリを使用します:
彼は他にも一時的なブランチ("topic branches")を使用します。 それぞれのブランチはパッチの論理的なグループを含んでいます。
この設定をする為には、最初に Linus の公開ツリーを複製することで 作業ツリーを作成します:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
$ cd work
Linus のツリーは origin/master という名前のリモートブランチに格納され、 git-fetch(1) を使用して更新できます;他の公開ツリーも git-remote(1) を使用して "remote" を設定し git-fetch(1) で それらを最新に保ちます;Chapter 1, リポジトリとブランチ を参照してください。
さて、あなたが作業する為のブランチを作成しました; このブランチは origin/master ブランチの現在の先端から開始していて、 (git-branch(1) の —track オプションを使用して) デフォルトでは Linus からの変更をマージする設定にすべきです。
$ git branch --track test origin/master
$ git branch --track release origin/master
これらは git-pull(1) を用いて簡単に最新に保つことができます。
$ git checkout test && git pull
$ git checkout release && git pull
重要な注意点! これらのブランチにローカルな変更を加えると、 このマージは履歴内にコミットオブジェクトを生成します (ローカルで 変更を加えていない時は単に "Fast forward" でマージされます)。 多くの人は Linux の履歴にこれが作成されることを "ノイズ" として嫌います。 その為 "release" ブランチにこの気まぐれが行なわれるのを避けるべきです。 これらノイズとなるコミットはあなたが Linus にリリースブランチへの pull を依頼するときに恒久的な履歴の一部になってしまいます。
幾つかの設定変数(git-config(1) 参照)は 両方のブランチをあなたの公開ツリーに push するのを容易にしてくれます。 (the section called “公開リポジトリの設定” 参照。)
$ cat >> .git/config <<EOF
[remote "mytree"]
url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
push = release
push = test
EOF
上記により test と release の両方のツリーに git-push(1) を 使用して push することができるようになります:
$ git push mytree
あるいは、test と release のブランチの一方にだけ push する場合は:
$ git push mytree test
あるいは、
$ git push mytree release
さて、コミュニティからの幾つかのパッチを適用することを考えます。 このパッチ(あるいは関連するパッチグループ)を保管するブランチに 短く分かり易い名前を付けます。
$ git checkout -b speed-up-spinlocks origin
パッチを適用し、幾つかのテストを実行し、変更をコミットします。 パッチが複数のパートから構成される場合は、それぞれを分割した コミットとしてこのブランチに適用すべきです。
$ ... patch ... test ... commit [ ... patch ... test ... commit ]*
この変更状態が良好なら、"test" ブランチに pull し 公開する準備をします。
$ git checkout test && git pull . speed-up-spinlocks
ここではコンフリクトは発生しそうにありません…しかし この段階までの間にしばらくの時間がかかり、上流から新しいバージョンを pull しているかもしれません。
しばらく後に十分な時間が経ちテストが完了したときに、 同じブランチを "release" ツリーに pull し、上流に向かう準備をします。 これは、それぞれのパッチ(あるいは一連のパッチ)をパッチ用のブランチ に留めておくありがたみを理解する場面です。この作業は一連のパッチが "release" ツリーへ任意の順番で移動できることを意味します。 (訳注: 先を読めばわかりますが、ブランチを作って、パッチを適用すると、パッチ群 のテストをブランチごとに並行して行なうことができます。そして、テストが完了したパッチ のブランチから順に release ツリーへ移動することができます。そして、各パッチ用のブラ ンチの状況も簡単に把握できます。)
$ git checkout release && git pull . speed-up-spinlocks
その後、たくさんのブランチが作成され、それらブランチの名前を 適切に指定していたとしても、それらが何であるか又は何が含まれているかを 忘れてしまうかもしれません。特定のブランチでどんな変更が行なわれているかを 思い出すには、次のようにします:
$ git log linux..branchname | git shortlog
test または release ブランチに既にマージされたかどうかは 次のようにして確認します:
$ git log test..branchname
あるいは
$ git log release..branchname
(このブランチがまだマージされていない場合、いくつかのログエントリが表示されます。 既にマージされている場合は、何も出力されません。)
パッチがこの大きなサイクル(test から release に移動し、 Linus に pull され、最終的に自身の "origin/master" ブランチに返却する流れ) を完遂すると変更に対するブランチは不要になります。 このことは次の出力が空であることを確認することで検出できます:
$ git log origin..branchname
この時点でブランチは削除することができます:
$ git branch -d branchname
幾つかの変更は自明で、分割したブランチを作成し test と release ブランチに それぞれマージする必要がない場合もあります。 そういった変更は直接 "release" ブランチに適用し、 "test" ブランチにマージします。
Linus に送る "pull してください" のリクエストに含める diff 状態と 変更の短いサマリを作成するには、次のようにします:
$ git diff --stat origin..release
そして
$ git log -p origin..release | git shortlog
以下はこれら全てをさらに単純化するスクリプトです。
==== update script ====
# GIT ツリーのブランチを更新する。更新すべきブランチが
# origin の場合、kernel.org から pull する。そうでない時は
# origin/master ブランチを test|release ブランチにマージします。
case "$1" in
test|release)
git checkout $1 && git pull . origin
;;
origin)
before=$(git rev-parse refs/remotes/origin/master)
git fetch origin
after=$(git rev-parse refs/remotes/origin/master)
if [ $before != $after ]
then
git log $before..$after | git shortlog
fi
;;
*)
echo "Usage: $0 origin|test|release" 1>&2
exit 1
;;
esac
==== merge script ====
# ブランチを test または release ブランチにマージ
pname=$0
usage()
{
echo "Usage: $pname branch test|release" 1>&2
exit 1
}
git show-ref -q --verify -- refs/heads/"$1" || {
echo "Can't see branch <$1>" 1>&2
usage
}
case "$2" in
test|release)
if [ $(git log $2..$1 | wc -c) -eq 0 ]
then
echo $1 already merged into $2 1>&2
exit 1
fi
git checkout $2 && git pull . $1
;;
*)
usage
;;
esac
==== status script ====
# ia64 GIT ツリーの状態をレポートする
gb=$(tput setab 2)
rb=$(tput setab 1)
restore=$(tput setab 9)
if [ `git rev-list test..release | wc -c` -gt 0 ]
then
echo $rb Warning: commits in release that are not in test $restore
git log test..release
fi
for branch in `git show-ref --heads | sed 's|^.*/||'`
do
if [ $branch = test -o $branch = release ]
then
continue
fi
echo -n $gb ======= $branch ====== $restore " "
status=
for ref in test release origin/master
do
if [ `git rev-list $ref..$branch | wc -c` -gt 0 ]
then
status=$status${ref:0:1}
fi
done
case $status in
trl)
echo $rb Need to pull into test $restore
;;
rl)
echo "In test"
;;
l)
echo "Waiting for linus"
;;
"")
echo $rb All done $restore
;;
*)
echo $rb "<$status>" $restore
;;
esac
git log origin/master..$branch | git shortlog
done