変態 git
git を変態的に使ったのでメモ。
どれもこれも、かなり特殊なシチュエーションなので参考になりません。多分。
SVN リポジトリを read-only で Git に取り込む
色々な都合により、コミットしてはいけない SVN リポジトリを git に取り込む必要があったので。(SVN サーバー側の hook 等は使えず)
普通に git-svn で取り込んだブランチを直接 git の中央リポジトリに push すると、他の人が clone して git svn dcommit とかできてしまうので、出来ないようにしたい。
そこで、git-svn で取り込んだ変更を、一度 Non-git-svn なブランチに merge し、そちらを push する運用にした。
レシピ
まず、空の git リポジトリを初期化。
$ mkdir hoge $ cd hoge $ git init $ touch .gitignore $ git add .gitignore $ git commit -m 'initial commit' $ git remote add origin REMOTE_GIT_REPOS $ git push -u origin master
SVN リポジトリを取り込む。git svn clone ではなく、git svn init + fetch で一旦リモートブランチだけ作るのがミソ。
$ git svn init -R svn-hoge --prefix "svn-hoge/" SVN_REPOS_URL $ git svn fetch -rHEAD svn-hoge # でかい SVN リポジトリなので -rHEAD で最新のリビジョンだけ取り込んでます
作ったリモートの SVN ブランチ (svn-hoge/git-svn) をローカルブランチとして作成
$ git checkout -b svn-hoge svn-hoge/git-svn
この時点で、Non-git-svn な master と、git-svn な svn-hoge ブランチが出来る。
$ git branch -a master * svn-hoge remotes/origin/master remotes/svn-hoge/git-svn
master と svn-hoge は全然関係ないブランチとなっているので、master では git svn rebase や dcommit は出来ない。
あとは、定期的に以下を行なって、master に SVN のブランチをマージ。
$ git checkout master $ git merge --no-ff svn-hoge $ git push origin master
git merge の際に --no-ff を付けておかないと、 master が git-svn 化してしまうので注意。
また、master を git 側で変更する場合、merge がコンフリクトする可能性があるけど、失敗した場合はアラート出して人間がマージします。
特定ワードをコミットメッセージに含むコミットだけ strategy を変えてマージ
色々な都合により、普通にマージすると特定のワードを含むコミットだけコンフリクトが起こるので、そいつらだけ --strategy=ours でマージしたかった。
結論から書くと、何回かにわけてマージする形で解決。
レシピ
git rev-list の --grep オプションを使うと、2つのブランチの差分のうち、特定のワードをコミットメッセージに含むコミットだけをリストアップできる。(git マジ変態。いい意味で)
# master 〜 hoge の差分の内、"特定のワード" を含むコミットをリストアップ (リビジョンのリストが返る) $ git rev-list --grep="特定のワード" master..hoge
あとは、これで得られるリビジョンそれぞれについて git merge $rev^ + git merge -s ours $rev をする。($rev^ = $rev の1つ前のリビジョン)
$ git checkout master $ for rev in `git rev-list --reverse --grep="特定のワード" master..hoge`; do git merge --no-ff "${rev}^" -m "early commit for merging hoge" git merge -s ours "${rev}" done $ git merge --no-ff hoge -m "merged hoge"
とっても気持ち悪いですね^^
過去のコミット履歴から特定のワードを含むコミットを消し去る
git filter-branch を使うと、過去の履歴を改ざんできるのは git の 変態的 強みですが、それを使って特定のワードをコミットメッセージに含むコミットを無かったことにします。(もはや動機の説明に疲れたので割愛)
レシピ
git filter-branch --commit-filter でコミットを選択して無かった事にできます。
引数に渡したスクリプトが実行され、skip_commit "$@" するか、 git commit-tree "$@" するかでコミットするかどうか選択できるんですが、コミットメッセージは標準入力に渡されてくるので、普通に cat や grep しちゃうと、フィルター後のコミットのメッセージが空になってしまいます。
そこで、 /dev/stdin から読んで判定します。
$ git filter-branch --commit-filter ' if [ `grep -c "特定ワード" /dev/stdin` -gt 0 ]; then skip_commit "$@"; else git commit-tree "$@"; fi' HEAD
これはそんなに変態じゃない、、、かな、、、?