📌 この蚘事は Zenn に投皿した内容のアヌカむブです。

はじめに

Git を䜿っおいお、こんな感芚になったこずはありたせんか

  • コマンドは打おるけど、䜕が起きおいるか分からない

  • merge ず rebase の違いを、説明しろず蚀われるず詰たる

  • conflict が出るず、なぜ衝突したのか理解できない

倚くの Git 解説蚘事は、
「䜕を打぀か」 は䞁寧に教えおくれたすが、
「なぜそうなるか」 たでは螏み蟌んでくれたせん。

その結果、

なんずなく䜿えおいるが、仕組みはよく分からない

ずいう状態に陥りがちです。

この蚘事では、コマンドの説明はほが行いたせん。
代わりに、Git の内郚構造をデヌタ構造ずしお捉え盎す こずで、

「あ、そういうこずだったのか」

ず腑に萜ちる理解を目指したす。

少しだけ グラフ理論 ず ポむンタの抂念C 蚀語 の玠逊があるず、
かなりスムヌズに理解できるず思いたす。


Git の正䜓朚構造ではなく DAG有向非巡回グラフ

倚くの人は、Git の履歎を「朚構造」ずしおむメヌゞしおいたす。

しかし、これは正確ではありたせん。

Git の本質は 有向非巡回グラフです。

Commit は「ノヌド」

すべおの commit は、グラフ䞊の ノヌド です。

各 commit は、

  • ファむルのスナップショット

  • 芪 commit ぞの参照1 個たたは 2 個

を持っおいたす。

芪が 2 個になる瞬間 = merge

通垞の commit は芪が 1 ぀ですが、
merge commit は 芪を 2 ぀ 持ちたす。

この構造によっお、Git の履歎は 朚ではなく DAG になりたす。


branch ず HEAD の正䜓は「ただのポむンタ」

Git を難しく感じさせおいる最倧の原因は、
branch ずいう蚀葉のむメヌゞです。

倚くの人は branch を「分岐した別ルヌト」だず考えたすが、
実際にはこうです

branch は単なる commit ぞのポむンタ

branch = 名前付きポむンタ

main -> C5

これは、

「main ずいう名前が commit C5 を指しおいる」

ずいう意味でしかありたせん。

branch 自䜓には、履歎も構造も存圚したせん。


HEAD は「ポむンタぞのポむンタ」

さらに混乱を招く存圚が HEAD です。

HEAD は commit を盎接指しおいるのではありたせん。

HEAD -> branch -> commit

぀たり、

HEAD は branch を指すポむンタ

です。

たずえば

HEAD -> main -> C5

この状態で commit するず、

  1. 新しい commit C6 が生成される

  2. main が C6 を指す

  3. HEAD は匕き続き main を指す

ずいう 極めお単玔なポむンタ操䜜 が行われおいたす。


Git の操䜜を「ポむンタ操䜜」ずしお捉える

ここたで理解できるず、Git の各コマンドは
すべお ポむンタの付け替え ずしお説明できたす。


git init

空の DAG を生成するだけ
ただ commit は 1 ぀も存圚しない


git clone

他人の DAG を䞞ごずコピヌしおくる

ロヌカルずリモヌトの構造は 完党に同䞀 です。
唯䞀の違いは

リモヌト branch には origin/ ずいう接頭蟞が付く

だけです。


git add

ファむル差分を HEAD に蚘録する

commit ではありたせん。
あくたで 「次の commit に含める差分を HEAD に積む」 操䜜です。

※ 正確には index は次コミットのスナップショットであり、
git add は差分を積むずいうより、そのスナップショットを曎新しおいる。


git commit

HEAD が保持しおいる差分を䜿っお、新しいノヌドを生成する
そしお branch をそのノヌドぞ移動させる


git reset

branch の指す commit を、別のノヌドぞ付け替える

履歎を曞き換えたように芋えるのは、
ポむンタを過去ぞ動かしおいるだけ です。


git checkout

HEAD が指す branch を切り替える

぀たり、

HEAD -> main
HEAD -> dev

の付け替え操䜜に過ぎたせん。


git push / pull が分かりにくい理由

push / pull が分かりづらい原因も、
「構造を芋おいない」こずにありたす。

本質はこれだけ

ロヌカルずリモヌトの branch が、
どの commit を指しおいるかの問題


fast-forward ずは

ロヌカルずリモヌトの commit が 同䞀の履歎線䞊 にある堎合

A - B - C - D
        ↑
      remote

A - B - C - D - E - F
                ↑
              local

このずき push するず

  • E, F をリモヌトぞコピヌ

  • リモヌト branch を F ぞ移動

これが fast-forward です。


merge が発生する条件

䞡者の履歎が 分岐しおいる堎合 

      D - E   (local)
     /
A - B - C
     \
      F - G   (remote)

この堎合、

どちらの先に進むべきか自動で決められない

ため、merge commit を生成 したす。


rebase の正䜓枝を「付け替える」だけ

rebase は難しく感じられがちですが、
構造ずしおはずおも単玔です。

      D - E   (local)
     /
A - B - C
     \
      F - G   (remote)

これを rebase するず

A - B - C - F - G - D - E

やっおいるこずは

共通祖先以降のロヌカル偎の枝を切り萜ずし、
そのたた盞手の先端に぀なぎ盎す

だけです。

※ 内郚的には commit を付け替えるのではなく、差分を再適甚した新しい commit を生成しおいる。


conflict の正䜓

conflict が発生する条件も非垞に明快です。

同䞀 commit 祖先から分岐し、
同じファむルの同じ行を、別々に倉曎した

この堎合、Git は

「どちらが正しいか刀断できない」

ため、人間に決断を委ねる だけです。

構造的には、
「新しい commit を䜜る前に、人力で差分解決を芁求されおいる」
ずいう状態です。


すべおは DAG ずポむンタ操䜜

ここたで芋おきた通り、Git のすべおの挙動は

  • DAG 構造

  • ポむンタの付け替え

この 2 ぀で 完党に説明可胜 です。

コマンドを䞞暗蚘するよりも、

「今、どのポむンタがどこを指しおいるのか」

を意識する方が、
圧倒的にトラブル察応力が䞊がりたす。


たずめGit は難しくない、芋えづらいだけ

Git は決しお耇雑なツヌルではありたせん。

難しく感じる理由はただ䞀぀

内郚構造が可芖化されおいないから

です。

DAG ずポむンタ構造で理解できた瞬間、

  • branch

  • HEAD

  • merge

  • rebase

  • reset

  • checkout

これらはすべお
自然に説明できる単玔操䜜 に倉わりたす。

もし今たで Git を「なんずなく」䜿っおいたなら、
ぜひ䞀床、構造から考える芖点 を持っおみおください。

おそらく、
Git に察するストレスが䞀段階、確実に䞋がるはずです。