js-gitの使い方


あらかじめ読んでおくもの

js-gitとは

gitをjavascriptで実装しちゃったjs-git

わざわざgitをjavascriptで実装したからにはブラウザで使うでしょう?

js-gitは「なんらかの形で表現された.gitディレクトリ」の中身をあれこれするためのAPI集です。

この「なんらかの形で表現された.gitディレクトリ」には、オンメモリとかnodejsのfs標準ライブラリのラッパとかindexedDBとかWebSQLとかいろいろあります。DOMStorage系がないのが気になりますがあんな容量でローカルリポジトリとして使うのは難しいでしょう。localStorage使いたかったらlocalStorage-dbをご自身で書いてみるのも面白いでしょう。また、どこかのクラウドストレージのAPI叩いて保存するものも作れそうですね。

これだけあればブラウザからでも使えそうです。

browserifyでブラウザから使う

requireで依存関係処理しているのでbrowserifyでサクっとまとめましょう。

main.js


// This provides symbolic names for the octal modes used by git trees.
window.modes = require('./lib/modes');

// Create a repo by creating a plain object.
window.repo = {};

// This provides an in-memory storage backend that provides the following APIs:
// - saveAs(type, value) => hash
// - loadAs(type, hash) => hash
// - saveRaw(hash, binary) =>
// - loadRaw(hash) => binary
require('./mixins/mem-db')(repo);

// This adds a high-level API for creating multiple git objects by path.
// - createTree(entries) => hash
require('./mixins/create-tree')(repo);

// This provides extra methods for dealing with packfile streams.
// It depends on
// - unpack(packStream, opts) => hashes
// - pack(hashes, opts) => packStream
require('./mixins/pack-ops')(repo);

// This adds in walker algorithms for quickly walking history or a tree.
// - logWalk(ref|hash) => stream<commit>
// - treeWalk(hash) => stream<object>
require('./mixins/walkers')(repo);

// This combines parallel requests for the same resource for effeciency under load.
require('./mixins/read-combiner')(repo);

// This makes the object interface less strict.  See it's docs for details
require('./mixins/formats')(repo);

index.html

<script src="bundle.js"></script>

とりあえず動かす

$ browserify main.js -o bundle.js
$ open index.html

Chromeが開いたと思うのでWebInspectorのコンソールからrepoの中身をのぞいてみてください。

コミットしてみる

このへんにコミットの仕方載ってますね。

yieldとか恐ろしげなワードを使ってますがcallbackパターンも問題なく使えるようです。
以下はcallbackパターンでのコミットの例です


// First we create a blob from a string.  The `formats` mixin allows us to
// use a string directly instead of having to pass in a binary buffer.
repo.saveAs("blob", "Hello World\n", function(err, blobHash){
  console.log(err, blobHash);

  // Now we create a tree that is a folder containing the blob as `greeting.txt`
  repo.saveAs("tree", {
    "greeting.txt": { mode: modes.file, hash: blobHash }
  }, function(err, treeHash){
    console.log(err, treeHash);

    // With that tree, we can create a commit.
    // Again the `formats` mixin allows us to omit details like committer, date,
    // and parents.  It assumes sane defaults for these.
    repo.saveAs("commit", {
      author: {
        name: "Tim Caswell",
        email: "[email protected]"
      },
      tree: treeHash,
      message: "Test commit\n"
    }, function(err, commitHash){
      console.log(err, commitHash);
    });
  });
});

ネストがえらいこっちゃ。

コミット読み込む

このへんに載ってます。

yield使ってますが先述の通りcallback(err, result)パターンが使えます。

気をつけるところはblobなgitオブジェクトを読み込むとbyte arrayで返ってくるところでしょうか。

repo.saveAs("blob", "Hello World\n", function(err, blobHash){
  console.dir(blobHash);
  repo.loadAs("blob", blobHash, function(err, byteArray){
    console.log(byteArray);// -> [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 10]
  });
});

treeなgitオブジェクトの拡張子みてバイナリの読み方判断しないといけませんね。

開発状況

2014-09-09現在、ごらんのありさまだよ!

2週間ほど前からようやく書き込みAPIの整備がされてきたようです。

また、リモートリポジトリとのやりとりも実装される見込みのようです。

熱いですね!