Skip to content
Tayakorn
Git Handbook

Git Handbook

LEVEL 4 · ระดับเทพ

กลไกภายในของ Git

บทนี้คือเส้นแบ่งระหว่าง "ใช้ Git เป็น" กับ "เข้าใจ Git" เมื่อเห็นว่าข้างในมันเก็บข้อมูลอย่างไร คำสั่งทุกบทก่อนหน้าจะกระจ่างขึ้นพร้อมกัน และคำถามกวนใจอย่าง "ทำไม commit ถึงแก้ไม่ได้" หรือ "ทำไม branch ถึงสร้างเร็วจัง" จะมีคำตอบที่ชัดเจน

12.1 เปิดดูข้างใน .git/

โฟลเดอร์ .git/ ที่ init สร้างขึ้น ไม่ใช่กล่องดำ — มันคือฐานข้อมูลที่เปิดดูได้ ข้างในมี objects/ (ที่เก็บข้อมูลจริงทั้งหมด), refs/ (ป้าย branch และ tag), และไฟล์ HEAD หัวใจของทั้งหมดคือประโยคเดียว:

Git คือฐานข้อมูลที่ค้นด้วยเนื้อหา (content-addressable)

หมายความว่า Git เก็บทุกชิ้นข้อมูลโดยใช้ SHA ที่คำนวณจากเนื้อหานั้นเอง เป็นกุญแจ ให้เนื้อหา → ได้กุญแจ → เก็บ/หยิบด้วยกุญแจนั้น เนื้อหาเดียวกันได้กุญแจเดียวกันเสมอ จึงไม่มีวันเก็บซ้ำ

12.2 วัตถุสี่ชนิดที่ประกอบเป็น Git ทั้งหมด

commitใคร · เมื่อไร · ข้อความชี้ไป tree + พ่อแม่treeสารบบไฟล์/โฟลเดอร์(เหมือน directory)blobเนื้อหาไฟล์ (ดิบ ๆ)tree ย่อยโฟลเดอร์ข้างในroot treefile→ commit ก่อนหน้า
FIG 12.1 blob เก็บเนื้อหาไฟล์ · tree คือสารบบที่จับคู่ชื่อไฟล์กับ blob (และ tree ย่อยสำหรับโฟลเดอร์) · commit ชี้ไป tree หนึ่งอัน (= ภาพถ่ายทั้งโปรเจกต์) บวกข้อมูลผู้เขียนและ commit ก่อนหน้า · ชนิดที่สี่คือ tag แบบ annotated ที่ชี้ commit พร้อมข้อความ

ลองพิสูจน์ด้วยตาเอง — แกะ commit ออกมาดู:

$ git cat-file -p HEAD
tree a1b2c3... ภาพถ่ายโปรเจกต์ commit นี้
parent 3b7d8c1... commit ก่อนหน้า
author Khaofang <...> 1718...

เพิ่มหน้า login

12.3 ไขปริศนาที่ค้างคามาทั้งเล่ม

  • ทำไม commit แก้ไม่ได้? เพราะ SHA ของมันคำนวณจากเนื้อหา (รวมถึง tree และ parent) แก้อะไรนิดเดียว SHA เปลี่ยน กลายเป็น commit คนละดวงทันที ของเดิมยังอยู่ — "แก้ประวัติ" จึงคือสร้างของใหม่มาแทน เสมอ
  • ทำไม branch ถึงเบาและเร็ว? เพราะมันเป็นแค่ไฟล์ข้อความใน refs/heads/ ที่เก็บ SHA หนึ่งบรรทัด สร้าง branch = เขียนไฟล์ 40 ตัวอักษร ไม่ได้ก๊อปอะไรเลย
  • ทำไมไฟล์ที่ไม่เปลี่ยนถึงไม่กินที่เพิ่ม? เพราะ blob เดิมมี SHA เดิม commit ใหม่แค่ชี้ blob เดิมซ้ำ ไม่เก็บใหม่
  • reflog/reset กู้ของได้เพราะอะไร? commit ที่ "หาย" แค่ไม่มีป้ายชี้ แต่ object ยังอยู่ใน objects/ จนกว่าจะถูกเก็บกวาด — รู้ SHA ก็ดึงกลับได้

12.4 packfiles — ทำไม repo ใหญ่ ๆ ถึงไม่บวม

ถ้าเก็บทุก blob แยกไฟล์จริง ๆ repo ที่มีประวัติยาวคงโตมหาศาล Git จึงบีบอัด object จำนวนมากเข้า packfile เดียวเป็นระยะ ๆ (ตอน gc หรือ push) โดยเก็บเฉพาะส่วนต่างระหว่าง object ที่คล้ายกัน — ได้ประโยชน์ของ snapshot ในการใช้งาน แต่ประหยัดที่แบบ diff ในการจัดเก็บ

สรุปบทที่ 12 — Git = ฐานข้อมูลค้นด้วยเนื้อหา เก็บทุกอย่างด้วย SHA ของเนื้อหานั้น · วัตถุสี่ชนิด: blob (เนื้อไฟล์), tree (สารบบ), commit (tree + ผู้เขียน + parent), tag · commit แก้ไม่ได้เพราะ SHA ผูกกับเนื้อหา · branch เบาเพราะเป็นแค่ไฟล์เก็บ SHA · ของที่ "หาย" ยังอยู่จนถูกเก็บกวาด · packfile บีบอัดเพื่อประหยัดที่

Read the full book