GitレポジトリをRubyから操作するライブラリGritを試してみた

仕事でブラウザベースのファイルライブラリ的なものを作ろうかどうしようかって雰囲気なので、どうせなら過去の履歴もコメント付きで追えて、なおかつ過去の変更時点での状態のものをダウンロード出来たら便利じゃね?って思ったらそれってGitじゃんっておもったので調べてみた (ハァハァ

参考にしたのはこのサイト
Grit を使って Git リポジトリを Ruby で操作する

紹介されているのはGritとかいうRubyのライブラリ。なんじゃいそれはと思ってたら、かのgithubでも使ってるそうな。おお。信頼性高そう。

インストール
gemを検索してみたらあったあった(^◯^)

$ gem search grit -r

*** REMOTE GEMS ***

grit (2.4.1)

ではインストール

$ sudo gem install grit

Successfully installed grit-2.4.1
1 gem installed
Installing ri documentation for grit-2.4.1...
Installing RDoc documentation for grit-2.4.1...

使い方
さっそく使ってみる。
まずはレポジトリへのパスを指定してレポジトリオブジェクトを生成してみます。
レポジトリはgithubにも上げてるEralng練習用に作ってみてるmessage_boxを使用しました。

irbで試してみます。

repo = Grit::Repo.new("/Users/shin/src/message_box")
=> #<Grit::Repo "/Users/shin/src/message_box/.git">

お、オブジェクトが返ってきました。

コミットの履歴オブジェクトを得るには以下のようにしてみます。

master_commits = repo.commits("master", 5)
=> [#<Grit::Commit "1827a536f9c15da508f549e02523216295031aeb">, 
       #<Grit::Commit "3786536f91e1e3274eedd25af74b25a04ba935b4">, 
       #<Grit::Commit "81418a12ca86d408bcb5ede6d6bdada61b15becb">, 
       #<Grit::Commit "6472a758b2ae004bad9cb27a974ff88020a354f3">, 
       #<Grit::Commit "e203df7909c5e6f5a368a9122842aeabae3e2140">]

おお、masterブランチの直近5つの履歴オブジェクトが返ってきました。ちなみに引数を省略するとmasterブランチの直近10個の履歴が得られるようです、あと、三つ目の引数を数値で指定するとスキップ数を指定出来るようです。

上記でmaster_commitsは得られた履歴の配列なので、[]で最新から数えて何番目かの履歴を取得出来ます。

log = master_commits[1]

これで変数logにmasterの最新から数えて一つ古い履歴が入りました。
このオブジェクトから色々な情報が得られます。

log.author #作成者
=> #<Grit::Actor "HIROE Shin <hiroe.orz@gmail.com>">

log.authored_date #ファイルが作成された日時
=> 2011-06-25 14:12:38 +0900

log.committer #このコミットの登録者
=> #<Grit::Actor "HIROE Shin <hiroe.orz@gmail.com>">

log.committed_date #このコミットのコミット日時
=> 2011-06-25 14:12:38 +0900

log.message #このコミットのコミットメッセージ
=> "first release version"

ファイル一覧の取得
レポジトリ直下のファイルの一覧を取得してみます。

repo = Grit::Repo.new("/Users/shin/src/message_box")  #レポジトリのオブジェクトを生成
=> #<Grit::Repo "/Users/shin/src/message_box/.git">

repo.tree #コミットのツリーを取得
=> #<Grit::Tree "master">

repo.tree.contents #ツリー以下のデータの一覧を取得
=> [#<Grit::Blob "850fe190ff78fb3811f0556f4a552c77c442bd4d">, 
       #<Grit::Blob "b897a931a387a758e096a60e7c4b5667578360a5">, 
       #<Grit::Blob "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391">,
       #<Grit::Tree "3040368964b92470cf8d54e2e2208cd05f6cc9ed">, 
       #<Grit::Blob "c0a10bd13de19cc8cd9d9e7ae96e0c8439e8bb44">, 
       #<Grit::Blob "d451f033967d4b04ab2789cc1ffcd4e75becbb8c">, 
       #<Grit::Tree "cb995891102d670d77d65716a217b5753309ce69">, 
       #<Grit::Tree "fe925416fda3dfb04f3f488d7b4d845d39f962ba">]

repo.tree.contents.collect{ |c| c.name }  #ファイル名の一覧を取得
=> [".gitignore", "Makefile", "README", "conf", "rebar", "rebar.config", "src", "test"]

特定のファイルの履歴を取得
ファイルブラウザのバックグラウンドとしてgitを使うならファイルごとの履歴が表示出来て、尚かつその時点でのファイルをダウンロード出来なければ画龍天星を欠きます。

history = repo.log("master", "src/user_db.erl", :max_count => 5, :skip => 0)
=> [#<Grit::Commit "1827a536f9c15da508f549e02523216295031aeb">, 
       #<Grit::Commit "0861094fafd6a06c98b3e6006a08c60178aafabe">, 
       #<Grit::Commit "3fadf13dfeeae522c4fd3455b4f1e32fc0a038a6">, 
       #<Grit::Commit "e16cdda2c106fe6b90f8f8cd4e861ad525992fe7">, 
       #<Grit::Commit "27ecdec17b6b0a9c943bc75f6f807e8761a043f4">]

history.collect{ |h| h.message } #上記の配列からコミットメッセージの配列を生成
=> ["add user_db_test", "fix system stop call", "home timeline getting ok", "add follow db", "add follow database"]

commit = history[0] #このファイルの最新のコミットオブジェクトを取得 
=> #<Grit::Commit "0861094fafd6a06c98b3e6006a08c60178aafabe">

commit.tree # コミットオブジェクトからツリーオブジェクトを取得
=> #<Grit::Tree "a6197b361300cd64020868e6481964254c3622de">

blob = commit.tree / "src/user_db.erl" #ツリーオブジェクトからファイルへアクセス"
=> #<Grit::Blob "e8be7d0c5e91064cf73c5676c05a409be43ecb07">

blob.data
=> "ファイルのデータ....."

ファイルの新規作成
ファイルを新規作成する場合は、gitの操作上で言えば、【ファイルを保存】→【git add】→【git commit】ですね。
これをGritから行います。ちなみに新しいファイルを作るので、あたらしくテスト用のgitレポジトリを用意しました。

require "grit"
=> true
repo = Grit::Repo.new("/Users/shin/src/test_repo") #レポジトリオブジェクトを生成
=> #<Grit::Repo "/Users/shin/src/test_repo/.git">

# ファイルを保存
File.open("hello.txt", "w") do |f|
  f.write("this is test file")
end

# 今保存したファイルの新しいBlobオブジェクトを生成する。
new_blob = Grit::Blob.create(repo, {:name => "hello.txt", :data => File.read("hello.txt")})
=> #<Grit::Blob "">

# git add
Dir.chdir(repo.working_dir) { repo.add(new_blob.name) }
=> ""

# git commit 
repo.commit_index("new file hello.txt added")
=> "[master d42b487] new file hello.txt added\n 1 files changed, 1 insertions(+), 0 deletions(-)\n create mode 100644 hello.txt\n"

ファイルの更新

# ファイルに新しい行を追加
File.open("hello.txt", "a") do |f|
  f.write("\nadd text")
end

# 先ほど修正したファイルのBlobオブジェクトを取得
blob = repo.tree / "hello.txt"
=> #<Grit::Blob "2989fdecdb20ecd0ca6b161c09f2aaa1ee5f537a">

# git add
Dir.chdir(repo.working_dir) { repo.add(blob.name) }

# git commit
repo.commit_index("modify hello.txt")
=> "[master 05b23ba] modify hello.txt\n 1 files changed, 2 insertions(+), 1 deletions(-)\n"

ファイルの削除

# 削除
repo.remove("hello.txt")
=> "rm 'hello.txt'\n"

# コミット
repo.commit_index("delete hello.txt")
=> "[master f18605e] delete hello.txt\n 1 files changed, 0 insertions(+), 2 deletions(-)\n delete mode 100644 hello.txt\n"