diff --git a/B-embedding-git.asc b/B-embedding-git.asc index 64d518c6..2c58c6c0 100644 --- a/B-embedding-git.asc +++ b/B-embedding-git.asc @@ -13,7 +13,8 @@ Even non-developer applications, such as document editors, could potentially ben また、非開発者向けのアプリケーション(例えばドキュメントエディタなど)であっても、バージョン管理機能から利益を享受できる可能性があります。Gitのモデルは、様々なシナリオに上手く適合します。 ////////////////////////// -If you need to integrate Git with your application, you have essentially three choices: spawning a shell and using the Git command-line tool; Libgit2; and JGit. +If you need to integrate Git with your application, you have essentially two options: spawn a shell and call the `git` command-line program, or embed a Git library into your application. +Here we'll cover command-line integration and several of the most popular embeddable Git libraries. ////////////////////////// Gitをアプリケーションに統合する場合、やり方は大きく分けて3種類あります。1つ目はシェルのプロセスを生成してGitのコマンドラインツールを使う方法、2つ目はLibgit2を使う方法、3つ目はJGitを使う方法です。 @@ -22,3 +23,7 @@ include::book/B-embedding-git/sections/command-line.asc[] include::book/B-embedding-git/sections/libgit2.asc[] include::book/B-embedding-git/sections/jgit.asc[] + +include::book/B-embedding-git/sections/go-git.asc[] + +include::book/B-embedding-git/sections/dulwich.asc[] diff --git a/book/B-embedding-git/sections/dulwich.asc b/book/B-embedding-git/sections/dulwich.asc new file mode 100644 index 00000000..c202686d --- /dev/null +++ b/book/B-embedding-git/sections/dulwich.asc @@ -0,0 +1,44 @@ +=== Dulwich + +(((Dulwich)))(((Python))) +There is also a pure-Python Git implementation - Dulwich. +The project is hosted under https://www.dulwich.io/ +It aims to provide an interface to git repositories (both local and remote) that doesn't call out to git directly but instead uses pure Python. +It has an optional C extensions though, that significantly improve the performance. + +Dulwich follows git design and separate two basic levels of API: plumbing and porcelain. + +Here is an example of using the lower level API to access the commit message of the last commit: + +[source, python] +---- +from dulwich.repo import Repo +r = Repo('.') +r.head() +# '57fbe010446356833a6ad1600059d80b1e731e15' + +c = r[r.head()] +c +# + +c.message +# 'Add note about encoding.\n' +---- + +To print a commit log using high-level porcelain API, one can use: + +[source, python] +---- +from dulwich import porcelain +porcelain.log('.', max_entries=1) + +#commit: 57fbe010446356833a6ad1600059d80b1e731e15 +#Author: Jelmer Vernooij +#Date: Sat Apr 29 2017 23:57:34 +0000 +---- + + +==== Further Reading + + * The official API documentation is available at https://www.dulwich.io/apidocs/dulwich.html[] + * Official tutorial at https://www.dulwich.io/docs/tutorial[] has many examples of how to do specific tasks with Dulwich diff --git a/book/B-embedding-git/sections/go-git.asc b/book/B-embedding-git/sections/go-git.asc new file mode 100644 index 00000000..3335802b --- /dev/null +++ b/book/B-embedding-git/sections/go-git.asc @@ -0,0 +1,83 @@ +=== go-git + +(((go-git)))(((Go))) +In case you want to integrate Git into a service written in Golang, there also is a pure Go library implementation. +This implementation does not have any native dependencies and thus is not prone to manual memory management errors. +It is also transparent for the standard Golang performance analysis tooling like CPU, Memory profilers, race detector, etc. + +go-git is focused on extensibility, compatibility and supports most of the plumbing APIs, which is documented at https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md[]. + +Here is a basic example of using Go APIs: + +[source, go] +---- +import "github.com/go-git/go-git/v5" + +r, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ + URL: "https://github.com/go-git/go-git", + Progress: os.Stdout, +}) +---- + +As soon as you have a `Repository` instance, you can access information and perform mutations on it: + +[source, go] +---- +// retrieves the branch pointed by HEAD +ref, err := r.Head() + +// get the commit object, pointed by ref +commit, err := r.CommitObject(ref.Hash()) + +// retrieves the commit history +history, err := commit.History() + +// iterates over the commits and print each +for _, c := range history { + fmt.Println(c) +} +---- + +==== Advanced Functionality + +go-git has few notable advanced features, one of which is a pluggable storage system, which is similar to Libgit2 backends. +The default implementation is in-memory storage, which is very fast. + +[source, go] +---- +r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ + URL: "https://github.com/go-git/go-git", +}) +---- + +Pluggable storage provides many interesting options. +For instance, https://github.com/go-git/go-git/tree/master/_examples/storage[] allows you to store references, objects, and configuration in an Aerospike database. + +Another feature is a flexible filesystem abstraction. +Using https://pkg.go.dev/github.com/go-git/go-billy/v5?tab=doc#Filesystem[] it is easy to store all the files in different way i.e by packing all of them to a single archive on disk or by keeping them all in-memory. + +Another advanced use-case includes a fine-tunable HTTP client, such as the one found at https://github.com/go-git/go-git/blob/master/_examples/custom_http/main.go[]. + +[source, go] +---- +customClient := &http.Client{ + Transport: &http.Transport{ // accept any certificate (might be useful for testing) + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + Timeout: 15 * time.Second, // 15 second timeout + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse // don't follow redirect + }, +} + +// Override http(s) default protocol to use our custom client +client.InstallProtocol("https", githttp.NewClient(customClient)) + +// Clone repository using the new client if the protocol is https:// +r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url}) +---- + +==== Further Reading + +A full treatment of go-git's capabilities is outside the scope of this book. +If you want more information on go-git, there's API documentation at https://pkg.go.dev/github.com/go-git/go-git/v5[], and a set of usage examples at https://github.com/go-git/go-git/tree/master/_examples[]. diff --git a/book/B-embedding-git/sections/jgit.asc b/book/B-embedding-git/sections/jgit.asc index c4f87755..d70f1b4b 100644 --- a/book/B-embedding-git/sections/jgit.asc +++ b/book/B-embedding-git/sections/jgit.asc @@ -1,10 +1,10 @@ === JGit -(((jgit)))(((java))) +(((jgit)))(((Java))) ////////////////////////// If you want to use Git from within a Java program, there is a fully featured Git library called JGit. JGit is a relatively full-featured implementation of Git written natively in Java, and is widely used in the Java community. -The JGit project is under the Eclipse umbrella, and its home can be found at http://www.eclipse.org/jgit[]. +The JGit project is under the Eclipse umbrella, and its home can be found at https://www.eclipse.org/jgit/[]. ////////////////////////// JavaのプログラムからGitを使いたい場合、十分な機能を備えたGitのライブラリであるJGitが利用できます。 JGitは、ネイティブJavaによるGitの実装です。Gitのほぼ全機能を備えており、Javaコミュニティで広く使われています。 @@ -32,14 +32,14 @@ JGitをあなたのプロジェクトへ追加して、コードを書き始め ---- ////////////////////////// -The `version` will most likely have advanced by the time you read this; check http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] for updated repository information. +The `version` will most likely have advanced by the time you read this; check https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] for updated repository information. Once this step is done, Maven will automatically acquire and use the JGit libraries that you'll need. ////////////////////////// 皆さんがこれを読んでいる時には、おそらく `version` の番号はもっと進んでいるでしょうから、 http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] で最新のリポジトリの情報を確認してください。 このステップが完了したら、以降は必要なJGitライブラリの取得と使用をMavenが自動的に行ってくれます。 ////////////////////////// -If you would rather manage the binary dependencies yourself, pre-built JGit binaries are available from http://www.eclipse.org/jgit/download[]. +If you would rather manage the binary dependencies yourself, pre-built JGit binaries are available from https://www.eclipse.org/jgit/download[]. You can build them into your project by running a command like this: ////////////////////////// バイナリの依存関係を自前で管理したい場合は、ビルド済みのJGitのバイナリが http://www.eclipse.org/jgit/download[] から取得できます。 @@ -173,7 +173,7 @@ There's quite a bit going on here, so let's go through it one section at a time. ////////////////////////// The first line gets a pointer to the `master` reference. -JGit automatically grabs the _actual_ master ref, which lives at `refs/heads/master`, and returns an object that lets you fetch information about the reference. +JGit automatically grabs the _actual_ `master` ref, which lives at `refs/heads/master`, and returns an object that lets you fetch information about the reference. You can get the name (`.getName()`), and either the target object of a direct reference (`.getObjectId()`) or the reference pointed to by a symbolic ref (`.getTarget()`). Ref objects are also used to represent tag refs and objects, so you can ask if the tag is ``peeled,'' meaning that it points to the final target of a (potentially long) string of tag objects. ////////////////////////// @@ -185,7 +185,7 @@ JGitは `refs/heads/master` にある _実際の_ master参照を自動的に取 ////////////////////////// The second line gets the target of the `master` reference, which is returned as an ObjectId instance. ObjectId represents the SHA-1 hash of an object, which might or might not exist in Git's object database. -The third line is similar, but shows how JGit handles the rev-parse syntax (for more on this, see <>); you can pass any object specifier that Git understands, and JGit will return either a valid ObjectId for that object, or `null`. +The third line is similar, but shows how JGit handles the rev-parse syntax (for more on this, see <>); you can pass any object specifier that Git understands, and JGit will return either a valid ObjectId for that object, or `null`. ////////////////////////// 2行目では、 `master` 参照の指す先を取得して、ObjectIdインスタンスの形で返します。 ObjectIdはGitのオブジェクトデータベース中にある(または、データベース中にない)オブジェクトのSHA-1ハッシュを表しています。 @@ -252,7 +252,7 @@ Git git = new Git(repo); ////////////////////////// The Git class has a nice set of high-level _builder_-style methods that can be used to construct some pretty complex behavior. -Let's take a look at an example – doing something like `git ls-remote`: +Let's take a look at an example -- doing something like `git ls-remote`: ////////////////////////// Gitクラスは、洗練された高レベルの _builder_ スタイルのメソッドを備えています。これは、非常に複雑な処理を組み立てる際に利用できます。 それでは例を見てみましょう。ここでは `git ls-remote` のような処理を行っています。 @@ -298,10 +298,9 @@ If you're interested and want to learn more, here's where to look for informatio 興味が湧いた、もっと知りたいということなら、情報は次の場所から探せます。 ////////////////////////// -* The official JGit API documentation is available online at http://download.eclipse.org/jgit/docs/latest/apidocs[]. +* The official JGit API documentation can be found at https://www.eclipse.org/jgit/documentation[]. These are standard Javadoc, so your favorite JVM IDE will be able to install them locally, as well. * The JGit Cookbook at https://github.com/centic9/jgit-cookbook[] has many examples of how to do specific tasks with JGit. -* There are several good resources pointed out at http://stackoverflow.com/questions/6861881[]. ////////////////////////// * オフィシャルなJGit APIドキュメントは http://download.eclipse.org/jgit/docs/latest/apidocs[] で参照できます。 標準的なJavadocなので、ローカルにインストールして、好きなJVM IDEから参照することもできます。 diff --git a/book/B-embedding-git/sections/libgit2.asc b/book/B-embedding-git/sections/libgit2.asc index bf3ed7fb..5f6bd5d4 100644 --- a/book/B-embedding-git/sections/libgit2.asc +++ b/book/B-embedding-git/sections/libgit2.asc @@ -7,7 +7,7 @@ ////////////////////////// Another option at your disposal is to use Libgit2. Libgit2 is a dependency-free implementation of Git, with a focus on having a nice API for use within other programs. -You can find it at http://libgit2.github.com[]. +You can find it at https://libgit2.org[]. ////////////////////////// あなたが取れる2つ目のオプションは、Libgit2を使用することです。 Libgit2は、他のプログラムへの依存性のないGitの実装であり、プログラムから使いやすいAPIを提供することにフォーカスしています。 @@ -22,7 +22,7 @@ Here's a whirlwind tour: [source,c] ////////////////////////// ------ +---- // Open a repository git_repository *repo; int error = git_repository_open(&repo, "/path/to/repository"); @@ -41,7 +41,7 @@ const git_oid *tree_id = git_commit_tree_id(commit); // Cleanup git_commit_free(commit); git_repository_free(repo); ------ +---- ////////////////////////// ----- // リポジトリを開く @@ -76,7 +76,7 @@ There's also the `git_repository_open_ext` which includes options for searching, 他の方法としては、 `git_repository_open_ext` を使って検索オプション付きで開く方法、 `git_clone` とその仲間を使ってリモートリポジトリのローカルなクローンを作る方法、 `git_repository_init` を使って全く新規にリポジトリを作る方法があります。 ////////////////////////// -The second chunk of code uses rev-parse syntax (see <> for more on this) to get the commit that HEAD eventually points to. +The second chunk of code uses rev-parse syntax (see <> for more on this) to get the commit that HEAD eventually points to. The type returned is a `git_object` pointer, which represents something that exists in the Git object database for a repository. `git_object` is actually a ``parent'' type for several different kinds of objects; the memory layout for each of the ``child'' types is the same as for `git_object`, so you can safely cast to the right one. In this case, `git_object_type(commit)` would return `GIT_OBJ_COMMIT`, so it's safe to cast to a `git_commit` pointer. @@ -132,7 +132,7 @@ tree = commit.tree ////////////////////////// As you can see, the code is much less cluttered. -Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions. +Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions. Secondly, there's no explicit freeing of resources, since Ruby is garbage-collected. Let's take a look at a slightly more complicated example: crafting a commit from scratch ////////////////////////// @@ -189,7 +189,7 @@ commit = repo.lookup(commit_id) # <8> ////////////////////////// The Ruby code is nice and clean, but since Libgit2 is doing the heavy lifting, this code will run pretty fast, too. -If you're not a rubyist, we touch on some other bindings in <>. +If you're not a rubyist, we touch on some other bindings in <<_libgit2_bindings>>. ////////////////////////// このRubyのコードは単純明快です。また、重い処理はLibgit2が行っているので、非常に高速に実行できます。 Rubyist でない方のために、 <> では他のバインディングにも触れています。 @@ -230,7 +230,7 @@ error = git_odb_add_backend(odb, my_backend, 1); // <3> git_repository *repo; error = git_repository_open(&repo, "some-path"); -error = git_repository_set_odb(odb); // <4> +error = git_repository_set_odb(repo, odb); // <4> ---- ////////////////////////// @@ -300,7 +300,7 @@ Take a look at the `include/git2/sys/odb_backend.h` file in the Libgit2 source f この初期化関数では、構造体にメモリを割り当て、カスタムコンテキストを設定し、それがサポートしている `parent` 構造体のメンバーへデータを設定しています。 その他の呼び出しのシグネチャについては、Libgit2のソースの `include/git2/sys/odb_backend.h` ファイルを見てみてください。ユースケースがはっきりしていれば、シグネチャのうちどれをサポートすればよいかを判断するのに役立つでしょう。 -[[r_libgit2_bindings]] +[[_libgit2_bindings]] ////////////////////////// ==== Other Bindings ////////////////////////// @@ -331,9 +331,9 @@ Here's what our example program looks like: サンプルプログラムは次のようになります。 [source,csharp] ------ +---- new Repository(@"C:\path\to\repo").Head.Tip.Message; ------ +---- ////////////////////////// For desktop Windows applications, there's even a NuGet package that will help you get started quickly. @@ -353,11 +353,11 @@ Objective-Git (https://github.com/libgit2/objective-git[]) は、そういった サンプルプログラムは次のようになります。 [source,objc] ------ +---- GTRepository *repo = [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL]; NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message]; ------ +---- ////////////////////////// Objective-git is fully interoperable with Swift, so don't fear if you've left Objective-C behind. @@ -369,7 +369,7 @@ Objective-git は Swift に対しても完全な相互運用性があるので (((Python))) ////////////////////////// -The bindings for Libgit2 in Python are called Pygit2, and can be found at http://www.pygit2.org/[]. +The bindings for Libgit2 in Python are called Pygit2, and can be found at https://www.pygit2.org[]. Our example program: ////////////////////////// Libgit2 の Python 向けバインディングは Pygit2 という名前で、 http://www.pygit2.org/[] から取得できます。 diff --git a/book/B-embedding-git/sections/libgit2.asc.bak b/book/B-embedding-git/sections/libgit2.asc.bak new file mode 100644 index 00000000..8e609dac --- /dev/null +++ b/book/B-embedding-git/sections/libgit2.asc.bak @@ -0,0 +1,407 @@ +////////////////////////// +=== Libgit2 +////////////////////////// +=== Libgit2を使う方法 + +(((libgit2)))((("C"))) +////////////////////////// +Another option at your disposal is to use Libgit2. +Libgit2 is a dependency-free implementation of Git, with a focus on having a nice API for use within other programs. +You can find it at https://libgit2.org[]. +////////////////////////// +あなたが取れる2つ目のオプションは、Libgit2を使用することです。 +Libgit2は、他のプログラムへの依存性のないGitの実装であり、プログラムから使いやすいAPIを提供することにフォーカスしています。 +Libgit2は http://libgit2.github.com[] から取得できます。 + +////////////////////////// +First, let's take a look at what the C API looks like. +Here's a whirlwind tour: +////////////////////////// +まずは、C言語用のAPIがどのようなものか見てみましょう。 +ここは駆け足で行きます。 + +[source,c] +////////////////////////// +---- +// Open a repository +git_repository *repo; +int error = git_repository_open(&repo, "/path/to/repository"); + +// Dereference HEAD to a commit +git_object *head_commit; +error = git_revparse_single(&head_commit, repo, "HEAD^{commit}"); +git_commit *commit = (git_commit*)head_commit; + +// Print some of the commit's properties +printf("%s", git_commit_message(commit)); +const git_signature *author = git_commit_author(commit); +printf("%s <%s>\n", author->name, author->email); +const git_oid *tree_id = git_commit_tree_id(commit); + +// Cleanup +git_commit_free(commit); +git_repository_free(repo); +---- +////////////////////////// +----- +// リポジトリを開く +git_repository *repo; +int error = git_repository_open(&repo, "/path/to/repository"); + +// HEADへの参照を解決してコミットを取得 +git_object *head_commit; +error = git_revparse_single(&head_commit, repo, "HEAD^{commit}"); +git_commit *commit = (git_commit*)head_commit; + +// コミットのプロパティのうちいくつかを出力 +printf("%s", git_commit_message(commit)); +const git_signature *author = git_commit_author(commit); +printf("%s <%s>\n", author->name, author->email); +const git_oid *tree_id = git_commit_tree_id(commit); + +// クリーンアップ +git_commit_free(commit); +git_repository_free(repo); +----- + +////////////////////////// +The first couple of lines open a Git repository. +The `git_repository` type represents a handle to a repository with a cache in memory. +This is the simplest method, for when you know the exact path to a repository's working directory or `.git` folder. +There's also the `git_repository_open_ext` which includes options for searching, `git_clone` and friends for making a local clone of a remote repository, and `git_repository_init` for creating an entirely new repository. +////////////////////////// +最初の2行で、Gitのリポジトリを開いています。 +`git_repository` 型は、メモリにキャッシュされているリポジトリへのハンドルを表しています。 +リポジトリの作業ディレクトリか、または `.git` フォルダの正確なパスが分かっている場合、これがリポジトリを開く最もシンプルな方法です。 +他の方法としては、 `git_repository_open_ext` を使って検索オプション付きで開く方法、 `git_clone` とその仲間を使ってリモートリポジトリのローカルなクローンを作る方法、 `git_repository_init` を使って全く新規にリポジトリを作る方法があります。 + +////////////////////////// +The second chunk of code uses rev-parse syntax (see <> for more on this) to get the commit that HEAD eventually points to. +The type returned is a `git_object` pointer, which represents something that exists in the Git object database for a repository. +`git_object` is actually a ``parent'' type for several different kinds of objects; the memory layout for each of the ``child'' types is the same as for `git_object`, so you can safely cast to the right one. +In this case, `git_object_type(commit)` would return `GIT_OBJ_COMMIT`, so it's safe to cast to a `git_commit` pointer. +////////////////////////// +2番目のコードのかたまりは、 rev-parse 文法(詳細は <> を参照)を使って、HEADが最終的に指しているコミットを取得しています。 +戻り値は `git_object` 型のポインタで、これはリポジトリのGitオブジェクトデータベースに存在する何かを表しています。 +`git_object` 型は、実際には数種類のオブジェクトの ``親'' にあたります。 ``子'' にあたる型のメモリレイアウトは `git_object` 型と同じになっているので、正しい型へのキャストは安全に行えます。 +上記の場合では、 `git_object_type(commit)` が `GIT_OBJ_COMMIT` を返すので、 `git_commit` 型のポインタへ安全にキャストできます。 + +////////////////////////// +The next chunk shows how to access the commit's properties. +The last line here uses a `git_oid` type; this is Libgit2's representation for a SHA-1 hash. +////////////////////////// +次のかたまりは、コミットのプロパティにアクセスする方法を示しています。 +ここの最後の行では `git_oid` 型を使用しています。これは、 Libgit2 において SHA-1 ハッシュを表現する型です。 + +////////////////////////// +From this sample, a couple of patterns have started to emerge: +////////////////////////// +このサンプルからは、いくつかのパターンが見て取れます。 + +////////////////////////// +* If you declare a pointer and pass a reference to it into a Libgit2 call, that call will probably return an integer error code. + A `0` value indicates success; anything less is an error. +* If Libgit2 populates a pointer for you, you're responsible for freeing it. +* If Libgit2 returns a `const` pointer from a call, you don't have to free it, but it will become invalid when the object it belongs to is freed. +* Writing C is a bit painful. +////////////////////////// +* ポインタを宣言して、 Libgit2 の呼び出しにそのポインタへの参照を渡すと、その呼び出しは多くの場合 int 型のエラーコードを返す。 + 値 `0` は成功を表す。それより小さい値はエラーを表す。 +* Libgit2 がポインタへ値を入れて返したら、解放は自前で行わなければならない。 +* Libgit2 の呼び出しが `const` ポインタを返した場合、開放する必要はない。ただし、それがそれが属するオブジェクトが解放されたら、ポインタは無効になる。 +* Cでコードを書くのはちょっとキツい。 + +(((Ruby))) +////////////////////////// +That last one means it isn't very probable that you'll be writing C when using Libgit2. +Fortunately, there are a number of language-specific bindings available that make it fairly easy to work with Git repositories from your specific language and environment. +Let's take a look at the above example written using the Ruby bindings for Libgit2, which are named Rugged, and can be found at https://github.com/libgit2/rugged[]. +////////////////////////// +最後の1つは、 Libgit2 を使用するときに、C言語でコードを書こうということはまずないだろう、というくらいの意味です。 +幸いなことに、様々な言語用のバインディングが利用可能です。これを使えば、あなたの使っている特定の言語や環境から、Gitリポジトリに対する作業を非常に簡単に行えます。 +Libgit2 の Ruby 向けバインディングを使って上記の例を書いたものを見てみましょう。Libgit2 の Ruby 向けバインディングは Rugged という名前で、 https://github.com/libgit2/rugged[] から取得できます。 + +[source,ruby] +---- +repo = Rugged::Repository.new('path/to/repository') +commit = repo.head.target +puts commit.message +puts "#{commit.author[:name]} <#{commit.author[:email]}>" +tree = commit.tree +---- + +////////////////////////// +As you can see, the code is much less cluttered. +Firstly, Rugged uses exceptions; it can raise things like `ConfigError` or `ObjectError` to signal error conditions. +Secondly, there's no explicit freeing of resources, since Ruby is garbage-collected. +Let's take a look at a slightly more complicated example: crafting a commit from scratch +////////////////////////// +ご覧のように、コードがだいぶすっきりしました。 +第一に、 Rugged は例外を使用します。エラーの状態を知らせるのに、 `ConfigError` や `ObjectError` のような例外を raise できます。 +第二に、リソースの明示的な解放処理がありません。これは、 Ruby がガベージコレクションをしてくれるためです。 +それではもう少し複雑な例を見てみましょう。次の例では、コミットをゼロから作成しています。 + +[source,ruby] +---- +blob_id = repo.write("Blob contents", :blob) # <1> + +index = repo.index +index.read_tree(repo.head.target.tree) +index.add(:path => 'newfile.txt', :oid => blob_id) # <2> + +sig = { + :email => "bob@example.com", + :name => "Bob User", + :time => Time.now, +} + +commit_id = Rugged::Commit.create(repo, + :tree => index.write_tree(repo), # <3> + :author => sig, + :committer => sig, # <4> + :message => "Add newfile.txt", # <5> + :parents => repo.empty? ? [] : [ repo.head.target ].compact, # <6> + :update_ref => 'HEAD', # <7> +) +commit = repo.lookup(commit_id) # <8> +---- + +////////////////////////// +<1> Create a new blob, which contains the contents of a new file. +<2> Populate the index with the head commit's tree, and add the new file at the path `newfile.txt`. +<3> This creates a new tree in the ODB, and uses it for the new commit. +<4> We use the same signature for both the author and committer fields. +<5> The commit message. +<6> When creating a commit, you have to specify the new commit's parents. + This uses the tip of HEAD for the single parent. +<7> Rugged (and Libgit2) can optionally update a reference when making a commit. +<8> The return value is the SHA-1 hash of a new commit object, which you can then use to get a `Commit` object. +////////////////////////// +<1> 新しいファイルの内容を含む新しい blob を作成します。 +<2> インデックスとHEADのコミットのツリーを取得し、パス `newfile.txt` にある新しいファイルを追加します。 +<3> ODBに新しいツリーを作成し、それを新しいコミット用に使用しています。 +<4> author フィールドと committer フィールドに同じ署名を使います。 +<5> コミットメッセージです。 +<6> コミットを作成するときには、新しいコミットの親を指定する必要があります。 + ここではHEADの先端を単一の親として指定しています。 +<7> Rugged (およびLibgit2)では、コミットを作成する際に、必要に応じて参照を更新することもできます。 +<8> 戻り値は新しいコミットオブジェクトの SHA-1 ハッシュです。これは後で `Commit` オブジェクトを取得するために使用できます。 + +////////////////////////// +The Ruby code is nice and clean, but since Libgit2 is doing the heavy lifting, this code will run pretty fast, too. +If you're not a rubyist, we touch on some other bindings in <<_libgit2_bindings>>. +////////////////////////// +このRubyのコードは単純明快です。また、重い処理はLibgit2が行っているので、非常に高速に実行できます。 +Rubyist でない方のために、 <> では他のバインディングにも触れています。 + + +////////////////////////// +==== Advanced Functionality +////////////////////////// +==== 高度な機能 + +////////////////////////// +Libgit2 has a couple of capabilities that are outside the scope of core Git. +One example is pluggability: Libgit2 allows you to provide custom ``backends'' for several types of operation, so you can store things in a different way than stock Git does. +Libgit2 allows custom backends for configuration, ref storage, and the object database, among other things. +////////////////////////// +Libgit2 には、Git のコアがスコープ外としている機能がいくつか備わっています。 +一つの例がプラグイン機能です。 Libgit2 では、一部の機能に対し、カスタム ``バックエンド'' を指定できます。これにより、Git が行うのとは別の方法でデータを保存することができます。 +Libgit2 では設定、refストレージ、オブジェクトデータベースなどに対してカスタムバックエンドを指定できます。 + +////////////////////////// +Let's take a look at how this works. +The code below is borrowed from the set of backend examples provided by the Libgit2 team (which can be found at https://github.com/libgit2/libgit2-backends[]). +Here's how a custom backend for the object database is set up: +////////////////////////// +バックエンドがどのように機能するか見てみましょう。 +次のコードは、Libgit2チームが提供しているサンプル( https://github.com/libgit2/libgit2-backends[] から取得できます)から拝借しています。 +オブジェクトデータベース用のカスタムバックエンドを設定する方法を示しています。 + +[source,c] +---- +git_odb *odb; +int error = git_odb_new(&odb); // <1> + +git_odb_backend *my_backend; +error = git_odb_backend_mine(&my_backend, /*…*/); // <2> + +error = git_odb_add_backend(odb, my_backend, 1); // <3> + +git_repository *repo; +error = git_repository_open(&repo, "some-path"); +error = git_repository_set_odb(repo, odb); // <4> +---- + +////////////////////////// +_(Note that errors are captured, but not handled. We hope your code is better than ours.)_ +////////////////////////// +_(ここで、エラーの捕捉はしていますが、エラー処理は行っていないことに注意してください。あなたのコードが私たちのものより優れていることを願っています。)_ + +////////////////////////// +<1> Initialize an empty object database (ODB) ``frontend,'' which will act as a container for the ``backends'' which are the ones doing the real work. +<2> Initialize a custom ODB backend. +<3> Add the backend to the frontend. +<4> Open a repository, and set it to use our ODB to look up objects. +////////////////////////// +<1> 空のオブジェクトデータベース(ODB) ``フロントエンド'' を初期化します。これは、実際の処理を行う ``バックエンド'' のコンテナとして機能します。 +<2> カスタムODBバックエンドを初期化します。 +<3> フロントエンドにバックエンドを追加します。 +<4> リポジトリを開きます。作成したODBを、オブジェクトの検索に使うように設定します。 + +////////////////////////// +But what is this `git_odb_backend_mine` thing? +Well, that's the constructor for your own ODB implementation, and you can do whatever you want in there, so long as you fill in the `git_odb_backend` structure properly. +Here's what it _could_ look like: +////////////////////////// +さて、この `git_odb_backend_mine` というのは何でしょうか? +そう、これは自作のODB実装のコンストラクタです。この中では、 `git_odb_backend` 構造体へ適切に値を設定しさえしていれば、どんな処理でも行えます。 +処理は _例えば_ 以下のようになります。 + +[source,c] +---- +typedef struct { + git_odb_backend parent; + + // Some other stuff + void *custom_context; +} my_backend_struct; + +int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/) +{ + my_backend_struct *backend; + + backend = calloc(1, sizeof (my_backend_struct)); + + backend->custom_context = …; + + backend->parent.read = &my_backend__read; + backend->parent.read_prefix = &my_backend__read_prefix; + backend->parent.read_header = &my_backend__read_header; + // … + + *backend_out = (git_odb_backend *) backend; + + return GIT_SUCCESS; +} +---- + +////////////////////////// +The subtlest constraint here is that `my_backend_struct`'s first member must be a `git_odb_backend` structure; this ensures that the memory layout is what the Libgit2 code expects it to be. +The rest of it is arbitrary; this structure can be as large or small as you need it to be. +////////////////////////// +ここで、非常に分かりにくい制約として、 `my_backend_struct` の最初のメンバ変数は `git_odb_backend` 構造体である必要があります。これによって、Libgit2 のコードが期待している通りのメモリレイアウトになることが保証されます。 +構造体の残りの部分は任意です。この構造体は必要に合わせて大きくしたり小さくしたりして構いません。 + +////////////////////////// +The initialization function allocates some memory for the structure, sets up the custom context, and then fills in the members of the `parent` structure that it supports. +Take a look at the `include/git2/sys/odb_backend.h` file in the Libgit2 source for a complete set of call signatures; your particular use case will help determine which of these you'll want to support. +////////////////////////// +この初期化関数では、構造体にメモリを割り当て、カスタムコンテキストを設定し、それがサポートしている `parent` 構造体のメンバーへデータを設定しています。 +その他の呼び出しのシグネチャについては、Libgit2のソースの `include/git2/sys/odb_backend.h` ファイルを見てみてください。ユースケースがはっきりしていれば、シグネチャのうちどれをサポートすればよいかを判断するのに役立つでしょう。 + +[[_libgit2_bindings]] +////////////////////////// +==== Other Bindings +////////////////////////// +==== その他のバインディング + +////////////////////////// +Libgit2 has bindings for many languages. +Here we show a small example using a few of the more complete bindings packages as of this writing; libraries exist for many other languages, including C++, Go, Node.js, Erlang, and the JVM, all in various stages of maturity. +The official collection of bindings can be found by browsing the repositories at https://github.com/libgit2[]. +The code we'll write will return the commit message from the commit eventually pointed to by HEAD (sort of like `git log -1`). +////////////////////////// +Libgit2 には各種の言語向けのバインディングがあります。 +ここでは、これを書いている時点で利用できるバインディングの中から、その一部を使用して、小さなサンプルプログラムを示していきます。他にも、C++、Go、Node.js、Erlang、JVMなど多くの言語向けのライブラリがあり、成熟度合いも様々です。 +バインディングの公式なコレクションは、 https://github.com/libgit2[] にあるリポジトリを探せば見つかります。 +以降で示すコードはいずれも、最終的にHEADが指しているコミットのコミットメッセージを返します(`git log -1` のようなものです)。 + + +===== LibGit2Sharp + +(((.NET)))(((C#)))(((Mono))) +////////////////////////// +If you're writing a .NET or Mono application, LibGit2Sharp (https://github.com/libgit2/libgit2sharp[]) is what you're looking for. +The bindings are written in C#, and great care has been taken to wrap the raw Libgit2 calls with native-feeling CLR APIs. +Here's what our example program looks like: +////////////////////////// +.NET や Mono でアプリケーションを書いているなら、 LibGit2Sharp (https://github.com/libgit2/libgit2sharp[]) をお探しでしょう。 +バインディングは C# で書かれていて、生の Libgit2 の呼び出しを、ネイティブ感のある CLR API でラップすることに細心の注意が払われています。 +サンプルプログラムは次のようになります。 + +[source,csharp] +---- +new Repository(@"C:\path\to\repo").Head.Tip.Message; +---- + +////////////////////////// +For desktop Windows applications, there's even a NuGet package that will help you get started quickly. +////////////////////////// +Windows向けのデスクトップアプリケーション向けにはNuGetパッケージもあります。これは、すぐに作業を始めようという時に役立ちます。 + +===== objective-git + +(((Apple)))(((Objective-C)))(((Cocoa))) +////////////////////////// +If your application is running on an Apple platform, you're likely using Objective-C as your implementation language. +Objective-Git (https://github.com/libgit2/objective-git[]) is the name of the Libgit2 bindings for that environment. +The example program looks like this: +////////////////////////// +Apple のプラットフォーム向けのアプリケーションを書いているなら、おそらく実装には Objective-C を使用しているものと思います。 +Objective-Git (https://github.com/libgit2/objective-git[]) は、そういった環境向けの Libgit2 のバインディングです。 +サンプルプログラムは次のようになります。 + +[source,objc] +---- +GTRepository *repo = + [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL]; +NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message]; +---- + +////////////////////////// +Objective-git is fully interoperable with Swift, so don't fear if you've left Objective-C behind. +////////////////////////// +Objective-git は Swift に対しても完全な相互運用性があるので、 Objective-C を捨てたとしても怖くありません。 + + +===== pygit2 + +(((Python))) +////////////////////////// +The bindings for Libgit2 in Python are called Pygit2, and can be found at http://www.pygit2.org/[]. +Our example program: +////////////////////////// +Libgit2 の Python 向けバインディングは Pygit2 という名前で、 http://www.pygit2.org/[] から取得できます。 +サンプルプログラムは次のようになります。 + +[source,python] +////////////////////////// +---- +pygit2.Repository("/path/to/repo") # open repository + .head # get the current branch + .peel(pygit2.Commit) # walk down to the commit + .message # read the message +---- +////////////////////////// +---- +pygit2.Repository("/path/to/repo") # リポジトリを開く + .head # 現在のブランチを取得 + .peel(pygit2.Commit) # HEADが指すコミットまで移動 + .message # メッセージを読む +---- + + +////////////////////////// +==== Further Reading +////////////////////////// +==== 参考文献 + +////////////////////////// +Of course, a full treatment of Libgit2's capabilities is outside the scope of this book. +If you want more information on Libgit2 itself, there's API documentation at https://libgit2.github.com/libgit2[], and a set of guides at https://libgit2.github.com/docs[]. +For the other bindings, check the bundled README and tests; there are often small tutorials and pointers to further reading there. +////////////////////////// +もちろん、 Libgit2 の機能の扱い方すべてを取り上げるのは、本書の範囲外です。 +Libgit2 自体についてより多くの情報が必要な場合は、 API ドキュメントが https://libgit2.github.com/libgit2[] にあります。また、ガイドが https://libgit2.github.com/docs[] にあります。 +他のバインディングについては、同梱されている README やテストを見てみてください。ちょっとしたチュートリアルや、参考文献へのポインタが書かれていることがあります。