Git internal

Miles
odds.team
Published in
3 min readJun 3, 2024

สวัสดีครับ วันนี้ผมอยากมาพูดถึง version control ที่เชื่อว่าทุกคนต้องรู้จัก นั่นคือ git นั่นเอง โดยเฉพาะเหล่าโปรแกรมเมอร์ที่ต้องทำงานอยู่กับโค้ดซึ่งน่าจะผ่านมือกันมาพอสมควร เนื่องจากในช่วงที่ผ่านมาผมได้มีโอกาสไปศึกษาการทำงานของ git มาเล็กน้อย เลยอยากนำความรู้มาแชร์ให้กับคนที่สนใจและเก็บไว้เผื่อตัวเองมาอ่านทบทวนด้วยแหละ โดยสำหรับหัวข้อในวันนี้เราจะมาดูกันว่า git เก็บข้อมูลยังไงกันบ้าง ต้องเก็บแต่ละบรรทัดเพื่อดูกันเลยไหม แล้วจะประกอบกันได้ยังไงกัน

Hash function

มาดูส่วนประกอบส่วนที่เป็นสิ่งสำคัญที่สุดของ git กันก่อน นั่นคือ hash function
hash function คือ function ที่รับค่าใดๆ มาก็ตามแต่ และให้ผลลัพธ์ออกมาในขนาดที่กำหนดไว้ ยกตัวอย่าง Sha1 หากนำคำว่า “git version control” มาคำนวณจะได้ค่า ecd8b181ffb55257889847b998fe73dc784d5539 หรือคำว่า “git” ก็จะได้ค่า 46f1a0bd5592a2f9244ca321b129902a06b53e03 ซึ่งต่างกันออกไป โดยมีจุดสำคัญจุดหนึ่ง คือแม้ถึงว่า input จะมีค่าใกล้เคียงกันมากๆ แต่ก็จะให้ผลลัพธ์ที่ต่างกันโดยสิ้นเชิง

// "git version control"
f12d2312aeb2f6e0f13256a00c0a9c132a575da1
// "git version control."
ecd8b181ffb55257889847b998fe73dc784d5539

ด้วยจุดนี้ เราสามารถนำมันมาใช้สร้างความ unique ของแต่ละ input ได้ ซึ่งสำหรับพวกเราที่ใช้ git ก็คือ source code ที่เปลี่ยนไปนั่นเอง

fn main() {
println!("hello git");
}
// sha1: a8ba88870c0de4618fde5f6ffcfdccfad2b098a2

fn main() {
println!("hello world");
}
// sha1: ef243f920fa5bca9cdc53897302e54a4a908bd14

Tree

tree คือ data structure แบบหนึ่งเก็บข้อมูลในรูปแบบคล้ายต้นไม้ ซึ่งเก็บข้อมูลเป็น node ประกอบด้วย root node เป็น node ที่ไม่มี parent ( node ก่อนหน้า ), internal node เป็น node ที่มี child node อย่างน้อย 1 node และ leaf node เป็น node ที่ไม่มี child node

การใช้ tree ในการแสดงข้อมูลจะทำให้ได้ผลคล้ายกับการแสดงผลของ directory ซึ่งมีการแสดงผลเป็นลำดับขั้น ทำให้สามารถใช้ tree ในการช่วยเก็บข้อมูลของ directory ได้

เพียงมี 2 อย่างนี้เราก็สามารถสร้าง database สำหรับเก็บ version ของไฟล์ได้แล้ว

Git object

มาดูภาพของ git แบบ high level กัน ตัว git นั้น จะทำการเก็บไฟล์ไว้ในรูปแบบของ object โดยมีชื่อของ object เป็น hash ของ object นั้นๆ ซึ่งชนิดหลักๆ ของ object ได้แก่ blob, tree, commit

  • blob ใช้สำหรับเก็บไฟล์แต่ละไฟล์
  • tree ใช้สำหรับเก็บข้อมูลของ directory หรือเป็น snapshot ของ repo ณ จุดๆ นั้น โดยประกอบด้วยชื่อไฟล์ และ hash ของ tree หรือ blob
  • commit ใช้สำหรับเก็บข้อมูล commit ได้แก่ tree หรือ snapshot ของ commit นั้น, commit ก่อนหน้า ผู้ commit และ message

มาลองสร้างสถานการณ์สมมุติกัน ถ้าเรามี object ดังนี้ ขอย่อชื่อไฟล์ให้สั้นลงหน่อยนะครับ จะได้ดูง่ายๆ

blob e1733 greeting.txt
tree a6f85 src
tree 5bd08 sub
blob 04204 config
tree c97e8 header
blob 9daea main
blob 17d5d text.txt
blob 32814 conf
blob 8e83f head
tree d0a16 . // root node

ภาพด้านบนคือ object ทั้งหมด และ tree ที่แสดง snapshot ของ repo ในขณะนั้น จะเห็นได้ว่าส่วนที่ internal node จะแสดงแทน directory และส่วนที่เป็น leaf node ก็จะใช้แสดงแทนไฟล์ของเรา ทีนี้เราลองมาแก้ไขไฟล์กันดูแล้วสังเกตุการเปลี่ยนแปลงของ tree นี้กัน โดยจะขอลองแก้ไฟล์ main ใน src นะครับ

blob e1733 greeting.txt
tree a6f85 src
tree 5bd08 sub
blob 04204 config
tree c97e8 header
blob 9daea main
blob 17d5d text.txt
blob 32814 conf
blob 8e83f head
tree d0a16 . // root node

tree 450f3 src
blob 66968 main
tree 400b1 . // root node current

จะเห็นว่ามี object ใหม่เพียง 3 object เพราะว่าเราแก้ main object ซึ่งจะส่งผลต่อไปแค่ src และ root tree เท่านั้น ส่วน object อื่นๆ ไม่มีการเปลี่ยนแปลงจึงไม่ต้องสร้าง object เพิ่ม

เท่านี้เราก็จะได้ version control แบบง่ายๆ แล้ว แต่ว่ายังขาดไปอีกหนึ่งอย่าง ถึงแม้ว่าจะมีแค่ tree ก็สามารถ ref กลับไปยัง snapshot นั้นๆ ได้แล้ว แต่ว่าเราไม่มีข้อมูลเลยว่า ใครเป็นคนแก้ไข ตอนไหน เพื่ออะไร เพราะงั้นเราเลยจะมี commit object เพิ่ม เพื่อเก็บข้อมูลเหล่านี้ไว้นั่นเอง โดย commit ก็จะเก็บแค่ tree ณ ตอนนั้น แล้วก็ commit ก่อนหน้าถ้ามี พร้อมกับข้อมูลรายละเอียดอื่นๆ

เท่านี้เราก็ได้รู้กันแล้วว่า git มีวิธีการเก็บไฟล์ยังไง นั่นก็คือเก็บไฟล์ไว้ทั้งไฟล์นั่นเลย โดยใช้เทนนิคการ hash และใช้ tree data structure ในการช่วยแสดงผล ซึ่งโดยส่วนตัวผมแล้ว เป็น concept ที่ง่ายไม่ซับซ้อน แต่ใช้งานได้ผลอย่างน่าทึ่ง

สำหรับเนื้อหาในวันนี้ก็หมดเพียงเท่านี้ครับหวังว่าจะพอให้ได้ความรู้กลับกันไปบ้างเล็กๆ น้อยๆ ผมมีแผนจะอธิบายในส่วนของ git object เพิ่มเติม ก็อาจจะได้มาอ่านกันในบทความต่อๆ ไปกัน ขอบคุณสำหรับทุกคนที่สละเวลามาอ่านกันนะครับบ

จะว่าไปแอบสารภาพว่าจริงๆ ผมโกหกที่บอกว่า git เก็บ object ไว้ทั้งไฟล์เลย … git มีเทคโนโลยีหนึ่งที่เรียกว่า packfile ซึ่งเป็นวิธีที่ใช้ในการส่งไฟล์ต่างๆ ไปยัง remote repo นั่นล่ะ ในเรื่องนี้ผมคิดว่าน่าจะมีโอกาสได้มาพูดถึงกันต่อเช่นกันน

--

--