-
1. はじめに
- 1.1 バージョン管理について
- 1.2 Gitの簡単な歴史
- 1.3 Gitとは何か?
- 1.4 コマンドライン
- 1.5 Gitのインストール
- 1.6 Gitの初回セットアップ
- 1.7 ヘルプの取得
- 1.8 まとめ
-
2. Gitの基本
- 2.1 Gitリポジトリの取得
- 2.2 リポジトリへの変更の記録
- 2.3 コミット履歴の表示
- 2.4 操作の取り消し
- 2.5 リモートでの作業
- 2.6 タグ付け
- 2.7 Gitエイリアス
- 2.8 まとめ
-
3. Gitのブランチ
- 3.1 ブランチの基本
- 3.2 基本的なブランチとマージ
- 3.3 ブランチ管理
- 3.4 ブランチングワークフロー
- 3.5 リモートブランチ
- 3.6 リベース
- 3.7 まとめ
-
4. サーバー上のGit
- 4.1 プロトコル
- 4.2 サーバーへのGitの導入
- 4.3 SSH公開鍵の生成
- 4.4 サーバーのセットアップ
- 4.5 Gitデーモン
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティホスティングオプション
- 4.10 まとめ
-
5. 分散型Git
- 5.1 分散型ワークフロー
- 5.2 プロジェクトへの貢献
- 5.3 プロジェクトの管理
- 5.4 まとめ
-
6. GitHub
- 6.1 アカウントのセットアップと設定
- 6.2 プロジェクトへの貢献
- 6.3 プロジェクトの管理
- 6.4 組織の管理
- 6.5 GitHubのスクリプティング
- 6.6 まとめ
-
7. Gitツール
-
8. Gitのカスタマイズ
- 8.1 Gitの設定
- 8.2 Gitアトリビュート
- 8.3 Gitフック
- 8.4 Git強制ポリシーの例
- 8.5 まとめ
-
9. Gitと他のシステム
- 9.1 クライアントとしてのGit
- 9.2 Gitへの移行
- 9.3 まとめ
-
10. Gitの内部
- 10.1 PlumbingとPorcelain
- 10.2 Gitオブジェクト
- 10.3 Git参照
- 10.4 パックファイル
- 10.5 Refspec
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータ復旧
- 10.8 環境変数
- 10.9 まとめ
-
A1. 付録A: 他の環境でのGit
- A1.1 グラフィカルインターフェース
- A1.2 Visual StudioでのGit
- A1.3 Visual Studio CodeでのGit
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMineでのGit
- A1.5 Sublime TextでのGit
- A1.6 BashでのGit
- A1.7 ZshでのGit
- A1.8 PowerShellでのGit
- A1.9 まとめ
-
A2. 付録B: アプリケーションにGitを組み込む
- A2.1 コマンドラインGit
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. 付録C: Gitコマンド
- A3.1 セットアップと設定
- A3.2 プロジェクトの取得と作成
- A3.3 基本的なスナップショット作成
- A3.4 ブランチとマージ
- A3.5 プロジェクトの共有と更新
- A3.6 検査と比較
- A3.7 デバッグ
- A3.8 パッチ適用
- A3.9 メール
- A3.10 外部システム
- A3.11 管理
- A3.12 Plumbingコマンド
A2.2 付録B: アプリケーションへのGitの組み込み - Libgit2
Libgit2
もう一つの選択肢としてLibgit2を使用できます。Libgit2は、他のプログラム内での使用に適したAPIを持つことに重点を置いた、依存関係のないGitの実装です。https://libgit2.orgで見つけることができます。
まず、C APIがどのようなものかを見てみましょう。以下に概要を説明します
// 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リポジトリを開きます。git_repository
型は、メモリ内にキャッシュを持つリポジトリへのハンドルを表します。これは、リポジトリのワーキングディレクトリまたは.git
フォルダへの正確なパスがわかっている場合の最も簡単な方法です。検索オプションを含むgit_repository_open_ext
、リモートリポジトリのローカルクローンを作成するためのgit_clone
とその関連関数、そしてまったく新しいリポジトリを作成するためのgit_repository_init
もあります。
コードの2番目のブロックでは、rev-parse構文(詳細についてはブランチ参照を参照)を使用して、HEADが最終的に指すコミットを取得します。返される型はgit_object
ポインタで、これはリポジトリのGitオブジェクトデータベースに存在するものを表します。git_object
は実際にはいくつかの異なる種類のオブジェクトの「親」型です。各「子」型のメモリレイアウトはgit_object
と同じなので、適切な型に安全にキャストできます。この場合、git_object_type(commit)
はGIT_OBJ_COMMIT
を返すため、git_commit
ポインタに安全にキャストできます。
次のブロックでは、コミットのプロパティにアクセスする方法を示します。ここの最後の行ではgit_oid
型を使用しています。これはLibgit2でのSHA-1ハッシュの表現です。
このサンプルから、いくつかのパターンが見えてきました
-
ポインタを宣言し、その参照をLibgit2呼び出しに渡した場合、その呼び出しは整数エラーコードを返す可能性があります。
0
は成功を示し、それ以外はエラーです。 -
Libgit2がポインタを埋める場合、それを解放する責任はあなたにあります。
-
Libgit2が呼び出しから
const
ポインタを返す場合、それを解放する必要はありませんが、それが属するオブジェクトが解放されると無効になります。 -
C言語での記述は少々骨が折れます。
最後の項目は、Libgit2を使用する際にC言語で記述する可能性はあまり高くないことを意味します。幸いなことに、特定の言語や環境からGitリポジトリを簡単に操作できる、多数の言語固有のバインディングが利用可能です。Libgit2用のRubyバインディングであるRugged(https://github.com/libgit2/ruggedで見つけることができます)を使用して、上記の例を記述したものを見てみましょう。
repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree
ご覧のとおり、コードははるかにすっきりしています。まず、Ruggedは例外を使用しており、エラー状態を通知するためにConfigError
やObjectError
などを発生させることができます。次に、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)
-
新しいファイルのコンテンツを含む新しいブロブを作成します。
-
ヘッドコミットのツリーでインデックスを埋め、パス
newfile.txt
に新しいファイルを追加します。 -
これにより、ODBに新しいツリーが作成され、新しいコミットに使用されます。
-
著者フィールドとコミッターフィールドの両方に同じ署名を使用します。
-
コミットメッセージ。
-
コミットを作成するときは、新しいコミットの親を指定する必要があります。これは、単一の親としてHEADの先端を使用します。
-
Rugged(およびLibgit2)は、コミットを行う際にオプションで参照を更新できます。
-
戻り値は、新しいコミットオブジェクトのSHA-1ハッシュであり、これを使用して
Commit
オブジェクトを取得できます。
Rubyコードはきれいで読みやすいですが、Libgit2が重い処理を行っているため、このコードもかなり高速に実行されます。Rubyistでない場合は、その他のバインディングでいくつかの他のバインディングについて触れています。
高度な機能
Libgit2には、コアGitの範囲外のいくつかの機能があります。その一例がプラグイン性です。Libgit2では、いくつかの種類の操作に対してカスタムの「バックエンド」を提供できるため、通常のGitとは異なる方法でデータを保存できます。Libgit2は、設定、参照ストレージ、オブジェクトデータベースなどに対してカスタムバックエンドを許可しています。
これがどのように機能するかを見てみましょう。以下のコードは、Libgit2チームが提供するバックエンドの例(https://github.com/libgit2/libgit2-backendsで見つけることができます)から借用したものです。オブジェクトデータベース用のカスタムバックエンドのセットアップ方法は次のとおりです。
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)
エラーは捕捉されますが、処理されないことに注意してください。皆様のコードが我々のものよりも優れていることを願います。
-
空のオブジェクトデータベース (ODB) の「フロントエンド」を初期化します。これは、実際の作業を行う「バックエンド」のコンテナとして機能します。
-
カスタムODBバックエンドを初期化します。
-
バックエンドをフロントエンドに追加します。
-
リポジトリを開き、オブジェクトを検索するために我々のODBを使用するように設定します。
しかし、このgit_odb_backend_mine
とは何でしょうか?それはあなた自身のODB実装のためのコンストラクタであり、git_odb_backend
構造を適切に埋める限り、その中でやりたいことは何でもできます。これは次のようになるかもしれません。
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;
}
ここでの最も微妙な制約は、my_backend_struct
の最初のメンバーがgit_odb_backend
構造である必要があることです。これにより、メモリレイアウトがLibgit2コードが期待するものであることが保証されます。残りの部分は任意であり、この構造体は必要なだけ大きくも小さくもできます。
初期化関数は、構造体用のメモリを割り当て、カスタムコンテキストをセットアップし、サポートするparent
構造のメンバーを埋めます。完全な呼び出しシグネチャについては、Libgit2ソースのinclude/git2/sys/odb_backend.h
ファイルを参照してください。特定のユースケースに応じて、どのサポートが必要かを判断するのに役立ちます。
その他のバインディング
Libgit2には多くの言語用のバインディングがあります。ここでは、本稿執筆時点でより完成度の高いバインディングパッケージのいくつかを使用した小さな例を示します。C++、Go、Node.js、Erlang、JVMなど、他の多くの言語用のライブラリも存在し、いずれもさまざまな成熟度段階にあります。公式のバインディングコレクションは、https://github.com/libgit2のリポジトリを参照することで見つけることができます。これから作成するコードは、最終的にHEADが指すコミットからのコミットメッセージを返します(git log -1
のようなものです)。
LibGit2Sharp
.NETまたはMonoアプリケーションを作成している場合、LibGit2Sharp(https://github.com/libgit2/libgit2sharp)が探しているものです。このバインディングはC#で書かれており、生のLibgit2呼び出しをネイティブ感のあるCLR APIでラップするために細心の注意が払われています。例のプログラムは次のようになります。
new Repository(@"C:\path\to\repo").Head.Tip.Message;
デスクトップWindowsアプリケーションの場合、すぐに開始できるNuGetパッケージもあります。
objective-git
アプリケーションがAppleプラットフォームで動作している場合、実装言語としてObjective-Cを使用している可能性が高いです。Objective-Git(https://github.com/libgit2/objective-git)は、その環境向けのLibgit2バインディングの名前です。例のプログラムは次のようになります。
GTRepository *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];
Objective-GitはSwiftと完全に相互運用可能なので、Objective-Cから離れていても心配ありません。
pygit2
Python用のLibgit2バインディングはPygit2と呼ばれ、https://www.pygit2.orgで見つけることができます。例のプログラムは次のようになります。
pygit2.Repository("/path/to/repo") # open repository
.head # get the current branch
.peel(pygit2.Commit) # walk down to the commit
.message # read the message
さらに読む
もちろん、Libgit2の全機能の詳細な説明はこの書籍の範囲外です。Libgit2自体に関するより詳しい情報が必要な場合は、https://libgit2.github.com/libgit2にAPIドキュメントがあり、https://libgit2.github.com/docsにガイド集があります。他のバインディングについては、同梱されているREADMEとテストを確認してください。そこには、小さなチュートリアルやさらに読むべき資料へのポインタがよくあります。