Help us understand the problem. What is going on with this article?

JGitでリポジトリを解析するサンプル3種

More than 3 years have passed since last update.

注意書き

情報を集めたのが数年前なので、より新しい方法が存在する可能性があります。

背景

JGitは少し癖のあるライブラリで、gitの解析をしていた際に困った記憶があります。
そんな当時のコードが一部発掘され、役に立つ人がいるかもしれないので掲載します。
他の解析をしたい場合は、以下のドキュメントが参考になるかと思います。

JGit User Guide
JGit API Documentation

前提

リポジトリを表す変数名は、repositoryとします。
以下のコードで作成しています。

val repository = new FileRepository(new File(repositoryPath + "/.git"))

if( ! repository.getRepositoryState.canResetHead ){
  println("target path is not a valid repository")
  System.exit(0);
}

ここでは、gitリポジトリが存在するかどうか、また、正常な状態かどうかの判定をcanResetHeadで行っています。
チェック方法は様々あるかと思いますが、resetできればおおよそ解析には困らない状態なはずですので、ひとまずこの方法を採用しています。

サンプル

カレントブランチのコミットメッセージ一覧を表示

val revWalk = new PlotWalk(repository)
val rootId = repository.resolve("HEAD")
val root = revWalk.parseCommit(rootId)
revWalk.markStart(root)

val plotCommitList = new PlotCommitList[PlotLane]()
plotCommitList.source(revWalk)
plotCommitList.fillTo(Integer.MAX_VALUE)

for( c <- plotCommitList )
  println( c.getFullMessage )

HEADからInteger.MAX_VALUE個だけコミットを遡る、というシンプルなサンプルです。
もちろんそんなにコミット数は無いので、限界まで遡る、という意味合いになります。

repository.resolve()に文字列を与えると、解析して適切なObjectId(git内部で使ってるハッシュ)を返してくれます。
下の例では"HEAD"を与えていますが、例えば"HEAD^"を与えると1つ前のコミットからたどり始めます。

このサンプルではコミットメッセージのみですが、RevCommitクラスに格納されている情報を出力することができます。

あるコミットにおけるdiffを取得

val df = new DiffFormatter(DisabledOutputStream.INSTANCE)
df.setRepository(repository)
df.setDiffComparator(RawTextComparator.DEFAULT)
df.setDetectRenames(true)

def get(c: RevCommit): mutable.Buffer[DiffEntry] =
  c.getParentCount match {
    case 0 => mutable.Buffer.empty[DiffEntry]
    case _ => df.scan(c.getParent(0), c.getTree).asScala
  }

getにコミットを与えることで、その親との差分を取得できます。

差分情報は、ファイルごとにDiffEntryというクラスに収められます。
DiffEntryには、新しい側の情報と古い側の情報が両方保存されており、リネームや破棄を検出することが可能です。
例えば、AからBにrenameされた場合は、getOldPath="A", getNewPath="B"となります。
また、Aが破棄された場合は、getOldPath="A"、getNewPath="/dev/null"となります。

DiffFormatter
DiffEntry

特定リビジョンのファイルの内容を表示

git cat-file -pです。
該当ファイルのID(blob)を取得した後、以下の通りストリームを使って取得する必要があります。

How to “cat” a file in JGit?

val fileId = ... // 目的ファイルのobject ID
val loader = repository.open(fileId)
loader.copyTo(System.out)   // loader.openStream でInputStreamが取得できる

まとめ

JGitは扱いにくい癖があるので、上記サンプルが参考になれば幸いです。

esplo
日頃からぷるぷるしています。直方体に近いです。
https://blog.esplo.net/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away