13 คะแนน โดย GN⁺ 2025-08-16 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • โปรเจกต์ Git เพิ่งเริ่มลงมือแก้ปัญหาการจัดการไฟล์ขนาดใหญ่อย่างเป็นทางการด้วยตัวเอง
  • Git LFS เป็นเพียงทางออกชั่วคราวที่สร้างต้นทุนหลายด้านและการผูกติดกับผู้ให้บริการสำหรับผู้ใช้
  • ช่วงหลังฟีเจอร์ partial clone ทำให้ Git เพียงอย่างเดียวสามารถทดแทนบทบาทส่วนใหญ่ของ LFS ได้แล้ว
  • ต่อจากนี้ โซลูชันใหม่ชื่อ large object promisor ก็กำลังเตรียมถูกรวมเข้าใน Git อย่างเป็นทางการ
  • จากการเปลี่ยนแปลงเหล่านี้ แนวโน้มคือคำตอบระยะยาวของการจัดการไฟล์ขนาดใหญ่จะกลับมาจบที่ Git เอง ไม่ใช่ส่วนขยายภายนอก

ปัญหาไฟล์ขนาดใหญ่ของ Git และการเปลี่ยนแปลง

  • ถ้าจะบอกว่าศัตรูตัวฉกาจที่สุดของ Git คือ ไฟล์ขนาดใหญ่ ก็คงไม่ผิด
  • ไฟล์ขนาดใหญ่ทำให้รีโพซิทอรีของ Git พองตัวขึ้น ทำให้ git clone ช้าลง และส่งผลเสียต่อสภาพแวดล้อมโฮสติ้งส่วนใหญ่ด้วย

การมาของ Git LFS และข้อจำกัด

  • ในปี 2015 GitHub เปิดตัว Git LFS เพื่อหลีกเลี่ยงปัญหาไฟล์ขนาดใหญ่
  • แต่ตัว Git LFS เองก็เพิ่มความซับซ้อนใหม่และค่าใช้จ่ายด้านสตอเรจเข้าไป
  • ชุมชน Git เฝ้าคิดเรื่องการแก้ปัญหารากฐานของไฟล์ขนาดใหญ่มาอย่างเงียบ ๆ และในรีลีสทางการล่าสุดของ Git ก็เริ่มเห็นทิศทางใหม่สำหรับการจัดการไฟล์ขนาดใหญ่โดยไม่ต้องพึ่ง LFS

วิธีที่ทำได้ตั้งแต่วันนี้: แทนที่ Git LFS ด้วย partial clone

  • หลักการของ partial clone

    • Git LFS: เก็บไฟล์ขนาดใหญ่ไว้นอกรีโพซิทอรี แล้วดาวน์โหลดเฉพาะไฟล์ที่ต้องใช้มาทำงาน
    • Git partial clone (เริ่มใช้ในปี 2017):
      • ใช้ออปชัน --filter เพื่อโคลนโดยตัด blob ที่มีขนาดเกินค่าที่ต้องการออก
      • ดาวน์โหลดไฟล์ขนาดใหญ่ที่เกี่ยวข้องจากเซิร์ฟเวอร์เมื่อจำเป็นเท่านั้น

      เมื่อใช้ Partial clone คุณไม่จำเป็นต้องดาวน์โหลด [large binary assets] ล่วงหน้าระหว่าง Clone และ Fetch จึงช่วยลดเวลาในการดาวน์โหลดและการใช้พื้นที่ดิสก์ได้

      โฆษณา
  • จุดร่วมของ partial clone กับ LFS

    • 1. ลดขนาดตอน checkout ให้เล็กที่สุด: รับมาเฉพาะเวอร์ชันล่าสุดและละประวัติไฟล์ทั้งหมด
    • 2. โคลนได้เร็ว: เพราะไม่มีการส่งไฟล์ขนาดใหญ่ระหว่าง clone
    • 3. ตั้งค่าได้เร็ว: ต่างจาก shallow clone ตรงที่ยังเข้าถึงประวัติทั้งหมดของโปรเจกต์ได้
  • ตัวอย่างการใช้ partial clone

    • ตัวอย่างความเร็วในการโคลนและการใช้ดิสก์ของรีโพที่มีประวัติไฟล์ PNG ขนาดใหญ่จำนวนมาก:
      • โคลนแบบปกติใช้เวลาเกือบ 4 นาที และกินพื้นที่ 1.3GB
      • ถ้าใช้ partial clone และจำกัด blob ที่ 100KB จะโคลนเสร็จใน 6 วินาที และใช้พื้นที่ 49MB
      • เทียบกับต้นฉบับแล้ว ความเร็วในการโคลนดีขึ้น 97% และขนาดตอน checkout ลดลง 96%
      โฆษณา
  • ข้อจำกัดของ partial clone

    • หากต้องใช้ข้อมูลที่ถูกกรองออกไป (เช่น git diff, git blame, git checkout) Git จะร้องขอไฟล์จากเซิร์ฟเวอร์
    • นี่เป็นลักษณะเดียวกันกับ Git LFS
    • ในการทำงานจริง การไปใช้ blame กับไฟล์ไบนารีเกิดขึ้นไม่บ่อย

ปัญหาของ Git LFS

  • การผูกติดกับผู้ให้บริการสูง: อิมพลีเมนเทชันของ GitHub รองรับเฉพาะเซิร์ฟเวอร์ของตัวเอง ทำให้เกิดค่าใช้จ่ายและการผูกติด
  • ปัญหาเรื่องต้นทุน: เก็บ 50GB บน GitHub LFS มีค่าใช้จ่าย $40 ต่อปี ขณะที่ Amazon S3 อยู่ที่ $13
  • ย้อนกลับได้ยาก: เมื่อย้ายไปใช้ LFS แล้ว จะไม่สามารถกลับคืนสภาพเดิมได้หากไม่ rewrite history
  • ต้นทุนการตั้งค่าอย่างต่อเนื่อง: ผู้ร่วมงานทุกคนต้องติดตั้ง LFS หากไม่ติดตั้งจะเห็นเป็นไฟล์เมตาดาต้าแทนไฟล์จริง ซึ่งทำให้สับสน

แนวโน้มในอนาคต: Large Object Promisor

  • ไฟล์ขนาดใหญ่สร้างปัญหาเรื่องต้นทุนให้กับแพลตฟอร์มโฮสติ้งอย่าง GitHub และ GitLab เช่นกัน
  • Git LFS ช่วยลดต้นทุนเซิร์ฟเวอร์ด้วยการ offload ไฟล์ขนาดใหญ่ไปยัง CDN
  • Large Object Promisor คืออะไร

    • เมื่อต้นปีนี้ Git ได้ merge ฟีเจอร์ชื่อ large object promisor เข้ามาอย่างเป็นทางการ
    • ฟีเจอร์นี้ช่วยลดภาระสตอเรจฝั่งเซิร์ฟเวอร์คล้ายกับ LFS แต่ลดความซับซ้อนฝั่งผู้ใช้ลงได้มาก

      ความพยายามนี้มีจุดมุ่งหมายเพื่อปรับปรุงงานฝั่งเซิร์ฟเวอร์ โดยเฉพาะงานที่เกี่ยวข้องกับ blob ขนาดใหญ่ซึ่งถูกบีบอัดในรูปแบบไบนารีอยู่แล้ว
      เป็นโซลูชันที่ใช้แทน Git LFS ได้
      Large Object Promisors, git-scm.com

      โฆษณา
  • หลักการทำงาน

    • 1. ผู้ใช้ push ไฟล์ขนาดใหญ่ไปยัง Git host
    • 2. โฮสต์ทำการ offload ไฟล์ขนาดใหญ่ไปยัง promisor backend
    • 3. ตอน clone นั้น Git host จะให้ข้อมูล promisor แก่ไคลเอนต์
    • 4. ไคลเอนต์จะดึงไฟล์ขนาดใหญ่จาก promisor นั้นโดยอัตโนมัติเมื่อจำเป็น
  • สถานะการนำมาใช้และโจทย์ที่ยังเหลือ

    • promisor สำหรับไฟล์ขนาดใหญ่ยังอยู่ระหว่างการพัฒนา และมีบางส่วนของโค้ดถูก merge เข้า Git ในเดือนมีนาคม 2025
    • GitLab และที่อื่น ๆ กำลังคุยกันเรื่องการอิมพลีเมนต์เพิ่มเติมและประเด็นที่ยังไม่คลี่คลาย
    • กว่าจะมีการใช้งานอย่างแพร่หลายยังต้องใช้เวลาอีกพอสมควร
    • ในระยะสั้น การเก็บไฟล์ขนาดใหญ่ยังคงหลีกเลี่ยงการพึ่ง Git LFS ได้ยาก
    • หาก promisor ถูกใช้อย่างแพร่หลาย ก็มีแนวโน้มว่า GitHub จะสามารถอัปโหลดไฟล์ที่เกิน 100MB ได้ด้วย

สรุป: อนาคตของไฟล์ขนาดใหญ่ใน Git คือ Git

  • โปรเจกต์ Git ยังคงครุ่นคิดเรื่อง ปัญหาไฟล์ขนาดใหญ่ แทนทุกคนอย่างต่อเนื่อง
  • ตอนนี้ยังคงจำเป็นต้องใช้ Git LFS อยู่
  • แต่เมื่อ partial clone และ large object promisors พัฒนาต่อไป Git LFS จะค่อย ๆ หมดความจำเป็น และอีกไม่นานเราก็น่าจะเข้าสู่ยุคที่จัดการไฟล์ขนาดใหญ่ได้อย่างง่ายดายด้วย Git เพียงอย่างเดียว
  • ในอนาคต อุปสรรคสุดท้ายของการใช้งานไฟล์ขนาดใหญ่อาจเหลือแค่การตัดสินใจว่าจะใส่คลัง MP3 ลงใน Git หรือไม่เท่านั้น

1 ความคิดเห็น

 
GN⁺ 2025-08-16
ความคิดเห็นบน Hacker News
  • สมัยก่อนแม้แต่ svn ก็ยังทำงานได้ดีโดยแทบไม่มีปัญหา แม้จะเป็น working directory ขนาด 150GB ที่มีไฟล์ไบนารีขนาดใหญ่จำนวนมาก แต่ git ไม่เป็นแบบนั้น เลยสงสัยว่าทำไม svn ถึงมีแนวทางกับไฟล์ไบนารีขนาดใหญ่ต่างจาก git และ git จะทำแบบเดียวกันไม่ได้หรืออย่างไร อีกทั้งฟีเจอร์ที่จำเป็นมากเมื่อจัดการข้อมูลไบนารีก็คือ file locking ควรให้มีคนแก้ไขไฟล์บางไฟล์ได้เพียงคนเดียว และให้คนอื่นอ่านได้อย่างเดียวเพื่อหลีกเลี่ยงปัญหา merge ที่ชวนปวดหัว ผมยังไม่ค่อยเข้าใจว่าการ offload ไฟล์ขนาดใหญ่ออกไปภายนอกช่วยแก้ปัญหาประสิทธิภาพหรือความเสถียรจริงหรือไม่ สุดท้ายมันก็แค่เป็น repository คนละตัวอยู่ดี และการ offload ก็ดูเหมือนเป็นวิธีที่ public git forge ใช้หลีกเลี่ยงต้นทุนเก็บไฟล์ขนาดใหญ่มากกว่า

    • git กับ svn เป็นการออกแบบคนละแบบโดยสิ้นเชิง git เป็นระบบ distributed เต็มรูปแบบ ดังนั้นทุก repository ต้องมีประวัติทั้งหมดของทุกไฟล์ และแนวคิดเรื่อง locking ก็แทบไม่มีความหมาย เลือกระบบควบคุมเวอร์ชันให้เหมาะกับโปรเจ็กต์ก็พอ ไม่จำเป็นว่า git ต้องเป็นทุกอย่างสำหรับทุกงาน
  • ชอบแนวคิด large object promisors มาก ถ้าต่อกับ S3 หรืออะไรทำนองนั้นได้ง่าย ๆ ก็น่าจะย้ายมาจาก LFS ทันที S3 มีจุดแข็งมากเมื่อใช้เก็บเวอร์ชันของไฟล์ไบนารีขนาดใหญ่ และยังได้ประโยชน์จาก intelligent tiering ที่จะย้ายข้อมูลเก่าไปยัง storage tier ที่ถูกลงโดยอัตโนมัติด้วย ต่อให้ต้องใช้เวลาครึ่งวันเพื่อกู้ข้อมูลอายุ 10 ปีก็ไม่เป็นไร

    • ผมก็คิดเหมือนกัน ไม่เข้าใจว่าทำไมตั้งแต่แรกถึงไม่ทำวิธีนี้เป็นค่าเริ่มต้น ตอนนี้ผมรัน git LFS server เล็ก ๆ เองอยู่ และถ้า git รองรับ S3 แบบ native เมื่อไร ก็พร้อมเปลี่ยนทันที

    • ที่ทำงานตอนนี้เรากำลัง cache LFS objects ลง bucket เพื่อลดค่าใช้จ่าย ทุกครั้งที่รัน PR ก็ใช้ git lfs ls-files เอารายชื่อไฟล์ ดึงจาก gcp มา จากนั้นใช้ git lfs checkout เพื่อเก็บ object ไว้ในเครื่อง แล้วค่อย pull เพิ่มเฉพาะส่วนที่ยังขาด ไฟล์ที่ยังไม่ได้ cache ก็อัปโหลดกลับเข้า bucket ด้วย gcloud storage rsync จากมุมของนักพัฒนาไม่ต้องตั้งค่าเพิ่มอะไร แค่ pull object ใหม่ และ Github UI ก็ไม่ทำให้สับสนเรื่องสถานะของ repository เมื่อก่อนเคยคิดจะรัน LFS backend เอง แต่ตอนนี้วิธีนี้แก้ปัญหาใหญ่ที่สุดที่เจอได้แล้ว แต่ก่อน Github คิดค่าทราฟฟิกแพงเกินไปทุกครั้งที่ CI ต้องดึงไฟล์ LFS และด้วยข้อจำกัด cache 10GB รวมถึงแชร์ข้าม branch ไม่ได้ เลยต้องดาวน์โหลดใหม่ทุกครั้ง ผมอยากจ่ายเงินเพื่อเพิ่มขนาด cache ด้วยซ้ำ แต่ก็ไม่มีทางเลือก ถ้าจะเอาวิธีนี้ไปใช้กับนักพัฒนาก็แค่เพิ่ม git hook เท่านั้นเอง เรียบง่ายมาก

    • S3 เป็นบริการของ Amazon หรือเปล่า

  • บทความพูดถึงข้อเสียของ Git LFS หลายอย่าง แต่ผมไม่เห็นด้วยกับประเด็น vendor lock-in เพราะ GitHub มีทั้ง client และ server แบบโอเพนซอร์สให้ใช้ จึงไม่แฟร์นักที่จะพูดแบบนั้น อย่างไรก็ตาม LFS ใช้งานแบบออฟไลน์หรือ sneaker-net ไม่ได้ ซึ่งแม้จะไม่ใช่กรณีที่พบบ่อย แต่ก็ควรพูดถึงอยู่ large object promisor ดูเหมือนจะย้ายความซับซ้อนฝั่ง client ของ LFS ไปไว้ที่ server ซึ่งสุดท้ายก็เหมือนแค่ย้ายจุดที่ความซับซ้อนอยู่ ถ้า git server เป็นคนอัปโหลดไฟล์ไปยัง LFS server กับ object storage ก็จะมี trade-off อีกแบบหนึ่งตามมา ผมสงสัยว่าถ้า public git server ซ่อน promisor remote ไว้ แล้วตอนอัปโหลดจะเกิดอะไรขึ้น

    • ผมคิดว่า LFS แย่มากจริง ๆ ทั้งการทำ server implementation ก็เละ และยังปนกันระหว่างเนื้อหาของ object กับวิธีจัดเก็บ วิธี opt-in ก็แย่มากจนถ้าใช้แบบไม่ทันคิด คุณจะได้ไฟล์ข้อความเล็ก ๆ แทนไฟล์จริงที่ต้องการ ไม่รู้ว่าโซลูชันใหม่นี้จะดีกว่าหรือไม่ แต่ที่แน่ ๆ คือ LFS ไม่ดี

    • อีกปัญหาของ Git LFS ที่เพิ่งรู้เมื่อไม่นานมานี้คือ การ migration ทำให้ .gitattributes ของ commit ต้นทาง ๆ ถูกทำให้ปนเปื้อนด้วย กล่าวคือ ถ้ามีลำดับ commit เป็น A→B→C และเพิ่งเพิ่มไฟล์ขนาดใหญ่เข้า LFS ที่ C เท่านั้น A กับ B ก็จะมี .gitattributes ที่ชี้ไปยังไฟล์ LFS ที่ไม่มีอยู่จริงด้วย กระบวนการ migration ทำให้ .gitattributes ย้อนแพร่ไปตาม history เพราะมันไม่ได้ตรวจว่ามีไฟล์นั้นอยู่จริงใน commit ปัจจุบันหรือไม่

    • เมื่อก่อน Git LFS ไม่รองรับ SSH จึงต้องมีใบรับรอง SSL เสมอ และนั่นเป็นอุปสรรคสำหรับคนที่ self-host ที่บ้าน ดูเหมือนว่า Gitlab เพิ่งมีแพตช์รองรับ SSH เมื่อไม่นานมานี้

  • ตอนเรียนวิศวกรรมซอฟต์แวร์ อาจารย์แนะนำว่าอย่าใส่ไฟล์ขนาดใหญ่ เช่น media ลงใน Git แต่ให้ใส่ไว้ใน artifact repository อย่าง Artifactory แทน แบบนั้นจะกระจายงานได้ในรูป snapshot dependency และให้ build system ควบคุมการดึงเฉพาะเวอร์ชันล่าสุดได้เอง ส่วนไฟล์เก่าที่กองอยู่ในเครื่องของเพื่อนร่วมงานก็จัดการได้ทันที แค่ล้าง build system cache

    • วิธีนี้ให้ความรู้สึกคล้าย git submodule อยู่เหมือนกัน ถ้า submodule แก้ปัญหาได้จริง คนก็น่าจะใช้กันไปแล้ว git submodule ก็รองรับ shallow clone เช่นกัน (ลิงก์ที่เกี่ยวข้อง: https://stackoverflow.com/questions/2144406/how-to-make-shallow-git-submodules) ผมไม่เคยเจอปัญหาไฟล์ขนาดใหญ่ เลยอยากรู้ว่าทำไมวิธีนี้ถึงใช้ไม่ได้ เพราะข้อเสียที่มีคนพูดไว้ใน SO ก็ดูไม่ใหญ่ขนาดนั้น

    • สงสัยว่าในคลาสเขาสอนสถาปัตยกรรมของระบบ CI/CD ด้วยหรือเปล่า ทุกวันนี้วิศวกรจบใหม่หลายคนยังไม่คุ้นกับภาพรวมการเชื่อมต่อของ GitLab, Artifactory, CodeSonar, Anchore และอื่น ๆ

    • วิธีนี้ก็มีข้อเสียเหมือนกัน เพราะต้องมีข้อมูลรับรองเพิ่มทั้งสำหรับระบบ CI/CD และนักพัฒนา กระบวนการ commit ก็เพิ่มเป็นหลายขั้น ต้องรู้ artifact ID ก่อน และถ้าพยายามทำให้เป็นอัตโนมัติด้วย git hook สุดท้ายก็จะซับซ้อนคล้าย git-lfs อยู่ดี

  • บทความนี้ประเมิน LFS ต่ำเกินไป LFS ไม่ได้ผูกติดกับ GitHub และตัวโปรโตคอลก็เปิดอยู่ ข้อเสียของ LFS เป็นสิ่งที่หลีกเลี่ยงไม่ได้ในฐานะส่วนขยายของ git และ promisors ก็แทบจะเป็นแนวคิดเดียวกัน เพียงแต่ฝังเข้ามาใน git ภายในเลยทำให้ UX ดีขึ้นหน่อย

    • repository ที่เคยใช้ LFS แม้เพียงครั้งเดียวก็จะถูก lock-in ไปตลอด ถ้าจะลดพื้นที่ที่ใช้ คุณต้องลบ repository ทิ้งทั้งก้อน และเรื่องนี้ก็ไม่ได้มีการสื่อสารไว้อย่างชัดเจนในเอกสารทางการ ตอนที่บริษัทของเราศึกษาสถิติ Github เคยเอาไฟล์ฐานข้อมูลแบบบีบอัดขนาดใหญ่มาใส่ใน LFS แล้วเจอปัญหานี้กับตัวเอง
  • นี่ไม่ใช่คำตอบที่แท้จริง git LFS เองก็เป็นแค่ทางออกเฉพาะหน้า และต่อให้ใส่ filter argument ตอน clone ก็ยังไม่ใช่การแก้ปัญหาที่ต้นตอ git clone เป็นคำสั่งแรก ๆ ที่ทุกคนเรียนรู้ แต่กลับต้องคอยจำ filter ทุกครั้ง ถ้าพลาดก็เสียเวลาไปนาน และถึงจะสำเร็จก็อาจได้ repository ที่ทำงานไม่สมบูรณ์ สุดท้ายต้องเปลี่ยนไปใช้โครงสร้างแบบ rsync ที่ดึงไฟล์เวอร์ชันล่าสุดมาก่อนอย่างมีประสิทธิภาพถึงจะเป็นการแก้ที่รากจริง ๆ ซึ่ง git ก็ไม่ค่อยทำการเปลี่ยนแปลงระดับรากฐานแบบนั้น

    • เห็นด้วยมาก git มัก "แก้" ปัญหาด้วยการเพิ่ม flag อีกตัว แต่ผู้ใช้ส่วนใหญ่ไม่รู้ด้วยซ้ำว่ามีฟีเจอร์นี้ ถ้าปรับค่าเริ่มต้นให้ดีขึ้นก็แก้ปัญหาได้โดยไม่ต้องทำลาย compatibility ด้วย

    • repository ที่ clone มาอาจทำงานไม่สมบูรณ์
      จริง ๆ แล้วที่หายไปมีแค่ history ของ blob

    • บอกว่า rsync แก้ปัญหานี้ได้ อยากรู้ว่าหน้าตามันเป็นอย่างไรในทางปฏิบัติ ไม่ใช่อัลกอริทึม แต่อยากรู้ว่าตอนผู้ใช้สั่ง "git clone" แล้วใน local filesystem จะมีอะไรเกิดขึ้นบ้าง

    • ถ้าขนาดส่วนใหญ่ของ repository อยู่ใน revision เก่า ๆ การดึงเฉพาะเวอร์ชันล่าสุดมาก่อนแบบ rsync จะเป็นคำตอบที่เหมาะกับผู้ใช้ส่วนใหญ่มากที่สุด

  • ดีใจมากที่ Git core กำลังเพิ่มการรองรับไฟล์ขนาดใหญ่ ต่อให้เป็น external solution สุดท้ายโครงสร้างแบบ opt-in ก็คล้ายกันอยู่ดี ตอนออกแบบ API เลยพยายามลดจำนวนคำสั่งให้มากที่สุดและทำให้ seamless โดยจำกัดไว้ที่ smudge/clean filter ของไฟล์ .gitattributes เท่านั้น นอกจากนี้ยังร่วมมือโดยตรงกับ Atlassian และ Microsoft เพื่อลด vendor lock-in และ API สำหรับ file locking ก็ได้รับความช่วยเหลือจาก Atlassian อย่างมาก LFS ถูกปล่อยเป็นโอเพนซอร์สและมี host สามแห่งที่รองรับกันได้

  • เรากำลังพัฒนา oxen เพื่อแก้ปัญหาที่เจอใน git และ git-lfs โดยยังคงใช้ interface แบบ git แต่ทำงานได้เร็วกว่าเยอะในสภาพแวดล้อมที่มีไฟล์ขนาดใหญ่และ monorepo ที่มีไฟล์นับล้าน เรามีทั้ง CLI และ server แบบโอเพนซอร์ส ถ้าสนใจก็อยากได้ฟีดแบ็ก
    https://github.com/Oxen-AI/Oxen

  • วิธีจัดเก็บของ git เองก็ควรถูกยกเครื่องใหม่ให้ใช้ content-defined chunking กับไฟล์และไดเรกทอรีแบบเครื่องมือสำรองข้อมูลสมัยใหม่อย่าง restic หรือ borg

  • ข้อเสียของ GitLFS อีกอย่างที่บทความไม่ได้พูดถึงคือเรื่องการยืนยันตัวตน ถ้าไม่ใช้ SSH-agent แม้แต่การ push ครั้งเดียวก็อาจต้องยืนยันตัวตนหลายรอบ จากประสบการณ์ของผมเคยเจอมากกว่าสองสามรอบด้วยซ้ำ