ぱろっと・すたじお

技術メモなどをまったりと / my site : http://parrot-studio.com/

Gitを使ったOSS的開発フロー

Subversionを使ってる人は山ほどいると思いますが、
最近のOSS開発で使われているのが、
「分散リポジトリ」のGitやMercurialです


Ruby界隈だとGitHubあたりがベースになることが多いので、
GitHubのアカウントも作って、なんとなくGitだな・・・と思ってはいたのですが、
先日本屋でこの本を軽く読んで、「これはGitしかない・・・」と


入門Git

入門Git


たぶん、Git最大のポイントは、基礎設計こそLinus氏が行っているものの、
開発のトップが日本人であるってことです
その人が書いた本ですから、Gitに関してこれ以上の本はありません*1 *2


最大のポイントは、Gitの使い方について書かれているのではなく、
(OSSの)開発フローの様々なポイントで、
Gitをどう扱えばいいかが、丁寧に書かれていることです*3


というわけで相変わらず前置きが長くなりましたが、
私がRDGCの開発を行う際、どんな開発フローでGitを使っているか、を書いてみます*4

前提条件

  • Publicな公開リポジトリで開発を進めている
  • なので、あまり公開リポジトリは汚したくない
    • 動作するコードであることはもちろん、試験的なコードも入れたくない
  • 一方で、個人レベルのコード履歴がないと不安

RDGC 0.1 でのやりかた

  • 自宅サーバSubversion環境を置く
    • 当然非公開
    • 自宅のRedmineと連携
  • 普段はそこと連携する環境Aで開発する
  • SF.JPのリポジトリと連動している環境Bを別に用意
  • 公開レベルに達したところで、環境AのコードをBにexportする
    • 単純な更新・追加のみならこれでいい
    • しかし、rename/deleteには手動で対処が必要
      • 手動ということは、ミスがある
  • 環境BからSF.JPにcommit
    • SF.JPのsvnはせいぜい8回程度のcommitだが、自宅ではその10倍以上
  • 普段は環境Aで開発しているが、持出し用の環境Cが存在
    • SSH+リモートで環境Aで開発するようにはしている
    • しかし、新幹線内など、回線はないけど時間はあるときにも進めたい
  • 環境Aでの書きかけのコードをCに移したい場合がある
  • 仕方ないので、いったんAのコードをcommitし、Cでupdateする
    • 家に帰ったらこの逆

RDGC 0.2 でのやり方

環境
  • リモート:master => SF.JPのリポジトリ/結果だけほしい・汚したくない
  • ローカルA:master => remort:masterとの同期にのみ使用/完成したコードのみ
  • ローカルA:[work branch] => 思いついた要素ごとにbranchを切る/好きにしていい
  • ローカルB:[work branch] => Aの同名branchと同期

先ほどの本でも勧めていますが、
Gitはbranchを切るコストが安いというか、
ローカルbranchを切って開発するのが標準です


Subversionだと、branchを切るだけでリビジョンが進んでしまうので、
branchを切るという行為自体がリポジトリを汚してしまいます
(ちょっと何か試したいだけなのにね・・・(´・ω・`))

開発環境構築
git clone [remote] => リモートからコピー
git checkout -b [work branch] => 作業用branchを切ってそっちで作業

Subversionでいうところのcheckoutです
Gitでのcheckoutは「branchを移動する」の意味です
「-b」をつけると、その名前のbranchを切って移動します


Gitでは同期用のmaster以外で作業をし、
最後にmasterにmergeするフローが基本なので、
常にどこのbranchにいるか意識するのは大事です*5

普段の作業


master以外のbranchであれば、好きにcommit/mergeを繰り返すことができます


先ほどの本では「やりたい仕事ごとにbranch」*6というのを勧めてますが、
RDGCの場合、現状で開発者は一人なので、branch一つでいろいろやってます
たぶん、開発者が増えたら変える必要があるかも

マージ
git checkout master => ローカル:masterに移動
git pull => リモート:masterの変更点を引っ張ってくる
git checkout [work branch] => 一度作業場所に戻る
git merge master => リモートからpullしたmasterの内容をmerge
(edit..commit) => 競合したら修正する
git checkout master => 再度、ローカル:masterに移動
git merge --squash --no-commit [work branch]
 => 作業の最終結果だけmerge
 => すでにworkにmasterの内容が含まれるから、失敗しないはず
git commit => 最終結果をローカル:masterにcommit
git push => リモート:masterを進める

ポイントがいくつかあります

  • masterにmergeする前にpullする
  • work branch側にmasterをmergeする
    • Gitは「祖先」のcommitしかmergeできない
    • 言い換えると、merge先の内容を全部含んでないとmergeできない
    • pushも同様

もう一つ、「公開リポジトリを汚さない」という条件を満たすのに必要なのが、
「git merge --squash --no-commit」です


通常、merge先のcommitを全て自分のツリーに結合してしまいます
ということは、試験的なゴミcommitまでmasterに結合されるので、
そのままpushすると、リモートのmasterにゴミができてしまいます


そこで「--squash」をつけると、
branchの「結果」だけをmasterにmergeできます
つまり、あたかも最初から迷わず修正を行っていたかのように見えます


Gitのリポジトリツリーを見るツール「gitk」(標準でついてます)で、
ここまでのフローを見るとこんな感じになるはずです


<work側>

※実際には「次の仕事」での処理を終えた後の画像


<master側>

work branchでいろいろな作業をしたにもかかわらず、
masterにはそのcommitが表れていません

次の仕事
git checkout [work branch or work branch2]
 => 君は同じbranchに戻ってもいいし、新しいbranchを切ってもいい
git merge master => masterの変更内容をマージ(同じbranchに戻った場合)
edit..commit..edit..commit.. => 以下繰り返し
他の環境と同期する


GitはSubversionと違い、
管理情報は環境のトップにある「.git」のみしかありません
(各ディレクトリに「.svn」みたいなのは作られません)


そのため、環境を共有ディレクトリにし、
そこから「git clone [work branch]」とすれば、
それだけで環境をコピーできます


それでも、作業途中のコードをcommitしなければならないのは同じですが、
作業中のコードだけ他のbranchを切ってcommitし、
別環境は動作するものだけcloneすれば多少緩和されます*7

Gitを扱う時のポイント


最後に、ここまでで取り上げなかった細かいポイントを

  • commitやmergeに失敗したらあせらず「git reset --hard HEAD^」
    • 直前のcommitを「なかったこと」にする
    • pushする前なら間に合うので、納得のいく状態になるまで試行錯誤可能
  • 常にGUIでツリーを確認するとよい
    • 先ほどのgitkでもいいし、TortoiseGitの「Git Show log」でも
  • commitは細かい単位で行う
  • 複数の内容を一度にcommitしない
    • Gitは特定のcommitを削除したり、履歴を書き換えることができる
    • この時、一つのcommitにいろいろ入っていると、手動の処理が発生してしまう



思ったよりだいぶ細かいことを書いてしまいましたが、
たぶん、実際に手を動かしてもらうと、
実はたいしたことではないってのがわかると思います


とはいえ、Gitの環境を持たない人の方が圧倒的に多いため、
RDGCではGitを使いつつも、Subversionにもcommitする方向で考えてます
(そこのmergeが手動になりますが、利用者の利便性を考えると・・・)


まあ、自分の開発環境に応じて、
SubversionとGit(やそれ以外の分散リポジトリ)を、
選択して使っていただければ(`・ω・´) b

*1:ただ、わりと抜けはありましたが 唐突に妙な単語が出てきて、索引にもなかったりとか

*2:トップが日本人でも、開発者に日本人が少ないためか、他に比べて日本語対応が甘いのはあります あと、出だしがLinuxのコード管理なので、Windowsとの相性もいまいち それでもWindowsで十分使えるレベルには達してますし、実際使ってます

*3:Gitには大量のコマンドが用意されていますが、実際に使うのはその一部だけで、そのコマンドを実装するためのサブコマンドが多いだけです [http://sourceforge.jp/magazine/09/03/16/0831212:title=SourceForge.JPのリファレンス]あたりがおすすめ

*4:社内でも使ってますが、所詮現在はプロトタイプの開発なので、わりとルーズな使い方なので参考にならない

*5:git branch コマンドを使いますが、EclipseのEGitや、TortoiseGitは作業branchをビジュアルに確認できるので楽です

*6:「トピックブランチ」という

*7:いったんコードを退避させる「git stash/stash pop」だとbranchを切る必要すらない