วิธียุบหลาย ๆ git commit เป็นหนึ่ง

Chonlasith Jucksriporn
odds.team
Published in
5 min readAug 10, 2020

เวลาเราเขียนโค้ดเสร็จสักฟีเจอร์หนึ่งแล้วต้องการ push กลับเข้าไปใน remote หรือ upstream โดยปกติแล้ว เรามักจะ push commit ที่เกิดขึ้นทั้งหมดของฟีเจอร์นั้น ซึ่งบางทีอาจจะมีการแก้ไขเล็ก ๆ น้อย ๆ การแก้คำผิด หรือ commit แบบอื่น ๆ ทีนี้พอมันเข้าไปรวมใน upstream แล้วเวลาเราดู log มันก็จะมี commit เต็มไปหมด

มันมีเทคนิคหนึ่งในการยุบรวมเอาหลาย ๆ commit เข้ามาเป็น commit เดียวก่อนที่จะ push ขึ้น upstream เราเรียกว่า git squash ซึ่งจะเปิดโอกาสให้เราเลือก commit ที่จะยุบรวมเข้าด้วยกัน และแก้ไขข้อความใน commit ให้สวย ๆ ก่อนที่จะ push กลับเข้าสู่ upstream

เช่น จากฟีเจอร์เพิ่มฟีเจอร์การโอนเงินเข้า wallet เราอาจจะมีข้อความ commit หลาย ๆ อันแบบนี้

(ใหม่สุด) 9fe2bc0 Add icon to available wallets
b8c2eab fix: test
77e1cef fix: correct typo in configuration file
038ca10 Add money transfer gateway to config file
dea4cd4 Make wallet configurable
(เก่าสุด) 817eac3 Add target wallet selection

พอมันถูก push กลับเข้า upstream มันจะกลายเป็นข้อความ commit แบบนี้จากหลาย ๆ ฟีเจอร์ปน ๆ กันโดยไม่ได้บ่งบอกว่ามันคือฟีเจอร์อะไร

สิ่งที่เราจะทำต่อไปนี้คือ เราจะยุบรวมข้อความ commit ทั้ง 6 ข้อความเข้าด้วยกันเป็น 1 ข้อความ และทำให้มันอ่านง่ายขึ้น เริ่มจาก

1. ดูก่อนว่าเราอยากจะยุบรวมกี่ commit เข้าด้วยกัน

เราสามารถใช้คำสั่ง ด้านล่างนี้เพื่อดูย้อนหลังไปว่าใน commit ที่ผ่าน ๆ มาเราทำอะไรไปบ้าง และมี commit id อะไรบ้าง

git log --graph --decorate --pretty=oneline --abbrev-commit

จริง ๆ ใช้ git log เฉย ๆ ก็ได้ แต่มันจะเป็นแบบหลายบรรทัด หลายหน้าจอ ไล่ยากพอสมควร

พอเราสั่ง git log --graph --decorate --pretty=oneline --abbrev-commit แล้วเราจะเห็นหน้าจอประมาณนี้

* (HEAD -> master) 9fe2bc0 Add icon to available wallets
* b8c2eab fix: test
* 77e1cef fix: correct typo in configuration file
* 038ca10 Add money transfer gateway to config file
* dea4cd4 Make wallet configurable
* 817eac3 Add target wallet selection
* c31dd0a feat: Add favorite feature
* ...
:

สิ่งที่เราสังเกตได้คือตอนนี้ HEAD อยู่ที่ 9fe2bc0และเราต้องการยุบ commit ที่เป็นฟีเจอร์การโอนเงินเข้า wallet เข้าด้วยกัน ถ้านับดูก็จะได้ทั้งหมด 6 commit โดยมี 817eac3 เป็น commit ที่เก่าที่สุดของฟีเจอร์

สังเกตว่า ถ้าเรามีจำนวน commit ที่มากเกิน 1 หน้า มันจะมีเครื่องหมาย : มารอรับคำสั่งต่อไป ถ้าเราต้องการไปหน้าถัดไป เราสามารถกด spacebar เพื่อไปหน้าถัดไปได้ หรือกด q เพื่อออกจากหน้านี้

2. ยุบ commit

หลังจากที่เราได้จำนวน commit แล้ว เราก็สั่งคำสั่งนี้

git rebase -i HEAD~<จำนวน commit>

หรือ

git rebase -i <commit-id>^

git จะทำการเปิด editor มีหน้าตาประมาณนี้

pick 9fe2bc0 Add icon to available wallets
pick b8c2eab fix: test
pick 77e1cef fix: correct typo in configuration file
pick 038ca10 Add money transfer gateway to config file
pick dea4cd4 Make wallet configurable
pick 817eac3 Add target wallet selection
# Rebase 9fe2bc0..817eac3 onto 817eac3 (6 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 <commit> = like "squash", but discard this commit's log message
# 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
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

สิ่งที่เราจะทำคือ แก้คำว่า pick ด้านหน้า commit ที่ต้องการยุบ เป็นคำว่า squash หรือใช้แค่ตัว s ก็พอ โดยที่เราจะต้องปล่อยให้ commit แรกเอาไว้เป็น pick เหมือนเดิม เพื่อให้เป็น commit เริ่มต้นด้วย

หลังจากที่เราแก้แล้ว เราจะเห็นว่า เราแก้จาก pick เป็น s ไปทั้งหมด 5 commit และทั้ง 5 commit นี้จะถูกเอาไปรวมกับอีก 1 commit ที่เราเก็บไว้

s 9fe2bc0 Add icon to available wallets
s b8c2eab fix: test
s 77e1cef fix: correct typo in configuration file
s 038ca10 Add money transfer gateway to config file
s dea4cd4 Make wallet configurable
pick 817eac3 Add target wallet selection
# Rebase 9fe2bc0..817eac3 onto 817eac3 (6 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 <commit> = like "squash", but discard this commit's log message
# 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
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

เสร็จแล้วก็ save และปิดไฟล์ได้เลย ใครที่ใช้ editor ตระกูล VI ก็กด Escape แล้วก็ wq! ได้เลย

3. แก้ข้อความ commit ให้สวย ๆ

# This is a combination of 6 commits.
# This is the 1st commit message:
Add target wallet selection# This is the commit message #2:Make wallet configurable# This is the commit message #3:Add money transfer gateway to config file# This is the commit message #4:fix: correct typo in configuration file# This is the commit message #5:fix: test# This is the commit message #6:Add icon to available wallets# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Fri Aug 7 23:48:49 2020 +0700
#
# interactive rebase in progress; onto 817eac3

เราก็แก้เป็นข้อความ commit ที่เราต้องการแล้วเซฟได้เลย

feat: Add wallet transfer feature

หลังจากที่เราเซฟ git ก็จะทำการยุบ commit ที่เราเลือกไปเป็น commit ใหม่ให้เหลือ commit เดียว

[detached HEAD d490ef0] feat: Add wallet transfer feature
Date: Fri Aug 7 23:20:58 2020 +0700
12 files changed, 2334 insertions(+)
create mode 100644 src/services/wallet.service
create mode 100644 src/assets/icons/wallet.png
...
Successfully rebased and updated refs/heads/master.

หลังจากนั้นเราก็แค่ push กลับขึ้นไปที่ upstream เป็นอันจบ

มันมีประโยชน์ยังไง

สำหรับคนที่ใช้เครื่องมือที่ช่วยในการทำ Release note หรือ CHANGELOG แบบอัตโนมัติจากข้อความ commit เครื่องมือพวกนั้นจะใช้ข้อความ commit มาใช้นี่แหละ ดังนั้นถ้าข้อความ commit ที่เราเขียนมันเป็นการแก้แบบหยุมหยิม เราก็อาจจะทำให้ Release note ของเราเต็มไปด้วยสิ่งที่ user อ่านไม่เข้าใจเลยว่าแอพหรือไลบรารี่ของเรามีออกอะไรใหม่ หรือแก้อะไรใหม่ไป

ทีมพัฒนาบางทีมเอาเทคนิค git squash นี้เข้าไปเป็นส่วนหนึ่งของการพัฒนาเลย เพื่อที่จะทำให้การทำ CI/CD มันเป็นอัตโนมัติได้มากขึ้น และ flow ในการทำงานแบบที่ใช้ git squash เพื่อยุบ commit ก่อน push เราเรียกว่า squash rebase workflow

อ้างอิง

--

--