こんにちは、SJC共同推進室の北谷です。
ソフトウェア開発に携わっている皆さんは普段Gitを活用されていますか。Gitってたくさんのコマンドやオプションがあり、とても全部は把握しきれないので、その中で開発に必要な使い方を覚えて使うというスタイルかと思います。
しかし、ドキュメントを見ているとこれは何だろうと思うところはたくさんあります。この記事ではその一つだけ、取り上げてみます。実用として役に立つというほどの情報ではないですが、興味を持たれた方はお付き合いください。
Tree-ish
取り上げるのは”tree-ish” です。コマンドの引数として所々で出てきます。単語に “-ish” がつくと「…っぽい」というような意味になります。”Pink-ish” だと「ピンクがかった」みたいに。なので “tree-ish”は「ツリーっぽい」。そうだとするとディレクトリツリーかなって思いますよね。
一例として git checkout の SYNOPSIS (https://git-scm.com/docs/git-checkout) を見てみると以下のように、5番目、6番目の用法に tree-ish が現れています。あと、時々お世話になる git reset にも出てきます。
1 2 3 4 5 6 7 8 9 |
git checkout [-q] [-f] [-m] [<branch>] git checkout [-q] [-f] [-m] --detach [<branch>] git checkout [-q] [-f] [-m] [--detach] <commit> git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>] git checkout [-f] <strong><tree-ish></strong> [--] <pathspec>… git checkout [-f] <strong><tree-ish></strong> --pathspec-from-file=<file> [--pathspec-file-nul] git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>… git checkout [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul] git checkout (-p|--patch) [<strong><tree-ish></strong>] [--] [<pathspec>…] |
この5番目、6番目の機能の説明は次のようになっています
1 2 3 |
pathspecにマッチするファイルの内容を上書きする。 <tree-ish>(ほとんどの場合はコミット)が与えられなければ、ワーキングツリーをインデックスで上書きする。 <tree-ish>がある場合は、インデックスとワーキングツリーを<tree-ish>の内容で上書きする。 |
tree-ish にはほとんどの場合はコミットを示すものを指定するのだ、と書いてあります。しかし、ちょっと不思議です。そもそもなぜ “tree-ish” (ツリーのようなもの) といった言い回しをするのでしょうか。また、コミットでないものもあるのでしょうか。
まず見るべきはもちろん Git ドキュメントの用語集の中の “tree-ish” の説明でしょう。見てみると、次のように書いてあります (https://git-scm.com/docs/gitglossary/ja#def_tree-ish):
1 2 3 4 5 6 |
ツリーの性質を持つもの (tree-ish (also treeish)) ツリーオブジェクトを再帰的に 参照外し できる ツリーオブジェクト または オブジェクト。 コミットオブジェクト の参照先を展開すると、 リビジョン の最上位 ディレクトリ に対応 するツリーオブジェクトが生成されます。以下はすべてツリーの性質を持つものです: コミットの性質を持つもの、 ツリーオブジェクト、ツリーオブジェクトを指す タグオブジェクト、 ツリーオブジェクトを指すタグオブジェクトを指すタグオブジェクトなど。 |
最初の文が定義のようですが、なんだか難しいですね。出てきている用語について一つ二つ見て行きましょう。
オブジェクト
実はオブジェクトは Gitの内部構造の基本です。いろんなところで説明されていますので、ここではざっくりとだけ触れますが、詳しくはこのあたりを参照していただければと思います。
- https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88
- https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
ざっくりいうと、ファイルも、ディレクトリもリビジョンも、コミットも、タグもみんなオブジェクトの1種です。そして、それぞれに SHA IDがついていて、他のオブジェクトを参照しています。
上のサイトからの図を貼付します。
(出典:Pro Git book, https://git-scm.com/book/en/v2/Git-Internals-Git-Objects)
ひとつ目は、オブジェクトがファイルツリーを構成している図、二つ目はそれがコミットによって更新された状態の図です。この中のそれぞれの箱がオブジェクトです:
- グレー:ファイルに相当するオブジェクト = blobオブジェクト
- 緑:ディレクトリに相当するオブジェクト = ツリーオブジェクト
- オレンジ:コミットに相当するオブジェクト = コミットオブジェクト
このように、オブジェクトは他のオブジェクトを参照していることがわかります。緑の四角がいわゆるツリーオブジェクトで、ディレクトリに相当するデータです。リビジョンが違うと同じディレクトリでも違うオブジェクトになります。
そして、Tree-ish のツリーはきっとこのツリーオブジェクトだなと見当がつきます。
参照外し
Tree-ish の説明に出てくるこの用語がひとつ混乱の元であるように思います。
英語では “dereference” です。同じく用語集を見てみます ( https://git-scm.com/docs/gitglossary/ja#def_dereference) と、残念ながら日本語訳はありません。
この説明を読むと、dereference は次のような意味にとれます:
- シンボリック参照、タグオブジェクト、コミットオブジェクトから、(参照を辿って)それらが指すオブジェクトにアクセスすること
参照先のそのまた参照先といった具合に再帰的に辿ることがあり、その場合一つ参照を辿るごとに残りの参照が減っていくので、「dereference=参照外し」と呼ぶのではないかと思います。
Tree-ish
では本題の Tree-ish に戻ります。
tree-ish の説明の原文 ( https://git-scm.com/docs/gitglossary#def_tree-ish) に戻ってみます。最初の1文:
- A tree object or an object that can be recursively dereferenced to a tree object.
結局これが tree-ish の説明なのですが、上記で見てきた内容を踏まえて少し簡略化して訳すとこんな感じにできるかと思います:
- tree-ish は参照を辿ることでツリーオブジェクトを得られるオブジェクトのこと
そして tree-ish を引数に取る git コマンドは、その tree-ish からツリーオブジェクトまで辿って、そのツリーオブジェクトを処理の対象にします。コミットIDもその1例である、と解釈しました。
これで、もやもや感も解消した気がします。
ちなみに、実際に tree-ish に指定できるものは、以下のものがあるそうです (commit-ish も一緒に書いてあります):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
---------------------------------------------------------------------- | Commit-ish/Tree-ish | Examples ---------------------------------------------------------------------- | 1. <sha1> | dae86e1950b1277e545cee180551750029cfe735 | 2. <describeOutput> | v1.7.4.2-679-g3bee7fb | 3. <refname> | master, heads/master, refs/heads/master | 4. <refname>@{<date>} | master@{yesterday}, HEAD@{5 minutes ago} | 5. <refname>@{<n>} | master@{1} | 6. @{<n>} | @{1} | 7. @{-<n>} | @{-1} | 8. <refname>@{upstream} | master@{upstream}, @{u} | 9. <rev>^ | HEAD^, v1.5.1^0 | 10. <rev>~<n> | master~3 | 11. <rev>^{<type>} | v0.99.8^{commit} | 12. <rev>^{} | v0.99.8^{} | 13. <rev>^{/<text>} | HEAD^{/fix nasty bug} | 14. :/<text> | :/fix nasty bug ---------------------------------------------------------------------- | Tree-ish only | Examples ---------------------------------------------------------------------- | 15. <rev>:<path> | HEAD:README, :README, master:./README ---------------------------------------------------------------------- | Tree-ish? | Examples ---------------------------------------------------------------------- | 16. :<n>:<path> | :0:README, :README ---------------------------------------------------------------------- |
出典: https://stackoverflow.com/questions/4044368/what-does-tree-ish-mean-in-git
おわりに
調べる過程で学んだオブジェクトについては、Git をもう1段理解するためには必要な知識でした。Tree-ish はたまたま目について取り上げましたがかなり重箱の隅的なトピックではあります。次はもうちょっと使える部分を掘ってみたいと思います。
以上、長くなりましたがお付き合いありがとうございました。