คู่มือ

git add และ git commit: วิธีบันทึกการเปลี่ยนแปลงให้ถูกต้อง

อัปเดต: · Skillaria.Top

การบันทึกการเปลี่ยนแปลงใน Git มีสองขั้นตอน: git add นำการแก้ไขที่ต้องการเข้าสู่ดัชนี (staging area) แล้ว git commit -m "ข้อความ" บันทึกเป็นสแนปชอตหนึ่งรายการในประวัติ git status แสดงว่ามีอะไรเปลี่ยนและอะไรถูกเตรียมไว้แล้ว และสามารถแก้คอมมิตล่าสุดด้วย git commit --amend

ใน Git ไม่มีปุ่ม "บันทึก" การเปลี่ยนแปลงถูกบันทึกเป็นสองขั้นตอน ขั้นแรก git add คัดเลือกว่าการแก้ไขใดจะเข้าสู่สแนปชอตถัดไป จากนั้น git commit บันทึกสแนปชอตนั้นลงในประวัติ พื้นที่กลางระหว่างสองขั้นตอนนี้เรียกว่าดัชนี หรือ staging area ซึ่งหลายคนไม่เข้าใจเป็นเวลาหลายปี และเป็นสิ่งที่ทำให้คอมมิตมีความหมาย

สามสถานะของไฟล์: ไดเรกทอรีทำงาน ดัชนี และประวัติ

การเปลี่ยนแปลงทุกอย่างผ่านสามพื้นที่:

  1. ไดเรกทอรีทำงาน คุณแก้ไขไฟล์ในเอดิเตอร์
  2. ดัชนี (stage) git add นำเวอร์ชันปัจจุบันของไฟล์ไปไว้ในร่างของคอมมิตถัดไป
  3. ประวัติ git commit เปลี่ยนเนื้อหาของดัชนีเป็นคอมมิตที่มีผู้เขียน วันที่ และข้อความ

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

เหตุผลที่ต้องมีดัชนี

ลองนึกภาพ ภายในหนึ่งชั่วโมงคุณแก้บั๊กการอนุญาตเข้าใช้งาน แก้คำพิมพ์ผิดใน README และเริ่มฟีเจอร์ใหม่ หากไม่มีดัชนี ทุกอย่างจะเข้าไปในคอมมิตเดียวชื่อ "เบ็ดเตล็ด" แต่เมื่อมีดัชนี คุณทำคอมมิตแยกกันสามรายการ ซึ่งแต่ละรายการเข้าใจง่าย ตรวจรีวิวได้ง่าย และย้อนกลับได้โดยไม่กระทบส่วนอื่น

git status: อะไรถูกแก้ไขและอะไรถูกเตรียมไว้แล้ว

ก่อนทุกครั้งที่ add และ commit ควรดู git status นี่คือคำสั่งที่ปลอดภัยที่สุดของ Git เพราะไม่เปลี่ยนแปลงอะไรเลย

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/auth.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/profile.js
  • Changes to be committed อยู่ในดัชนีแล้ว และจะเข้าสู่คอมมิต
  • Changes not staged ถูกแก้ไขแต่ยังไม่ได้เพิ่มเข้าดัชนี
  • Untracked files เป็นไฟล์ใหม่ที่ Git ยังไม่รู้จัก

รูปแบบสั้นจะสะดวกเมื่อมีไฟล์จำนวนมาก:

$ git status -s
M  src/auth.js
 M README.md
?? src/profile.js

คอลัมน์แรกคือสถานะในดัชนี คอลัมน์ที่สองคือสถานะในไดเรกทอรีทำงาน M ทางซ้ายหมายถึง "แก้ไขและเพิ่มแล้ว" ทางขวาหมายถึง "แก้ไขแต่ยังไม่เพิ่ม" และ ?? หมายถึงไฟล์ที่ไม่ถูกติดตาม

git add: เพิ่มการเปลี่ยนแปลงเข้าดัชนี

คำสั่งสิ่งที่เพิ่ม
git add file.jsไฟล์ที่ระบุ
git add src/ทุกอย่างที่แก้ไขในโฟลเดอร์
git add .การเปลี่ยนแปลงทั้งหมดในโฟลเดอร์ปัจจุบันและที่อยู่ข้างใน รวมไฟล์ใหม่และไฟล์ที่ถูกลบ
git add -Aการเปลี่ยนแปลงทั้งหมดในรีโพซิทอรี
git add -uเฉพาะไฟล์ที่ถูกติดตามอยู่แล้ว ไม่แตะไฟล์ใหม่
git add -pแบบโต้ตอบ ทีละส่วน (hunk)
git add '*.css'ไฟล์ที่ตรงกับรูปแบบ

คำพ้องของ git add คือ git stage เป็นคำสั่งเดียวกัน จึงเป็นที่มาของคำว่า "stage" หรือ "สเตจ"

git add -p: เพิ่มเฉพาะบางส่วนของไฟล์

เทคนิคที่ถูกมองข้ามบ่อยที่สุด หากในไฟล์เดียวมีการแก้ไขสองอย่างปนกัน git add -p จะแสดงทีละส่วนและถามว่าจะเพิ่มหรือไม่:

$ git add -p src/auth.js
@@ -12,7 +12,7 @@ function login(user) {
-  if (user.password = input) {
+  if (user.password === input) {
(1/2) Stage this hunk [y,n,q,a,d,s,e,?]?

y คือเพิ่ม n คือข้าม s คือแบ่งส่วนให้เล็กลง q คือออก ด้วยวิธีนี้ไฟล์เดียวสามารถกลายเป็นคอมมิตที่เป็นระเบียบสองรายการ

วิธีตรวจว่าอะไรจะเข้าสู่คอมมิต

git diff            # การเปลี่ยนแปลงที่ยัง ไม่ ถูกเพิ่มเข้าดัชนี
git diff --staged   # สิ่งที่อยู่ในดัชนีแล้วและจะเข้าสู่คอมมิต

ความสับสนที่พบบ่อย: หลัง git add คำสั่ง git diff ธรรมดาจะไม่แสดงอะไร ไม่ได้หมายความว่าการเปลี่ยนแปลงหายไป มันอยู่ในดัชนี ให้ดู git diff --staged (คำพ้องคือ --cached)

วิธีนำไฟล์ออกจากดัชนี

git restore --staged file.js

การแก้ไขในไฟล์ยังคงอยู่ เพียงแค่ไฟล์นั้นไม่ได้เตรียมไว้สำหรับคอมมิตอีกต่อไป ในเวอร์ชัน Git รุ่นเก่า ใช้ git reset HEAD file.js ซึ่งยังใช้งานได้อยู่

git commit: บันทึกการเปลี่ยนแปลง

git commit -m "แก้การตรวจสอบรหัสผ่านตอนเข้าสู่ระบบ"
[main 3f2a9c1] แก้การตรวจสอบรหัสผ่านตอนเข้าสู่ระบบ
 1 file changed, 1 insertion(+), 1 deletion(-)

ผลลัพธ์แสดงบรานช์ แฮชย่อของคอมมิตใหม่ และสถิติ คอมมิตจะรวมเฉพาะเนื้อหาของดัชนี หากคุณแก้ไฟล์อีกครั้งหลัง git add การแก้ไขใหม่จะไม่เข้าไปในคอมมิต จนกว่าคุณจะเพิ่มอีกครั้ง

แฟล็กที่มีประโยชน์ของ git commit

คำสั่งทำอะไร
git commitเปิดเอดิเตอร์สำหรับข้อความหลายบรรทัด
git commit -m "..."ใส่ข้อความในคำสั่งเลย
git commit -am "..."เพิ่มไฟล์ที่ถูกติดตามและถูกแก้ไขทั้งหมดแล้วคอมมิต ไฟล์ใหม่จะไม่รวมอยู่ด้วย
git commit -vแสดง diff ในเอดิเตอร์ระหว่างที่คุณเขียนข้อความ
git commit --amendเขียนทับคอมมิตล่าสุด
git commit --allow-empty -m "..."คอมมิตว่าง เช่น เพื่อรัน CI ใหม่

หากเอดิเตอร์เริ่มต้นคือ Vim และคุณติดอยู่ในนั้น: พิมพ์ข้อความ กด Esc แล้วพิมพ์ :wq และ Enter เปลี่ยนเอดิเตอร์ได้ด้วย git config --global core.editor "code --wait" หรือ nano

วิธีเขียนข้อความคอมมิตที่ดี

ข้อความจะถูกอ่านอีกหกเดือนต่อมา เมื่อมีคนค้นหาว่าบั๊กมาจากไหน ข้อความควรตอบคำถาม "คืออะไรและทำไม" ไม่ใช่ "วันนี้ฉันทำอะไรไปบ้าง"

  • บรรทัดแรกไม่เกิน 50–72 ตัวอักษร บอกแก่นของการเปลี่ยนแปลง
  • ใช้รูปคำสั่งหรือกลาง ๆ เช่น "Add email validation" หรือ "Fix login redirect"
  • หากจำเป็น ให้เว้นบรรทัดว่างแล้วเขียนรายละเอียด ว่าทำไมจึงทำแบบนี้ และมีข้อจำกัดอะไร
  • หนึ่งคอมมิตคือหนึ่งการเปลี่ยนแปลงเชิงตรรกะ

ไม่ดี: fix, edits, wip, asdf ดี: Fix crash in cart when product list is empty หลายทีมใช้รูปแบบ Conventional Commits เช่น feat: ..., fix: ..., docs: ...

git commit --amend: แก้คอมมิตล่าสุด

พิมพ์ข้อความผิดหรือลืมเพิ่มไฟล์ ไม่จำเป็นต้องทำคอมมิตใหม่ "ลืมไฟล์" ใช้ amend

แก้ข้อความ

git commit --amend -m "แก้การตรวจสอบรหัสผ่านตอนเข้าสู่ระบบ"

เพิ่มไฟล์ที่ลืม

git add forgotten.js
git commit --amend --no-edit

แฟล็ก --no-edit คงข้อความเดิมไว้

สิ่งสำคัญเกี่ยวกับ amend

--amend ไม่ได้ "แก้ไข" คอมมิต แต่สร้างคอมมิตใหม่ที่มีแฮชต่างออกไป และแทนที่ของเดิม ตราบใดที่คอมมิตนั้นอยู่กับคุณคนเดียว การทำเช่นนี้ปลอดภัย หากคุณ git push ไปแล้ว หลัง amend การ push ปกติจะถูกปฏิเสธ และคุณต้องใช้ git push --force-with-lease ในบรานช์ที่มีคนอื่นทำงานอยู่ อย่าทำเช่นนั้น เพราะคุณจะเขียนประวัติทับสิ่งที่เพื่อนร่วมทีมกำลังต่อยอดอยู่

หาก amend ผิดพลาด คอมมิตเดิมยังหาได้ใน git reflog และกลับไปได้ด้วย git reset --soft HEAD@{1}

ข้อผิดพลาดที่พบบ่อย

"nothing to commit, working tree clean"

ไม่มีการเปลี่ยนแปลง อาจคอมมิตไปหมดแล้ว หรือคุณกำลังแก้ไฟล์ในโฟลเดอร์อื่น ตรวจด้วย git status และเส้นทางของโปรเจกต์

"no changes added to commit"

ไฟล์ถูกแก้ไขแต่ยังไม่ได้เพิ่มเข้าดัชนี ให้รัน git add หรือใช้ git commit -am สำหรับไฟล์ที่ถูกติดตามอยู่แล้ว

"Please tell me who you are"

ยังไม่ได้ตั้งชื่อและอีเมล รันครั้งเดียว: git config --global user.name "ชื่อ" และ git config --global user.email "อีเมล"

คอมมิตสิ่งที่ไม่ควรคอมมิต

หากคอมมิตยังไม่ได้ส่งขึ้นไป ให้ยกเลิกโดยคงการเปลี่ยนแปลงไว้ในดัชนี: git reset --soft HEAD~1 จากนั้นนำส่วนที่ไม่ต้องการออกด้วย git restore --staged แล้วคอมมิตใหม่ หากรหัสลับหลุดเข้าไปในคอมมิต ให้ถือว่ารหัสนั้นถูกเปิดเผยแล้วและออกรหัสใหม่

git add . เพิ่มขยะเข้ามา

ตั้งค่า .gitignore ไว้ล่วงหน้า และตรวจ git status ก่อนคอมมิตเสมอ ไฟล์ที่ถูกติดตามอยู่แล้วจะหยุดถูกติดตามก็ต่อเมื่อรัน git rm --cached ไฟล์

คอมมิตจริง ๆ คืออะไร

คอมมิตไม่ได้เก็บความต่างจากเวอร์ชันก่อนหน้า แต่เก็บสแนปชอตของทั้งโปรเจกต์: การอ้างถึงต้นไม้ไฟล์ การอ้างถึงคอมมิตพาเรนต์ ผู้เขียน วันที่ และข้อความ ไฟล์ที่ไม่เปลี่ยนแปลงจะไม่ถูกคัดลอก Git อ้างถึงอ็อบเจกต์ที่บันทึกไว้แล้ว ประวัติจึงกินพื้นที่น้อย

แฮชของคอมมิต (เช่น 3f2a9c1) คำนวณจากเนื้อหาทั้งหมดนี้ เปลี่ยนตัวอักษรเดียวในข้อความ แฮชก็จะต่างออกไป นี่คือเหตุผลที่ --amend และ rebase สร้างคอมมิตใหม่แทนการแก้ไขของเดิม และเหตุผลที่ประวัติที่ถูกเขียนใหม่ขัดแย้งกับสำเนาของเพื่อนร่วมทีม

ดูประวัติแบบย่อได้ด้วย:

git log --oneline --graph -10

แต่ละบรรทัดคือหนึ่งคอมมิต หากข้อความในประวัติของคุณเข้าใจได้โดยไม่ต้องเปิด diff แสดงว่าคุณทำถูกต้อง

วงจรการทำงานประจำวัน

git status                 # มีอะไรเปลี่ยนบ้าง
git diff                   # ดูการแก้ไข
git add -p                 # เลือกสิ่งที่ต้องการ
git diff --staged          # ตรวจว่าอะไรจะเข้าคอมมิต
git commit -m "คำอธิบายที่เข้าใจได้"
git log --oneline -5       # ตรวจว่าคอมมิตอยู่ในประวัติ
git push                   # ส่งขึ้นเซิร์ฟเวอร์

คอมมิตบันทึกการเปลี่ยนแปลงไว้ในเครื่องเท่านั้น หากต้องการให้ไปถึง GitHub หรือเซิร์ฟเวอร์อื่น ต้องใช้ git push

สรุป

git add รวบรวมสแนปชอตถัดไปไว้ในดัชนี git status และ git diff --staged แสดงว่าอะไรจะเข้าไปในนั้น ส่วน git commit บันทึกสแนปชอตลงในประวัติ ทำคอมมิตเล็ก ๆ ที่มีเหตุผล พร้อมข้อความที่ชัดเจน และใช้ git commit --amend สำหรับแก้ไขเล็กน้อยของคอมมิตล่าสุดก่อนการ push ดัชนีและการสองขั้นตอน add → commit ถูกฝึกจริงในหลักสูตร Git

คำถามที่พบบ่อย

ควรคอมมิตบ่อยแค่ไหน?

ทุกครั้งที่งานการเปลี่ยนแปลงเชิงตรรกะหนึ่งเสร็จและโปรเจกต์อยู่ในสถานะใช้งานได้ ดีกว่าคอมมิตจำนวนมากเล็ก ๆ มากกว่าคอมมิตเดียวขนาดใหญ่ตอนท้ายวัน

มีคำสั่ง git save ใน Git หรือไม่?

ไม่มี การบันทึกคือ git add และ git commit ส่วนการพักงานที่ยังไม่เสร็จชั่วคราวใช้ git stash

จะดูว่ามีอะไรอยู่ในคอมมิตได้อย่างไร?

คำสั่ง git show แฮช จะแสดงผู้เขียน ข้อความ และการเปลี่ยนแปลงทั้งหมดของคอมมิต หากไม่ใส่แฮช จะแสดงคอมมิตล่าสุด

git add . ต่างจาก git add -A อย่างไร?

ใน Git เวอร์ชันใหม่ ทั้งสองคำสั่งเพิ่มไฟล์ใหม่ ไฟล์ที่แก้ไข และไฟล์ที่ถูกลบ ความต่างอยู่ที่ขอบเขต git add . ทำงานกับโฟลเดอร์ปัจจุบันและโฟลเดอร์ย่อย ส่วน git add -A ทำงานกับทั้งรีโพซิทอรี

แก้คอมมิตที่ไม่ใช่คอมมิตล่าสุดได้หรือไม่?

ได้ ด้วย interactive rebase: git rebase -i แฮชของพาเรนต์ จากนั้นทำเครื่องหมายคอมมิตที่ต้องการเป็น edit หรือ reword ทำเช่นนี้เฉพาะกับคอมมิตที่ยังไม่ได้ส่งเข้าบรานช์ที่ใช้ร่วมกัน

เรียนหัวข้อนี้ผ่านวิดีโอ

หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Git с нуля до уверенного разработчика”

ไปที่คอร์ส