──「心をgitで管理する世界」の物語で、親が二人いる人間は、git cloneでは作れない
〈ショートストーリー〉
家を出る朝、荷物は思ったより少なかった。
子どもの頃の私は、選べなかった。気が小さいのも、すぐ謝る癖も、左の眉だけ上がる笑い方も、物心ついたときにはもう私の履歴に入っていた。最初のほうのコミットには、私の署名がない。けれど確かに私のものだ。誰かが私の名前で、勝手にcommitしたみたいに。
玄関で靴を履く。手首の端末が薄く光った。
git clone ── そうやって私は、この家から複製された。母の声、味つけ、休みの日の匂い。全部いったんまるごと持って、生まれた。
でも、今日からは違う。
「行きます」
母が台所から出てきた。手を拭きながら。何か言いたそうにして、言わなかった。ただ、私を見ていた。
母の手首にも端末がある。その中に、母が持っている「私」がいるはずだ。それがいつ取り込まれた、どのくらい古い私なのか ── 私には見えない。他人のリポジトリは覗けない。母のも、私のも、互いに覗けないまま向かい合っていた。
端末に、最後の確認が出た。
〈upstream(母)からpullしますか?〉
指を、その上で止めた。
pullすれば、まだ少しだけ母の続きを取り込める。今日の小言も、心配も。押さなければ、私のoriginはここで母から切り離れて、独り立ちする。
私は、押さなかった。
代わりに、新しいリポジトリに自分で名前をつけた。私の、origin。ここから先のコミットは全部、私の署名で積む。
「たまには、帰ってきなさいよ」
母が言った。pullして、という意味だと、後になって分かった。そのときはただ、頷いた。
ドアを開けると、朝の光がまっすぐ入ってきた。
私は、手のひらを開いたまま外に出た。まだ何も握っていない手。これからcommitするもので、いっぱいにするはずの手。後ろで、ドアの閉まる音がした。
母の中の私は、たぶんこの朝のまま止まる。
〈技術ノート〉── clone / fork
ノートは答え合わせではない。物語が嘘をついた、ちょうどその場所の地図である。
物語が嘘をついた場所
「git cloneで、この家から複製された」── ここが、入口のために嘘をついている。
cloneは、URLを一つしか取らない。一つの元からまるごと複製する操作だ。だが人間は、一つの元から生まれない。父と母 ── 共通祖先を持たない、二つの無関係な履歴の合流として生まれる。
gitに、それ専用の操作がある。
git merge --allow-unrelated-histories
共通祖先(merge base)を持たない二つのリポジトリを、一つに融合する。生まれてくるcommitは親を二つ持つ(三人以上のoctopus mergeすらある)。「親が二人」は、gitの自然な構造だ。
そしてmerge baseが無いと、gitは差分の基準を持てない。だから重なる箇所は、ほぼ全部conflictする。父の版と母の版が同じ一行 ── 気質、癖、顔の造作 ── を奪い合い、誰かが一行ずつ選ばないとcommitできない。
つまり、あなた自身が、その未解決のresolutionだ。誰も意図して解消しなかった、もつれたままの選択の集積。それが、あなたというmerge commit。
物語が単親のcloneで軽く開いたのは、第一歩を明快にするため。本当のあなたは、二親の--allow-unrelated-histories mergeだ。
正しい名前
| 物語の言葉 | gitの正名 | 補足 |
|---|---|---|
| 家からまるごと複製されて生まれた | clone | 元を一つ取り、その全履歴を複製する |
| 家を出て、独り立ちする | fork → 独立したorigin | 厳密にはforkはforge(ホスト層)の語。git単体では「複製し、元をupstreamとしてremoteに残す」。pullをやめれば、二つはdivergeしていく |
| 最初のほうのコミットに、私の署名がない | author ≠ committer | gitは「書いた人(author)」と「適用した人(committer)」を別に記録する。子ども時代のcommitは、authorが親で、committerがようやくあなた、のようなもの |
| upstream(母)からpullしない | fork元との切断 | upstreamをfetchしなくなれば、母の続きは取り込まれない。関係は、メンテしない限りここから古び始める |
要点
-
cloneは元が一つ。あなたは元が二つ。 正確には人間は
--allow-unrelated-historiesのmerge commitであり、共通祖先が無いぶん、重なる箇所はconflictだらけになる。 - 初期コミットは選べない。 あなたの履歴のいちばん古い層は、他人がauthorで、あなたはそれを引き継いだだけ。
- forkは、pullをやめた瞬間に意味を持つ。 複製しただけでは、まだ親と同じ。取り込むのをやめて初めて、独立した別のoriginになる。