前回の記事にちょっと出てきたのですが、gitのコミットログを見た時にハッシュ値の横にgraftedというものを初めて見たので調べてみました。

ここに至る経緯
ざっくりまとめると
- Homebrewで1つ前のバージョンのものをインストールしたかった
brew switchが出来なかったのでgit checkoutでFormulaを古いバージョンに戻そうとした- 該当ファイルの
git logを見たらコミット履歴が1つしかなくて戻せなかった - そのコミットのハッシュ値の隣に
(grafted)の表記があった
という感じ。
ググってみた
率直にgit graftedをキーワードでググって幾つか記事を見てみます。
タイトルの「shallow clone した際の "grafted" なコミットとは一体何ですか?」のコレ感がパない\(^o^)/
このStackOverflowの質問の要約は
- gitで
--depthオプションを使ってshallow cloneするとgraftedマークが付く - ググっても納得行く情報が見つからなかった
git graftsとは違いそうだけど同じ意味をするもの?- コミット履歴を省略しているフラグに過ぎないのか?それとももっと特別な意味がある?
という感じですかね。
ここでわかったのは
git clone --depthするとgraftedマークが付く- その他にも
graftsという概念がgitにはある
ということです。
StackOverflowの回答でも触れられていましたけど、上記の記事の質問に以下のページへのリンクがありました。
超絶ざっくりまとめると
- 異なる開発ラインを結合することが出来る
- 別のSCMからインポートした場合など別リポジトリとの履歴と結合するのに便利
- Git 1.6.5で追加された
git replaceでこれが出来る
ということですかね。なんか間違っていそうですが。。
試してみる
git clone --depth
近々試そうとしているdeployerを使わせてもらって試してみます。
普通にgit cloneしてみます。
$ git clone git@github.com:deployphp/deployer.git Cloning into 'deployer'... remote: Counting objects: 8810, done. remote: Compressing objects: 100% (5/5), done. remote: Total 8810 (delta 0), reused 0 (delta 0), pack-reused 8805 Receiving objects: 100% (8810/8810), 1.85 MiB | 578.00 KiB/s, done. Resolving deltas: 100% (5649/5649), done. $ cd deployer $ git log --graph * commit 006e0b16f3a92025759fc1da30481000e00c0320 (HEAD -> master, origin/master, origin/HEAD) | Author: Martin Supiot <martin@webaaz.com> | Date: Tue Apr 10 09:49:32 2018 +0200 | | fix typo (#1585) | * commit 35027de18ba2eadd6205336961910f0f400e557c | Author: Keith Bremner <kmbremner@gmail.com> | Date: Sat Mar 17 17:39:37 2018 +0000 | | Update shared.php (#1571) | | fix typos | :
ついでに.gitディレクトリ配下を確認してみます。
$ ls .git HEAD description index logs packed-refs config hooks info objects refs
ふむ。
では次はgit clone --depth=1をしてみます。
$ git clone --depth=1 git@github.com:deployphp/deployer.git Cloning into 'deployer'... remote: Counting objects: 218, done. remote: Compressing objects: 100% (199/199), done. remote: Total 218 (delta 36), reused 50 (delta 4), pack-reused 0 Receiving objects: 100% (218/218), 119.99 KiB | 8.57 MiB/s, done. Resolving deltas: 100% (36/36), done. $ git log --graph * commit 006e0b16f3a92025759fc1da30481000e00c0320 (grafted, HEAD -> master, origin/master, origin/HEAD) Author: Martin Supiot <martin@webaaz.com> Date: Tue Apr 10 09:49:32 2018 +0200 fix typo (#1585)
--depth=1とオプションを指定したのでログにはコミットが1つだけとなりHEADにgraftedがつきました。
そして普通にcloneした場合と比較してオブジェクト数が1/40まで減っています!!
先程と同様に.git配下も確認してみます。
$ ls .git HEAD description index logs packed-refs shallow config hooks info objects refs
直下にshallowというファイルが増えましたね。
中身を確認してみます。
$ cat .git/shallow 006e0b16f3a92025759fc1da30481000e00c0320
graftedなポジションのコミットのハッシュ値が記録されているようです。
ローカルのHomebrewのコアリポジトリhomebrew-coreを確認してみたら同じようになっていました。

あまり意識して--depthオプションをつけることがないですが、
大規模なリポジトリを扱う際(かつ過去を振り返らない場合)は有効なオプションですね。
git replace --graft
正直、今回のことがなければgit replaceを使うことはなかったかなと。。
初めて見たのでマニュアルを確認して先程と同様にdeployerリポジトリを使って叩いてみます。
Git - git-replace Documentation
$ git log --graph * commit 006e0b16f3a92025759fc1da30481000e00c0320 (HEAD -> master, origin/master, origin/HEAD) | Author: Martin Supiot <martin@webaaz.com> | Date: Tue Apr 10 09:49:32 2018 +0200 | | fix typo (#1585) | * commit 35027de18ba2eadd6205336961910f0f400e557c | Author: Keith Bremner <kmbremner@gmail.com> | Date: Sat Mar 17 17:39:37 2018 +0000 | | Update shared.php (#1571) | | fix typos | :
コマンドがgit replace --graft {commit}となるのでHEADを指定してみます。
$ git replace --graft HEAD $ git log * commit 006e0b16f3a92025759fc1da30481000e00c0320 (HEAD -> master, replaced, origin/master, origin/HEAD) Author: Martin Supiot <martin@webaaz.com> Date: Tue Apr 10 09:49:32 2018 +0200 fix typo (#1585)
今度はgraftedではなくreplacedとマークされましたね。
マニュアルにAdds a replace reference in refs/replace/ namespace.と記載があったので確認してみます。
$ ls .git HEAD description index logs packed-refs config hooks info objects refs $ ls .git/refs/replace/ 006e0b16f3a92025759fc1da30481000e00c0320
git clone --depthの時と違って.git/shallowではなく.git/refs/replace/{commit}が作成されていました。
まとめ
正直ちょっとよくわからずw
graftedのマークはcloneする時点で対象のオブジェクトが絞られているのでリポジトリ容量はだいぶ軽くなるので、
別リポジトリを参照のためにネストする場合は最新のコミットだけ知っておけば事足りるので活用出来る気がしますがreplacedの使いみちがピンと来ず。。
業務でgitを使っている間にgraftedやreplacedなリポジトリを作ることはなさそうだなーというのが正直なところ。
最近ポッドキャストのsoussuneを一ヶ月遅れで聴いていてその中でgitの話があったのですが、内部構造とかもっと詳しくなるとこの辺ピンとくるのかな。
入門Git買って読むかー(´•ω•`)
- 作者: 濱野純(Junio C Hamano)
- 出版社/メーカー: 秀和システム
- 発売日: 2009/09/24
- メディア: 単行本
- 購入: 31人 クリック: 736回
- この商品を含むブログ (155件) を見る
またわかったら別記事として書こうと思います。眠いので寝るー(ヽ´ω`)
追記
寝ぼけててまとめの文章が超絶おかしいけど、git clone --depth自体はわりと普通にやる行為なんですよね。
ただその際にgraftedとマークが付くことを初めて知ったのとそこがイマイチしっくり来ていません。故にわからず。。
graftを直訳すると接ぎ木という意味なので別リポジトリとジョイントする際にそちら側の履歴を知っておく必要はないのでgraftedになるのかなと思うのですが、
その辺のGitの運用フローがピンときていないのでclone --depthとreplace --graftがふわっとしている感じなのかな。
もう少し調べてわかったら纏めたいと思います。