-
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 Packfile
- 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コマンド
7.9 Gitツール - Rerere
Rerere
git rerere
機能は、少し隠れた機能です。この名前は「reuse recorded resolution」(記録された解決の再利用)の略で、その名の通り、一度解決したハンクの競合をGitに記憶させ、次回同じ競合が発生した際に自動的に解決させることを可能にします。
この機能が非常に便利になるシナリオは数多くあります。ドキュメントに記載されている例の一つは、長期にわたるトピックブランチが最終的にきれいにマージされることを確認したいが、中間マージコミットがコミット履歴を乱雑にするのを避けたい場合です。rerere
を有効にすると、時折マージを試み、競合を解決し、その後マージを取り消すことができます。これを継続的に行うと、rerere
がすべてを自動的に処理してくれるため、最終的なマージは簡単になるはずです。
同じ戦術は、ブランチをリベースした状態に保ちたい場合にも使えます。そうすれば、リベースするたびに同じ競合に対処する必要がなくなります。あるいは、マージして多くの競合を修正したブランチを、代わりにリベースすることに決めた場合でも、同じ競合をすべて再度行う必要はおそらくありません。
rerere
のもう一つの応用例は、Gitプロジェクト自体がよく行うように、進化する複数のトピックブランチを時々テスト可能なヘッドにマージする場合です。テストが失敗した場合、競合を再解決することなく、テストを失敗させたトピックブランチなしでマージを巻き戻し、やり直すことができます。
rerere
機能を有効にするには、この設定を実行するだけです
$ git config --global rerere.enabled true
特定のリポジトリに .git/rr-cache
ディレクトリを作成することでも有効にできますが、設定の方がより明確で、その機能をグローバルに有効にできます。
では、以前の例と同様に、簡単な例を見てみましょう。次のような hello.rb
というファイルがあるとします。
#! /usr/bin/env ruby
def hello
puts 'hello world'
end
片方のブランチでは「hello」を「hola」に、もう片方のブランチでは「world」を「mundo」に変更します。これは以前と同じです。

2つのブランチをマージすると、マージ競合が発生します。
$ git merge i18n-world
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Recorded preimage for 'hello.rb'
Automatic merge failed; fix conflicts and then commit the result.
その中に新しい行 Recorded preimage for FILE
があることに気づくでしょう。それ以外は、通常のマージ競合とまったく同じに見えるはずです。この時点で、rerere
はいくつか教えてくれることがあります。通常、競合したものをすべて確認するために、この時点で git status
を実行するかもしれません。
$ git status
# On branch master
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add <file>..." to mark resolution)
#
# both modified: hello.rb
#
しかし、git rerere status
を使うと、git rerere
がマージ前の状態を何として記録したかも教えてくれます。
$ git rerere status
hello.rb
また、git rerere diff
は解決策の現在の状態、つまり解決を始めたものと、解決した後の状態を示します。
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,11 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
+<<<<<<< HEAD
puts 'hola world'
->>>>>>>
+=======
+ puts 'hello mundo'
+>>>>>>> i18n-world
end
また(これは rerere
とは直接関係ありませんが)、git ls-files -u
を使うと、競合したファイルと、競合前、左側、右側のバージョンを見ることができます。
$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3 hello.rb
ここで、puts 'hola mundo'
となるように解決し、再度 git rerere diff
を実行して、rerereが何を記憶するかを確認できます。
$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,7 @@
#! /usr/bin/env ruby
def hello
-<<<<<<<
- puts 'hello mundo'
-=======
- puts 'hola world'
->>>>>>>
+ puts 'hola mundo'
end
つまり、Gitが hello.rb
ファイルで、片側に「hello mundo」、もう片側に「hola world」があるハンクの競合を見つけた場合、「hola mundo」に解決するという意味です。
これで、解決済みとしてマークし、コミットできます。
$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'
「Recorded resolution for FILE」と表示されているのがわかります。

さて、そのマージを取り消し、代わりに master
ブランチの上にリベースしてみましょう。Resetの解明で見たように、git reset
を使うことでブランチを元に戻すことができます。
$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello
マージは取り消されました。次に、トピックブランチをリベースしてみましょう。
$ git checkout i18n-world
Switched to branch 'i18n-world'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: i18n one word
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 i18n one word
期待通り同じマージ競合が発生しましたが、Resolved FILE using previous resolution
の行に注目してください。ファイルを見ると、すでに解決されており、マージ競合マーカーがないことがわかります。
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
また、git diff
はどのように自動的に再解決されたかを示します。
$ git diff
diff --cc hello.rb
index a440db6,54336ba..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
#! /usr/bin/env ruby
def hello
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
end

git checkout
を使って、競合したファイルの状態を再作成することもできます。
$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby
def hello
<<<<<<< ours
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> theirs
end
これの例は高度なマージで見ました。しかし、ここでは、もう一度 git rerere
を実行して再解決してみましょう。
$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
end
rerere
のキャッシュされた解決策を使って、ファイルを自動的に再解決しました。これで、リベースを完了するために、追加して続行できます。
$ git add hello.rb
$ git rebase --continue
Applying: i18n one word
ですから、もし多くの再マージを行う場合や、大量のマージコミットなしでトピックブランチを master
ブランチと同期させたい場合、あるいは頻繁にリベースを行う場合は、rerere
を有効にすると少し楽になります。