พอพูดถึง git เราก็จะมี command ที่ใช้กันบ่อยๆ จนคุ้นชินเช่น commit, push, pull, merge, etc., แต่ git worktree เป็นหนึ่งใน command ที่ไม่ค่อยเห็นคนใช้กันสักเท่าไหร่ จริงๆ ผมว่ามันมีประโยชน์แล้วก็สะดวกมาก วันนี้เรามาลองใช้งานกันครับ
Problem Statement
เคยไหมครับเวลาที่เราทำงานบนเครื่องของเรา ทำไปสักพักมีเหตุให้ต้องเปลี่ยนไป checkout ไปที่ commit หรือ branch อื่นๆ ยกตัวอย่างที่ผมเจอบ่อยๆ เช่น อาจจะมี Pull Request ที่ขอให้เราช่วยรีวิว ซึ่งบางครั้งเราจำเป็นที่จะต้อง checkout code มาดูในเครื่องเราเองเพื่อลอง run ดู แต่งานที่เราทำอยู่ หรือ file ที่เราแก้บนเครื่องเราที่ยังไม่ได้ commit ล่ะครับ เราจะทำยังไง? 🧐
ถ้าเกิดเจอเหตุการณ์แบบนี้เราอาจจะต้องทำการ git clone ไปอีก directory แล้วค่อย checkout ไปยัง branch นั้น หรืออีกท่าที่ง่ายกว่าคืออาจจะทำ git stash
เพื่อเก็บ changes ที่เราทำไว้ชั่วคราวแล้วค่อย git stash pop
เพื่อค่อยมาทำต่อจากที่ค้างไว้ ซึ่งวิธีนี้ก็น่าจะแก้ปัญหาที่เรามีได้แล้ว แต่วันนี้ผมจะมาแนะนำท่าที่ส่วนตัวผมคิดว่า elegant กว่า :) ด้วยการใช้ git worktree
The Solution
ปกติแล้ว git จะอนุญาติให้เรามีได้แค่ 1 active working tree ณ ช่วงเวลาหนึ่งเท่านั้น ก็คือใน directory ของ project หนึ่ง เราไม่สามารถ checkout ไปหลายๆ branch หรือ commit พร้อมกันได้ แต่ git worktree เป็น feature ที่ทำให้เราข้ามข้อจำกัดนี้ไปได้ครับ 🚀
Git worktree เป็น feature ที่เพิ่มเข้ามาตั้งแต่ git version 2.5 เป็นต้นมา ทำให้เราสามารถสร้าง working trees หรือ working directories หลายๆ อันได้ใน repo เดียว ทำให้เราสามารถ checkout ไปยัง branch หรือ commit อื่นๆ ได้โดยไม่มีผลกระทบกับ working directory ที่เรากำลังทำงานอยู่เลยครับ ทำให้เราสามารถทำงานกับ code หลายๆเวอร์ชั่นได้พร้อมกัน โดยที่ไม่ต้องกังวลเรื่อง git stash หรือ git clone มาใหม่
How to use
วิธีการใช้งานหลักๆ ก็คือให้ cd ไป directory ของ repo ที่เราต้องการแล้วก็ใช้คำสั่ง
git worktree
ตามด้วย command ที่เราต้องการได้เลยครับ ถ้าอยาก list operation ทั้งหมดที่ทำได้ให้ลองgit worktree --help
เพื่อให้เห็นภาพ ลองจินตนาการว่าเรากำลังทำงานอยู่บน branch feature-1
ซึ่งเรากำลัง dev อย่างเมามัน แต่แล้วจู่ๆ มี critical bug บน production ที่เราต้องแก้อย่างด่วน ทำไงหล่ะครับทีนี้ ขั้นแรกสุดเราต้องการดู code ใน master branch เพื่อไปแก้ bug อันนี้ เราทำได้โดยการสร้าง worktree ใหม่โดยใช้คำสั่ง
git worktree add hotfix
ซึ่ง git จะทำการสร้าง directory ใหม่พร้อมกับ branch ที่ชื่อว่า hotfix ให้เรา ซึ่ง code ของ branch นี้จะ based on commit ล่าสุดจาก working directory ของ repo เราครับ เช่นใน branch feature-1 ของผม last commit เป็น xyz ทำให้ใน worktree hotfix ของเรา ก็จะใช้ commit xyz นี้เป็น base commit
เราสามรถระบุ path ไปที่ไหนก็ได้ที่เราต้องการเช่น
git worktree add /tmp/another-hotfix
แล้วถ้าสมมติเราอยากสร้าง worktree ใหม่ที่ based on โค้ดใน master branch เลยทำยังไง? เราสามาถทำได้โดยการระบุ branch name ต่อท้ายไปยัง command ก่อนหน้าได้เลยครับ เช่น
git worktree add hotfix master
ซึ่งคำสั่งนี้จะสร้าง worktree ใหม่ที่เอา code มาจาก last commit ของ master branch
เราสามารถมอง worktree หรือ directory ใหม่นี้เป็นอีก repo นึงได้เลยครับ เราสามรถ cd เข้าไปแล้วทำการแก้ไขส่วนที่ต้องการ ทำการ commit แล้ว push code ขึ้นไปได้ปกติ เหมือน repository ทั่วไป โดยที่ code ใน feature-1 ของเราที่ยังไม่ได้ commit นั้นยังอยู่ปกติ
นอกจากนี้เรายังสามารถสร้าง worktree ได้ไม่จำกัด ทำให้เราสามารถ checkout code ได้หลายๆ version พร้อมๆกัน ส่วนวิธีการดูว่ามี worktree อะไรบ้างใน repository ให้เราใช้คำสั่ง
git worktree list
สุดท้ายแล้วพอเราทำการแก้ bug ใน hotfix เสร็จแล้วเราสามารถลบ worktree นี้โดยใช้คำสั่ง
git worktree remove hotfix
Benefits
ประโยชน์หลักๆในการใช้ท่า git worktree ก็คือ
- Concurrent branch/commit work: ประโยชน์ที่ชัดเจนสุดๆ ของ git worktree ก็คือสามาถให้เราทำงานกับหลายๆ branches หรือหลายๆ commits พร้อมกันได้ โดยไม่กระทบกับงานที่เราทำอยู่
- Isolated Environments: ในแต่ละ worktree ที่เราสร้างจะ เป็น independent directory กล่าวคือ การแก้ code ใน worktree หนึ่งจะไม่มีผลกระทบกับ worktree อื่นๆ จุดนี้ทำให้เราสามารถลองแก้ ได้อย่างอิสระโดยไม่ต้องกังวลว่าจะไปพัง code ชาวบ้าน 😁
- No git clone needed: เราไม่จำเป็นต้อง clone repository ใหม่เพื่อทำการ checkout branch ที่เราต้องการ ข้อนี้จริงๆ จะเห็นผลชัดเจนมากๆ ถ้ากรณีที่ repository size มีขนาดใหญ่มากๆ ครับ (>10GB) แล้วอีกข้อดีคือใน git worktree จะใช้ Shared object database (.git/objects) ทำสามารถใช้ข้อมูล git commit, history ต่างๆ ร่วมกันได้ ไม่ต้องโหลดใหม่ทำให้ใช้พื้นที่น้อยลงเมื่อเทียบกับการ git clone
Reference: