Git
章 ▾ 第2版

A2.3 付録B:アプリケーションへのGitの組み込み - JGit

JGit

Javaプログラム内からGitを使用したい場合は、JGitと呼ばれるフル機能のGitライブラリがあります。JGitは、Javaでネイティブに記述された比較的高機能なGitの実装であり、Javaコミュニティで広く使用されています。JGitプロジェクトはEclipse傘下にあり、そのホームはhttps://www.eclipse.org/jgit/にあります。

セットアップ

プロジェクトをJGitに接続してコードを書き始めるには、いくつかの方法があります。おそらく最も簡単なのはMavenを使用することです。統合は、pom.xmlファイルの<dependencies>タグに次のスニペットを追加することで実現されます。

<dependency>
    <groupId>org.eclipse.jgit</groupId>
    <artifactId>org.eclipse.jgit</artifactId>
    <version>3.5.0.201409260305-r</version>
</dependency>

versionはおそらくこのドキュメントを読む頃には進んでいるでしょう。最新のリポジトリ情報については、https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgitを確認してください。この手順が完了すると、Mavenは必要なJGitライブラリを自動的に取得して使用します。

バイナリの依存関係を自分で管理したい場合は、https://www.eclipse.org/jgit/downloadから事前にビルドされたJGitバイナリを入手できます。次のようなコマンドを実行して、プロジェクトに組み込むことができます。

javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java
java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App

配管(Plumbing)

JGitには、配管(plumbing)と磁器(porcelain)の2つの基本的なレベルのAPIがあります。これらの用語はGit自体に由来しており、JGitもほぼ同じ種類の領域に分かれています。磁器APIは、一般的なユーザーレベルのアクション(通常のユーザーがGitコマンドラインツールを使用するような種類のもの)に対応するフレンドリーなフロントエンドであり、配管APIは、低レベルのリポジトリオブジェクトと直接対話するためのものです。

ほとんどのJGitセッションの出発点はRepositoryクラスであり、最初にやりたいことは、そのインスタンスを作成することです。ファイルシステムベースのリポジトリ(はい、JGitでは他のストレージモデルも使用できます)の場合、これはFileRepositoryBuilderを使用して実現されます。

// Create a new repository
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
    new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();

// Open an existing repository
Repository existingRepo = new FileRepositoryBuilder()
    .setGitDir(new File("my_repo/.git"))
    .build();

ビルダーには、プログラムが正確な場所を知っているかどうかに関係なく、Gitリポジトリを見つけるために必要なすべてのものを提供するためのfluent APIがあります。環境変数(.readEnvironment())を使用したり、作業ディレクトリ内の場所から検索を開始したり(.setWorkTree(…).findGitDir())、上記のように既知の.gitディレクトリを開いたりできます。

Repositoryインスタンスを取得したら、それを使用してあらゆる種類の操作を実行できます。簡単な例を次に示します。

// Get a reference
Ref master = repo.getRef("master");

// Get the object the reference points to
ObjectId masterTip = master.getObjectId();

// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");

// Load raw object contents
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);

// Create a branch
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();

// Delete a branch
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();

// Config
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");

ここにはかなりの処理が行われているため、セクションごとに説明していきます。

最初の行は、master参照へのポインタを取得します。JGitは、refs/heads/masterにある実際のmaster参照を自動的に取得し、その参照に関する情報を取得できるオブジェクトを返します。名前(.getName())と、直接参照のターゲットオブジェクト(.getObjectId())またはシンボリック参照が指す参照(.getTarget())を取得できます。Refオブジェクトはタグ参照とオブジェクトを表すため、タグが「peeled」、つまりタグオブジェクトの(潜在的に長い)文字列の最終ターゲットを指しているかどうかを問い合わせることができます。

2行目は、master参照のターゲットを取得します。これはObjectIdインスタンスとして返されます。ObjectIdはオブジェクトのSHA-1ハッシュを表しますが、これはGitのオブジェクトデータベースに存在する場合と存在しない場合があります。3行目も同様ですが、JGitがrev-parse構文をどのように処理するかを示しています(詳細については、ブランチ参照を参照してください)。Gitが理解できる任意のオブジェクト指定子を渡すことができ、JGitはそのオブジェクトの有効なObjectIdまたはnullのいずれかを返します。

次の2行は、オブジェクトの生の内容を読み込む方法を示しています。この例では、ObjectLoader.copyTo()を呼び出して、オブジェクトの内容を直接stdoutにストリーミングしていますが、ObjectLoaderにはオブジェクトの型とサイズを読み取るメソッドや、バイト配列として返すメソッドもあります。大きなオブジェクト(.isLarge()trueを返す場合)については、.openStream()を呼び出して、生のオブジェクトデータをすべて一度にメモリに取り込むことなく読み取ることができるInputStreamのようなオブジェクトを取得できます。

次の数行は、新しいブランチを作成するために必要なものを示しています。RefUpdateインスタンスを作成し、いくつかのパラメーターを設定し、.update()を呼び出して変更をトリガーします。この直後にあるのは、同じブランチを削除するコードです。これが機能するためには、.setForceUpdate(true)が必要であることに注意してください。そうしないと、.delete()の呼び出しはREJECTEDを返し、何も起こりません。

最後の例は、Git構成ファイルからuser.nameの値を取得する方法を示しています。このConfigインスタンスは、ローカル構成のために前に開いたリポジトリを使用しますが、グローバルおよびシステムの構成ファイルも自動的に検出し、そこから値を読み取ります。

これは、フル配管APIのほんの一例にすぎません。他にも多くのメソッドとクラスが利用可能です。また、ここではJGitが例外を使用してエラーを処理する方法についても示されていません。JGit APIは、標準のJava例外(IOExceptionなど)をスローすることがありますが、JGit固有の例外タイプ(NoRemoteRepositoryExceptionCorruptObjectExceptionNoMergeBaseExceptionなど)も多数提供されています。

ポーセリン

配管APIはかなり完全ですが、インデックスにファイルを追加したり、新しいコミットを作成したりするなど、一般的な目標を達成するためにそれらを結合するのは面倒な場合があります。JGitは、これに役立つ高レベルのAPIセットを提供しており、これらのAPIへのエントリポイントはGitクラスです。

Repository repo;
// construct repo...
Git git = new Git(repo);

Gitクラスには、非常に複雑な動作を構築するために使用できる、高レベルのビルダースタイルのメソッドの優れたセットがあります。例を見てみましょう。 — git ls-remoteのようなことをします

CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");
Collection<Ref> remoteRefs = git.lsRemote()
    .setCredentialsProvider(cp)
    .setRemote("origin")
    .setTags(true)
    .setHeads(false)
    .call();
for (Ref ref : remoteRefs) {
    System.out.println(ref.getName() + " -> " + ref.getObjectId().name());
}

これは、Gitクラスの一般的なパターンです。メソッドは、メソッド呼び出しをチェーンしてパラメーターを設定できるコマンドオブジェクトを返します。パラメーターは、.call()を呼び出すと実行されます。この場合、originリモートにタグを要求していますが、ヘッドは要求していません。認証のためにCredentialsProviderオブジェクトを使用していることにも注目してください。

addblamecommitcleanpushrebaserevertresetなど、他の多くのコマンドがGitクラスを通じて利用できます。

さらに詳しく知る

これは、JGitのすべての機能のほんの一部にすぎません。興味があり、さらに詳しく知りたい場合は、情報を探したり、インスピレーションを得たりする場所は次のとおりです。

scroll-to-top