Categories
การพัฒนาซอฟท์แวร์

รูปแบบ Commit Message ของ Git ที่น่าสนใจ

ที่มา: dev.to

เพื่อให้การทำงานเป็นทีมมีประสิทธิภาพมากขึ้น เราควรใส่ใจกับ Commit Message ของ Git

เพราะ Commit Message ช่วยในการสื่อสารกับเพื่อนร่วมทีม หรือแม้แต่เป็นการสื่อสารกับตัวเราเองในอนาคต เพื่อให้รู้ว่ามีการเปลี่ยนแปลงอะไรบ้าง จะเกิดผลกระทบอะไรบ้าง

บล็อกนี้นำเสนอรูปแบบหนึ่งสำหรับ Commit Messageของ Git ที่น่าสนใจ

รูปแบบของ Commit Message

!type(?scope): !subject

❗คือ บังคับว่าต้องมี
❓คือ มีหรือไม่มี ก็ได้


Type

  • ❗ต้องมีเสมอ
จากรูปแบบ Commit Message :
!type(?scope): !subject

Type บอกว่าการเปลี่ยนแปลงนี้ทำเกี่ยวกับอะไร โดยมี Types ให้เลือก ดังนี้

  • test
    • สร้างหรือเปลี่ยนแปลง Test Code
  • feat
    • เพิ่ม Feature ใหม่ สำหรับ Project
  • refactor
    • ทำ Code Refactoring โดยไม่กระทบกับ Logic หรือ Rules
  • style
    • เมื่อมีการเปลี่ยน Code Style หรือมีการ Formatting Code
  • fix
    • แก้ Error หรือ Bugs ในระบบ
  • docs
    • เปลี่ยนแปลง Document ของ Project
  • chore
    • มีการเปลี่ยนแปลงที่ไม่เกี่ยวกับ Code หรือ Test
    • เป็นการเปลี่ยนแปลงด้าน Development Dependency หรือ Tools เช่น
      • eslint
      • prettier
      • .gitignore
  • build
    • มีการเปลี่ยนแปลงที่ส่งผลต่อ Build Process หรือ External Dependency
    • เปลี่ยนแปลง Dependency ของ Project
  • perf
    • การเปลี่ยนแปลงที่เกี่ยวข้องกับการเพิ่ม Performance ของระบบ
  • ci
    • เปลี่ยนแปลง CI/CD Configuration File เช่น
      • Circle
      • Travis
      • Jenkins
  • revert
    • มีการย้อยกลับไปที่ Commit ก่อนหน้านี้
📝 Notes (เกี่ยวกับ Type)
  • ใน Commit message ต้องมี Type เสมอ
  • ต้องมีแค่ 1 Type ต่อ 1 Commit
  • ถ้าไม่รู้จะใช้ Type ใด เป็นไปได้ว่า Commit ใหญ่เกินไป
    • ควรแยก Commit ให้เล็กพอที่จะเลือก Type ได้
    • และแต่ละ Commit ต้องเป็นการเปลี่ยนแปลงเพียงเรื่องเดียว

Subject

  • ❗ต้องมีเสมอ
จากรูปแบบ Commit Message :
!type(?scope): !subject

Subject ต้องใช้ประโยคเชิงคำสั่งที่ขึ้นต้นด้วย Verb.1 (โดยไม่มี to) แทนที่จะเป็น Past Tense

เพื่อบอกทีมว่า commit นี้จะทำอะไร ถ้าเรา apply มัน:

If applied, this commit will do something according to this message

เปรียบเทียบ 2 commit message นี้ดู

ตัวอย่าง: การใช้ Subject ใน Commit Message ของ Git
ตัวอย่าง: การใช้ Subject ใน Commit Message ของ Git

จะเห็นว่า

If applied, this commit will change the markup ✅, ดู make sense กว่า

“If applied, this commit will changed the markup” 🟥

นอกจากนี้การเขียน Subject ควรเขียนสั้น ๆ กระชับ โดยทั่วไปไม่ควรเกิน 50 ตัวอักษร ถ้าต้องการเขียนยาวกว่านี้ให้ไปเขียนใน Body ของการ Commit


Scope

  • ❓ มีหรือไม่มี ก็ได้
  • บอก Context หรือบริบทของ Commit นี้

จากรูปแบบ Commit Message :

!type(?scope): !subject

จากรูปแบบที่เขียนมาก่อนหน้านี้ เราจัดการ Commit Message ให้ชัดเจนด้วยการใช้

  • Type บอกประเภทของการเปลี่ยนใน Commit นี้
  • Subject บอกอย่างชัดเจนว่าจะเกิดอะไรขึ้น เมื่อ Apply Commit นี้

แม้ว่า Scope ไม่จำเป็นต้องมีก็ได้ แต่มันช่วยบอก Context ของ Commit นี้เพิ่มเติมได้

และยังช่วยลดภาระของ Subject ลงได้ ทำให้ Subject สั้นและกระชับกว่าเดิม

หมายเหตุ :

  1. Scope ต้องใส่ใน “วงเล็บ” เช่น (Scope) เสมอ
  2. Scope จะถูกแบ่งแยกด้วย “/” (Slash) ได้ เช่น
    • (module/AuthService)
    • (package/PackageName)

ตัวอย่าง

ตัวอย่าง: การใช้ Scope ใน Commit Message ของ Git
ตัวอย่าง: การใช้ Scope ใน Commit Message ของ Git

แนวปฏิบัติเพิ่มเติมเพื่อการ Commit ที่ดี

  • Commit บ่อย ๆ และ เล็ก ๆ
    • ช่วยให้การ review code ง่ายขึ้น
    • ช่วยให้ย้อนกลับไปยังจุดที่ต้องการได้ง่าย
    • ช่วยให้การ merge ง่ายขึ้น เพราะเมื่อเกิด merge conflict จะมี code ไม่มากที่ต้องพิจารณา
    • การเปลี่ยนแปลง code ทีละน้อย ทำให้คุณภาพโค้ดดีขึ้น พัฒนาขึ้นอย่างต่อเนื่อง
  • การเขียน Body ใน Commit
    • เว้น 1 บรรทัดจากหัวข้อ
    • ควรเขียนให้สั้น และกระชับเช่นกัน
    • ควร Wrap ความยาวต่อ 1 บรรทัด ไม่เกิน 72 ตัวอักษร
    • ควรเขียนอธิบายว่าทำอะไร และทำไม (What + Why)
    • ขึ้นต้นประโยคด้วยตัวพิมพ์ใหญ่ และปิดท้ายประโยคด้วยจุด
  • Commit Message เป็นเพียงเครื่องมือหนึ่งในการสื่อสาร ทีมพัฒนาซอฟต์แวร์ยังจำเป็นต้องคุยกัน สื่อสารกันบ่อย ๆ
Categories
Git

การใช้ไฟล์ .gitignore กับโปรเจคเก่า ๆ

บางครั้งเรามีโปรเจคเก่าที่ใช้ Git อยู่แล้ว แต่เรา ignore ไฟล์ไม่ครบ อาจมีไฟล์ที่เราไม่ต้องการ ถูก track เข้าไปด้วย เช่น ไฟล์จาก OS หรือ ไฟล์จาก IDE เป็นต้น

เราสามารถแก้ไขได้ง่าย ๆ ด้วยคำสั่งเพียงไม่กี่คำสั่ง แต่ก่อนอื่นผมขอแนะนำสำหรับคนที่ไม่รู้ว่าจะ Ignore ไฟล์อะไรดี สามารถเข้าไปดูได้ที่ https://github.com/github/gitignore ซึ่งรวบรวมไฟล์และโฟลเดอร์ที่เราควร ignore ไว้ครบถ้วน (ผมแนะนำให้ดูในโฟล์เดอร์ Global ด้วย เพราะมีคำแนะนำการ ignore ไฟล์ของ IDE และ OS เพิ่มเติม)

สมมุติว่าผมเขียน Java ด้วย Eclipse บน macOS ผมจะสามารถสร้างไฟล์ .gitignore ได้ประมาณนี้

[gistpen id=”1197″]

เมื่อสร้างไฟล์ .gitignore ก่อนที่จะทำขั้นตอนต่อไปให้ทำการ commit การเปลี่ยนแปลงทั้งหมดก่อน

แล้วใช้คำสั่งนี้เพื่อเคลียร์ index ของ git

$ git rm -r --cached .

แล้วใช้คำสั่ง

$ git add .

และ commit

$ git commit -m ".gitignore is now working"

เพียงเท่านี้เราก็สามารถ ignore ไฟล์และโฟลเดอร์ที่เราไม่ต้องการได้แล้วครับ

Categories
Git

การใช้งาน Git Submodules

หลังจากที่ผมได้รู้จัก Git Submodules มาซักพักหนึ่ง ผมก็พยายามใช้ในงานต่าง ๆ ซึ่งช่วยให้ผมจัดการ Source code สะดวกขึ้นมาก แต่เนื่องจากได้ใช้คำสั่งพวกนี้ในช่วงเริ่มโปรเจคซะเป็นส่วนใหญ่เลยทำให้มีอาการหลงลืมคำสั่งไปบ้าง ก็เลยมาบันทึกไว้หน่อยจะได้ไม่ต้องไปหาข้อมูลใหม่อีก

การเพิ่ม Submodules เข้ามาใน Git Repository

ใช้คำสั่งดังนี้

$ git submodule add https://github.com/golfz/module1 lib/module1
Initialized empty Git repository in ~/a_project/lib/module1/.git/
remote: Counting objects: 1006, done.

remote: Compressing objects: 100% (978/978), done.

remote: Total 1006 (delta 631), reused 0 (delta 0)

Receiving objects: 100% (1006/1006), 408.22 KiB, done.

Resolving deltas: 100% (631/631), done.

แต่ละส่วนของคำสั่งมีรายละเอียดดังนี้

git submodule add เป็นคำสั่ง git เพื่อเพิ่ม Submodules

https://github.com/golfz/module1 คือ git repository ภายนอกที่เราต้องการเพิ่มเข้ามาเป็น submodule ซึ่งคุณต้องมั่นใจว่าคุณมีสิทธิ clone repository นี้

lib/module1 คือ path ของ submodules ใน repository หลัก

ลองตรวจสอบ repository ดูด้วย git status
$ git status
# On branch master
# Changes to be committed:

# (use "git reset HEAD <file>..." to unstage)

#

# new file: .gitmodules

# new file: lib/module1

#

ถ้าเราลองตรวจสอบ .gitmodules ที่เพิ่มเข้ามาใหม่ จะพบข้อมูลดังนี้

$ cat .gitmodules
[submodule "lib/module1"]
path = lib/module1

url = https://github.com/golfz/module1

การใช้งาน Submodules

ถ้าเราเพิ่ม clone repository ลงมา เราจะพบว่าโฟลเดอร์ที่เป็น submodule จะว่างเปล่า ดังนั้นเราต้อง initial submodules ก่อน ดังนี้

$ git submodule init
Submodule 'lib/module1' (https://github.com/golfz/module1) registered for path 'lib/module1'

ขั้นตอนต่อไปเราจำเป็นต้อง pull files ลงมา

$ git submodule update
Initialized empty Git repository in ~/a_project/lib/module1/.git/
remote: Counting objects: 26, done.

remote: Compressing objects: 100% (22/22), done.

remote: Total 26 (delta 5), reused 0 (delta 0)

Receiving objects: 100% (26/26), 17.37 KiB, done.

Resolving deltas: 100% (5/5), done.

Submodule path 'lib/module1': checked out '1c407cb2315z0847facb57d79d680f88ca004332'

ตอนนี้ถ้าเราเข้าไปดูใน lib/module1 เราจะพบว่ามีไฟล์อยู่อย่างถูกต้องแล้ว

การลบ Submodules

ขั้นตอนในการลบ Submodules จะมีความซับซ้อนเล็กน้อย ดังนี้

1. เข้าไปลบข้อมูล submodules ในไฟล์ .gitmodules ให้ลบข้อมูลออกไปดังนี้
[submodule "lib/module1"]
path = lib/module1
url = https://github.com/golfz/module1

2. เข้าไปลบข้อมูล submodule entry ในไฟล์ .git/config ขั้นตอนนี้ไม่จำเป็นต้องทำก็ได้ แต่เพื่อป้องกันปัญหาในอนาคตหากเราใช้คำสั่ง “git submodule init” ผมแนะนำว่าทำก็ดีครับ โดยให้ลบข้อมูลออกไปดังนี้
[submodule "lib/module1"]
url = https://github.com/golfz/module1

3. ลบ path ของ Submodules ด้วยคำสั่ง
$ git rm --cached lib/module1
rm 'lib/module1'

การอัพเดต Submodules

นี่เป็นอย่างหนึ่งที่เราจะค่อนข้างสับสนเมื่อใช้งาน Submodules เพราะเมื่อเราเพิ่ม submodules เข้ามา เราจะได้รับ commit ล่าสุดของ repository ของ submodule นั้น

หากหลังจากนั้น Repository ของ submodule มีการอัพเดตแล้วเรา clone โปรเจคหลักไปที่อื่น แล้วใช้คำสั่ง

$ git submodule init

ต่อด้วย

$ git submodule update

เราจะพบว่าเราจะไม่ได้โค้ดล่าสุดของ submodule ซึ่งมักสร้างความสับสนอยู่บ่อยครั้ง

แต่หากคิดดูดี ๆ ก็จะพบว่ากลไกนี้มีความเหมาะสมแล้ว เนื่องจาก code ใน repository หลักของเราได้ถูกทดสอบกับ submodules เวอร์ชั่นใด เมื่อ clone repository หลักไปใช้ก็ควรได้ submodule เวอร์ชั่นเดิมที่เคยทดสอบแล้ว ไม่ใช่เวอร์ชั่นใหม่ล่าสุดที่เราไม่เคยทดสอบ

แต่หากเราต้องการ repository ของ submodule ใหม่ล่าสุด สามารถทำได้ดังนี้

1. เข้าไปที่โฟลเดอร์ของ submodule

$ cd lib/module1

2. checkout ไปที่ master

$ git checkout master
Previous HEAD position was b8ff8f6... re-ordering
Switched to branch 'master'

Your branch is behind 'origin/master' by 8 commits, and can be fast-forwarded.

3. ดึงอัพเดตล่าสุดลงมา

$ git pull
remote: Counting objects: 31, done.
remote: Compressing objects: 100% (24/24), done.

remote: Total 24 (delta 15), reused 0 (delta 0)

Unpacking objects: 100% (24/24), done.

From https://github.com/golfz/module1

b8ff8f6..5cab93f master -> origin/master

* [new tag] 1.2.28 -> 1.2.28

From https://github.com/golfz/module1

* [new tag] 1.2.26 -> 1.2.26

* [new tag] 1.2.27 -> 1.2.27

Updating c547e0d..5cab93f

Fast-forward

index.php | 109 ++++++++++++++-

css/admin.css | 26 ++++

js/admin.js | 17 +++

3 files changed, 51 insertions(+), 4 deletions(-)

create mode 100644 css/admin.css

create mode 100644 js/admin.js

4. กลับไปที่ repository หลัก แล้ว

$ git add lib/module1
$ git status
# On branch master
# Changes to be committed:

# (use "git reset HEAD ..." to unstage)

#

# modified: lib/module1

#

Categories
Git

การใช้งาน git กับโปรเจคที่มีอยู่แล้ว

  1. สร้าง Repository ใหม่
  2. $ cd /path/of/your/local/project
  3. $ git init
  4. $ git add .
    # Adds the files in the local repository and stages them for commit
  5. $ git commit -m ‘First commit’
    # Commits the tracked changes and prepares them to be pushed to a remote repository
  6. copy the remote repository URL.
  7. $ git remote add origin <remote repository URL>
    # Sets the new remote
    $ git remote -v
    # Verifies the new remote URL
  8. $ git push origin master
    # Pushes the changes in your local repository up to the remote repository you specified as the origin