Edited at

冴えないコミットグラフの育てかた (1) 孤立ブランチ


3行まとめ


  • ファーストコミット(イニシャルコミット)はルートコミットの一種で、ルートコミットは孤立ブランチから生まれる。

  • 孤立ブランチを作成するにはgit checkout --orphan BRANCH_NAMEを使用する。その際、インデックスの状態に注意する。

  • 孤立ブランチをマージするにはgit merge --allow-unrelated-histories BRANCH_NAMEを使用する。


はじめに

本記事は、Gitの「普段は使わないけれど、知っていると便利な場面があるかも」という機能に焦点を当て、Gitの理解をより深めることを目的としています。

今回紹介するのは「孤立ブランチ」(orphan branch)です。ここで言う「孤立」とは、「親コミットを持たないこと」を指します。

孤立ブランチは一般的な用語ではなく、分かりやすさを優先してこの記事向けに定義した用語です。検索する際は「orphan branch」とするのが良いでしょう。

「orphan」は「孤児」、「親のいない子」という意味ですが、幾分日本語の意味が強すぎるので、ここでは「孤立」と表現しています。

また、本記事内では、「親コミットを持たないコミット」を「ルートコミット」(root commit)と呼びます。こちらは一般的な用語かと思います。

git initの直後に行う、いわゆる「ファーストコミット」(first commit)、「イニシャルコミット」(initial commit)も孤立ブランチから生じたルートコミットの一種です。


冴えないコミットグラフ: 孤立ブランチ編

今回作成する冴えない(あまり一般的ではない、一般的には推奨できない)コミットグラフは以下の通りです。

orphan_5.png

上図が示すとおり、複数のルートコミットが存在し、それらがマージされています。

以下、手順を追って解説します。


リポジトリの作成

まずは実験用のリポジトリを作成します。

$ mkdir -p ~/tmp/saenai/orphan

$ cd ~/tmp/saenai/orphan/
$ git init
Initialized empty Git repository in /Users/yuya/tmp/saenai/orphan/.git/

$ git branch -v
# なにも出力されない

$ git log --graph
fatal: your current branch 'master' does not have any commits yet

実行後の状態は、以下の通りです。

orphan_1.png


masterブランチへのコミット

次に、masterブランチにファイルを追加します。これは、ブランチのチェックアウト状態を分かりやすくするためのファイルです。

$ touch master && git add master && git commit -m "add master"

[master (root-commit) 4e6e43e] add master
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 master

$ git branch -v
* master 4e6e43e add master

$ git log --graph
* commit 4e6e43e957d442ee25ad6e6f056f902bfc75f00f (HEAD -> master)
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 13:18:36 2018 +0900

add master

$ ls
master

実行後の状態は、以下の通りです。

orphan_2.png


孤立ブランチの作成とコミット

次に、孤立ブランチを作成し、ファイルを追加します。

git checkout --orphan orphan1とすることで、orphan1という名前の孤立ブランチを作成することができます。

ここで注意が必要なのがインデックス(ステージ)と作業ディレクトリの状態です。孤立ブランチは親コミットを持ちませんが、孤立ブランチを作成した直後のインデックス、作業ディレクトリの状態は開始点(ここではmasterブランチ)と同等になっています。

そのため、そのままgit commitしてしまうと、開始点のファイルが追加されてしまうので注意が必要です。

$ git checkout --orphan orphan1

Switched to a new branch 'orphan1'

$ ls
master

$ git status
On branch orphan1

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: master

$ git reset
$ git clean -f
Removing master

$ touch orphan1 && git add orphan1 && git commit -m "add orphan1"
[orphan1 (root-commit) ea905fa] add orphan1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 orphan1

$ git branch -v
master 4e6e43e add master
* orphan1 ea905fa add orphan1

$ git log --graph
* commit ea905faf56573be38b8b06a7bcd96e415fff9878 (HEAD -> orphan1)
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 13:21:09 2018 +0900

add orphan1

$ ls
orphan1

実行後の状態は、以下の通りです。

orphan_3.png


それぞれのブランチへコミット

次に、masterブランチ、orphan1ブランチにそれぞれコミットを追加し、状態を確認してみます。

$ git checkout master

Switched to branch 'master'

$ echo "A" >> master && git commit -a -m "update master"
[master 744c294] update master
1 file changed, 1 insertion(+)

$ git log --graph
* commit 744c294027194751e3fe6abb5b2eb499a82e4b5a (HEAD -> master)
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 13:32:09 2018 +0900
|
| update master
|
* commit 4e6e43e957d442ee25ad6e6f056f902bfc75f00f
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 13:18:36 2018 +0900

add master

$ git checkout orphan1
Switched to branch 'orphan1'

$ echo "B" >> orphan1 && git commit -a -m "update orphan1"
[orphan1 db750ab] update orphan1
1 file changed, 1 insertion(+)

$ git log --graph
* commit db750ab0270157c0a0f663628e9208d0f757187e (HEAD -> orphan1)
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 13:33:31 2018 +0900
|
| update orphan1
|
* commit ea905faf56573be38b8b06a7bcd96e415fff9878
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 13:21:09 2018 +0900

add orphan1

実行後の状態は、以下の通りです。

orphan_4.png

SourceTreeで見てみると、関連のない(接続されていない)2つのブランチとして存在していることがわかります。

orphan_4_sourcetree.png


孤立ブランチのマージ

最後に、orphan1ブランチをmasterブランチにマージしてみます。

この時、普段通りにgit merge orphan1としてもfatal: refusing to merge unrelated historiesというエラーメッセージが表示され、マージすることができません。

孤立ブランチをマージするためにはgit merge--allow-unrelated-historiesオプションが必要です。読んで字の如く、関連しない履歴を持つコミットのマージを許可するオプションです。

$ git checkout master

Switched to branch 'master'

$ git merge orphan1
fatal: refusing to merge unrelated histories

$ git merge --allow-unrelated-histories orphan1
Merge made by the 'recursive' strategy.
orphan1 | 1 +
1 file changed, 1 insertion(+)
create mode 100644 orphan1

$ git log --graph
* commit d209276330d41ad747723295e25abd6f07213382 (HEAD -> master)
|\ Merge: 744c294 db750ab
| | Author: Yuya Kato <yuyakato@gmail.com>
| | Date: Sat Dec 29 13:35:12 2018 +0900
| |
| | Merge branch 'orphan1'
| |
| * commit db750ab0270157c0a0f663628e9208d0f757187e (orphan1)
| | Author: Yuya Kato <yuyakato@gmail.com>
| | Date: Sat Dec 29 13:33:31 2018 +0900
| |
| | update orphan1
| |
| * commit ea905faf56573be38b8b06a7bcd96e415fff9878
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 13:21:09 2018 +0900
|
| add orphan1
|
* commit 744c294027194751e3fe6abb5b2eb499a82e4b5a
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 13:32:09 2018 +0900
|
| update master
|
* commit 4e6e43e957d442ee25ad6e6f056f902bfc75f00f
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 13:18:36 2018 +0900

add master

実行後の状態は、以下の通りです。

orphan_5.png

SourceTreeで見てみると、orphan1ブランチがmasterブランチにマージされていることが分かります。

orphan_5_sourcetree.png


作例

孤立ブランチを使って、いくつか「冴えないコミットグラフ」を作ってみます。


天涯孤独

ぼっちコミット。masterという名称以外を使ってファーストコミットする例でもあります。

$ mkdir -p ~/tmp/saenai/tengai_kodoku

$ cd ~/tmp/saenai/tengai_kodoku/
$ git init
Initialized empty Git repository in /Users/yuya/tmp/saenai/tengai_kodoku/.git/

$ git checkout --orphan alone
Switched to a new branch 'alone'

$ git commit --allow-empty -m "I'm... empty."
[alone (root-commit) 1729bd7] I'm... empty.

$ git branch -v
* alone 1729bd7 I'm... empty.

$ cat << EOF > .git/hooks/pre-commit
#!/bin/sh
echo "Could you please leave me alone?"
exit 1
EOF

$ chmod +x .git/hooks/pre-commit

$ touch friend && git add friend
$ git commit -m "Hi, let's be friends with me."
Could you please leave me alone?

$ git branch -v
* alone 1729bd7 I'm... empty.

ポイント:



  • git init直後にgit checkout --orphan BRANCH_NAMEすると、master的なブランチの名称を変えることができます。(あとから変えるので十分ですが)


  • git commitコマンドに--allow-emptyオプションを付加すると、内容の無い(空っぽな)コミットを作成することができます。


  • .git/hooks/pre-commitはコミット前に呼び出され、終了コードが0以外の場合はコミットが拒絶されます。


教会

複数の孤立ブランチをマージして、異なる歴史を持つブランチを1つに統合する例。

※ タイトルに深い意味はありません

$ mkdir -p ~/tmp/saenai/church

$ cd ~/tmp/saenai/church/
$ git init
Initialized empty Git repository in /Users/yuya/tmp/saenai/church/.git/

$ touch master && git add master && git commit -m "add master"
[master (root-commit) 1a8a65e] add master
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 master

$ ls
master

$ git checkout --orphan orphan1
Switched to a new branch 'orphan1'

$ git reset
$ git clean -f
$ touch orphan1 && git add orphan1 && git commit -m "add orphan1"
[orphan1 (root-commit) 013a597] add orphan1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 orphan1

$ ls
orphan1

$ git checkout --orphan orphan2
Switched to a new branch 'orphan2'

$ git reset
$ git clean -f
$ touch orphan2 && git add orphan2 && git commit -m "add orphan2"
[orphan2 (root-commit) 0da2d3e] add orphan2
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 orphan2

$ ls
orphan2

$ git checkout master
Switched to branch 'master'

$ git merge --allow-unrelated-histories orphan1
Merge made by the 'recursive' strategy.
orphan1 | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 orphan1

$ git merge --allow-unrelated-histories orphan2
Merge made by the 'recursive' strategy.
orphan2 | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 orphan2

$ ls
master
orphan1
orphan2

$ git log --graph
* commit 7cf7022fd8971d29cc3f49e17d6c2548b27075b1
|\ Merge: e18f799 0da2d3e
| | Author: Yuya Kato <yuyakato@gmail.com>
| | Date: Sat Dec 29 12:42:41 2018 +0900
| |
| | Merge branch 'orphan2'
| |
| * commit 0da2d3ec987f2ae0790e55673c13a4421aac9bd1
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 12:40:02 2018 +0900
|
| add orphan2
|
* commit e18f799ade795e459e4eb91da7a3ce7c127c3b08
|\ Merge: 1a8a65e 013a597
| | Author: Yuya Kato <yuyakato@gmail.com>
| | Date: Sat Dec 29 12:42:25 2018 +0900
| |
| | Merge branch 'orphan1'
| |
| * commit 013a5973e4663c150ed4d244724831a36c04464c
| Author: Yuya Kato <yuyakato@gmail.com>
| Date: Sat Dec 29 12:38:20 2018 +0900
|
| add orphan1
|
* commit 1a8a65e3dae05894076b32de8f89cc265bd11ee8
Author: Yuya Kato <yuyakato@gmail.com>
Date: Sat Dec 29 12:32:23 2018 +0900

add master

church_sourcetree.png

ポイント:



  • git mergeコマンドに--allow-unrelated-historiesオプションを付加することで、孤立ブランチをマージすることができます。


最後に

みなさま、よい年末年始をお過ごしくださいませ🎉


参考