บันทึกการใช้ Git Rebase และ Squash
วันนี้ขอเขียนบันทึกเรื่อง Git Rebase ไว้ซักหน่อย เพราะเป็นคำสั่ง ที่ใช้บ่อยมากๆ แม้จะไม่เท่าพวก git push
, git pull
, git checkout
ก็เถอะ โดยผมติต่างว่า ผู้อ่าน อาจจะพอมีพื้นฐาน git มาบ้าง หรือพอรู้จัก git rebase
มาบ้างครับ
เรื่องของ Rebase ผมเห็นคนพูดถึงบ่อยๆ Git Rebase vs Git Merge และพยายามจะเปรียบเทียบกันตลอด ถ้าลองไป search ดู (แต่คงไม่เท่ากับ tab vs space หรือ ใช้ vs ไม่ใช้ semicolon มั้งนะ ) แต่ส่วนตัวผมมองว่า อยากใช้อะไร ถนัดอะไรก็ใช้ไปเถอะครับ มันก็มีข้อดี ข้อเสีย ต่างกันไป ยอมรับข้อดี ข้อเสียได้ ก็จบ
แต่ละทีม แต่ละบริษัทก็จะมี Workflow การทำงาน ขั้นตอนการทำงานแตกต่างกัน คล้ายๆกันบ้าง และก็ไม่มีผิด มีถูก และก็ไม่ตายตัว มันอาจจะสามารถปรับเปลี่ยน แก้ไข ยืดหยุ่นได้ตามสถานการณ์
Git Rebase คืออะไร?
Git Rebase - แบบสั้นๆ ก็คือขั้นตอนการ re-commit ใหม่ทั้งหมดโดยจะย้าย commits ของเรา (HEAD) ไปต่อกับ branch target เรา หรือถ้ามองง่ายๆ คล้ายๆ ว่าเรา git checkout
branch หลัก จาก commit ล่าสุด
เพื่อให้เห็นภาพ ลองดูภาพด้านบน (ภาพ Reference จากบทความ Differences Between Git Merge and Rebase — and Why You Should Care)
ลองเปรียบเทียบระหว่าง git merge
ปกติ กับ git rebase
และเราจะเห็นความต่างครับ ว่า git rebase
ตัว commit เรา จะถูกย้ายไปต่อ master
ทำไมผมถึงใช้ Rebase
- เพราะผมชอบ history ที่ clean อ่านง่าย เป็น linear history (แม้จะ change history ก็เถอะ)
- ผมสนใจแค่ feature ที่เรากำลังทำ ฉะนั้น เลยไม่ต้องการ commit อื่นๆ ที่ไม่เกี่ยวข้อง
- ลบ commit message ที่ไม่เกิดประโยชน์ ก็ยุบรวมกันซะ ด้วย squash
แต่สิ่งที่ต้องรู้ของ Rebase และข้อควรระวัง ก็คือ
- แน่นอน มันเปลี่ยน history และก็อาจจะเสียคุณสมบัติของ git ไป เพราะเราไม่ควรไปเปลี่ยน history รึเปล่านะ?
- ใช้
git rebase
แค่เฉพาะ branch ที่เราทำเท่านั้น จะไม่ rebase ที่ share branch เด็ดขาด เพราะในเมื่อเราทำ branch ของเรา จะแก้ history จะเปลี่ยนอะไร ก็ทำไปเถอะ ตราบใดที่ยังไม่ merge เข้า develop หรือ main ก็แทบไม่มีใครสนใจ - ถ้าไม่คล่อง แนะนำสร้าง backup branch ไว้เผื่อฉุกเฉินครับ เพราะมันมีการใช้งานที๋ซับซ้อน 😅
- แต่ rebase ก็มีข้อเสีย คือ ถ้าเกิดเรา branch ใหญ่ มี commit เยอะๆ หรือเราไปแก้ history แล้วเกิด conflict ขึ้นมา ก็อาจจะแก้ไข หลายรอบ (เทียบกับ merge แค่รอบเดียว)
- แน่นอน rebase ผมมักใช้ร่วมกับ squash เพราะบางที ทำ feature แต่มี commit ที่ไม่มีประโยชน์ ก็รวมมันซะ จะได้เข้าใจง่าย (ถึงแม้จะแทบไม่ได้กลับมาดู history เลย นอกจากจะเจอบัค ฮา)
- นอกจาก squash บางครั้ง ก็ใช้
git commit --amend
เหมือนกัน
ด้านล่างคือตัวอย่าง คำสั่ง ที่ผมใช้ เวลาที่เราจะ rebase ครับ
# 1. switch ไป branch develop
git checkout develop
# 2. fetch และ merge develop จาก remote ล่าสุดมาที่เครื่อง local
git pull origin develop
# 3. กลับไป feature ที่เราทำงาน
git checkout feature/my-feature
# 4. rebase เพื่อย้าย commits ไปต่อ develop
git rebase develop
# 5. หรือจะใช้โหมด -i เพื่อ pick, squash ก็ได้
git rebase develop -i
# ถ้ามี conflict ก็แก้ จากนั้นก็
git add <FILE_CHANGES>
git rebase --continue
# 6. เสร็จแล้ว push code ไป remote (force push)
git push -f origin feature/my-feature
Git Rebase และ Squash
ทีนี้ปกติเราใช้แค่ git rebase
ทีนี้ถ้าจะ Squash เพื่อรวม commit หลายๆ ตัวเป็น commit เดียว เข้าด้วย กัน จะใช้คำสั่ง
git rebase <target> -i
จากนั้น มันก็จะมี list ของ commits ทั้งหมด ของ branch เรา
pick fff123 chore(login) - Add login button
pick fafa234 feat(login) - Support more providers
pick ee3aaa ci - config terraform
pick aabbbb update README.md
pick cccc333 doc(api) - Update API error messages.
สิ่งที่ทำก็คือการเปลี่ยนจาก pick
เป็น squash
ซึ่งมันมีคำอธิบายบอกไว้อยู่ครับ ว่า แต่ละคำมีความหมายว่าอะไร
อธิบายคร่าวๆ คือ:
p, pick <commit>
คือเอา commit นี้r, reword <commit>
คือเอา commit นี้ แต่ edit message ได้s, squash <commit>
คือเอา commit นี้ พร้อมกับรวม commit ก่อนหน้า
ทีนี้ ปัญหาคือ ถ้ามี commit เยอะๆ มานั่งเปลี่ยนทีละคำ ก็ไม่ใช่เรื่อง ฉะนั้น ก็ใช้ Vim ให้เป็นประโยชน์ครับ (ผมไม่แน่ใจว่า default ของ git เวลามันเปิด editor มันใช้ Text Editor ตัวไหนนะครับ ถ้าไม่ใช่ Vim ก็ขออภัยด้วยครับ)
สิ่งที่อยากจดบันทึกวันนี้ก็คือ ใช้ Vim มาแก้ปัญหาเรื่อง squash หลายๆ commit นั่นเอง (ก็คือ multi-select ของพวก Text Editor นั่นแหละ)
Reference: Git Interactive Rebase, Squash, Amend and Other Ways of Rewriting History
เริ่มจากเข้าโหมด interactive
git rebase -i <target_branch>
จะเห็น commit ประมาณนี้
pick fff123 chore(login) - Add login button
pick fafa234 feat(login) - Support more providers
pick ee3aaa ci - config terraform
pick aabbbb update README.md
pick cccc333 doc(api) - Update API error messages.
# Rebase xxxx..yyyy onto xxxx (5 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
สิ่งที่ต้องทำคือ
- เข้าโหมด Virtual Block โดยกด Ctrl + v
- จากนั้น ก็เลือก
pick
ตั้งแต่บรรทัด 2 จนถึง commit ที่เราจะรวม squash - จากนั้น กด c และพิมพ์ squash หรือ s (แบบสั้นๆ)
- กด ESC จากนั้น ตัว
pick
จะถูกเปลี่ยนsquash
pick fff123 chore(login) - Add login button
squash fafa234 feat(login) - Support more providers
squash ee3aaa ci - config terraform
squash aabbbb update README.md
squash cccc333 doc(api) - Update API error messages.
- แก้ไข commit message ตามที่ต้องการ จากนั้น save เป็นอันจบ
Done! 🎉
สรุป
แม้ว่าบทความนี้จะไม่ได้พูดถึงพื้นฐาน git rebase
เท่าไหร่ เป็นภาพรวม ภาพกว้างการใช้งาน รวมถึงบันทึกเอาไว้เผื่อตัวผมเองด้วยครับ บางทีก็มีลืมๆ แต่ก็หวังว่าจะมีประโยขน์ไม่มากก็น้อยนะครับ หากใครยังไม่เคยใช้ git rebase
ลองมาฝึกใช้กันดูได้นะครับ แม้ว่าช่วงเริ่มอาจจะซับซ้อน พอคล่องแล้วคุณอาจจะชอบเลยก็ได้ แต่ทั้งนี้ทั้งนั้น ใช้สิ่งที่ชอบ และเข้าใจมันดีกว่านะครับ เพราะถ้าใช้แบบไม่เข้าใจ แทนที่จะจบงาน กับเป็นการเพิ่มงานนะครับ ฮ่าๆ
อ่านเพิ่มเติม
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit