Git เป็นโปรแกรมที่ผมต้องใช้ทำงานเกือบทุกวัน โปรเจ็คของที่บริษัทอยู่บน GitLab ส่วนโปรเจ็คหมาแมวของตัวเองอยู่บน GitHub
ที่บริษัทใช้ flow ที่อยู่ตรงกลางระหว่าง Gitflow กับ GitHub flow ไม่ได้มีระบบ branch อะไรซับซ้อนมาก (แล้วแต่แต่ละโปรเจ็คอีกที) pattern การใช้ Git ของผมมันก็จะเหมือนๆ เดิม วนไปวนมาอยู่แถวๆ นี้ครับ
โพสต์นี้ไม่ใช่การสอนวิธีการใช้งาน Git จึงจะไม่ได้ลงรายละเอียดแต่ละ command เยอะนะครับ
Config
$ git config <key> <value>
$ git config --get <key>
git config
เอาไว้ override ค่า config ของ repo ให้ต่างไปจาก global configgit config --get
ดูค่า config ที่ตั้งไว้ ถ้าใน repo มีค่าที่ override มาจาก global config มันก็จะไปต่อข้างล่างเวลาออกมาเป็น list
Status
$ git status
ใช้บ่อยที่สุด เพื่อเช็คว่า status ของ repo เป็นแบบที่คิดไว้หรือไม่ ผมมักจะรัน git status
ก่อน command อื่นเสมอ
สิ่งที่ผมดูจาก git status
ไม่ได้เป็นข้อความ แต่เป็น “ภาพ” ครับ
สมมุติว่า output ออกมาเป็นแบบนี้
สิ่งที่ผมเห็นคือ: ก้อนสีเหลือง ก้อนสีเขียว และก้อนสีแดง ในขนาดที่ต่างกัน
- ถ้าผมแค่ edit ไฟล์อย่างเดียว ก็เดาได้ว่าจะต้องมีแต่ก้อนสีเหลือง
- ถ้าสร้างไฟล์ใหม่ด้วย ก็อาจจะมีทั้งก้อนสีเหลืองและก้อนสีแดง
- แต่ถ้า commit ไปแล้ว รัน
git status
แล้วยังเจอก้อนๆ สีๆ อยู่ แสดงว่าต้องทำอะไรผิดแล้วล่ะ (ที่เจอบ่อยๆ คือ commit ไปแล้วแต่ลืม add file ที่ยังเป็น untracked เข้าไปด้วย)
Diff
$ git diff
$ git diff --staged
มักจะมาแพ็คคู่กับ git status
เพื่อรีวิวไฟล์ที่แก้ไปแล้ว (สถานะ modified
) ผ่าน diff view อีกที
ใส่ --staged
ไปเพื่อ diff ไฟล์ที่ add แล้ว (สถานะ staged
) แต่ยังไม่ได้ commit
และจะดู diff view ก่อน commit ทุกครั้งเพื่อเช็คอีกทีว่าทุกอย่างที่แก้ไป ใช่สิ่งที่ต้องการหรือไม่
(ผมใช้ diff-so-fancy
กับ diff view
ทำให้อ่านง่าย สบายตา)
พักหลังมานี้ได้ลองเล่นคอมโบ git diff คู่กับ fzf เพื่อดู diff ทีละไฟล์ ดูได้จากโพสต์นี้ครับ: fzf - The Fuzzy Finder
Add
$ git add .
$ git add :/
$ git add .
add ทุกไฟล์ในโฟลเดอร์ เข้าสู่ staged status$ git add :/
ใช้ตอนอยู่ใน sub-folder แล้วต้องการ add ทุกไฟล์รวมถึงที่อยู่ข้างนอกโฟลเดอร์ที่เราอยู่ แต่ยังอยู่ใน repo เดียวกัน เข้าสู่ staged status
Commit
$ git commit
$ git commit -am "message"
$ git commit --amend
สองอันแรกก็คือ commit command ธรรมดา แต่บางครั้งใช้ commit -am
ไปแล้ว แต่มาเจอทีหลังว่าดันลืม add ไฟล์ที่สร้างใหม่เข้าไปด้วย
(เพราะลืมบ่อยแบบนี้ ผมจะพยายามหลีกเลี่ยงไม่ใช้ git commit -am
แต่จะแยก git add
กับ git commit
กันไปเลย
ช้ากว่า แต่ป้องกันการลืม add ไฟล์ก่อน commit ได้ดี)
แทนที่จะต้องสร้าง commit เพิ่มอีกอันเพื่อบอกว่าลืม add ไฟล์ ผมจะรัน git add
ไปเลย แล้วใช้ git commit --amend
เพื่อสร้าง commit ใหม่แทนที่ของเดิม
เหตุการณ์คร่าวๆ ประมาณนี้
# ทำฟีเจอร์เสร็จ มีทั้งแก้ไฟล์เดิมและสร้างไฟล์ใหม่
$ git commit -am "blah blah"
$ git status
# อ้าว ลืม add ไฟล์ที่สร้างใหม่ไปด้วย!
$ git add .
$ git commit --amend
เพราะฉะนั้นจะใช้ก็ต่อเมื่อยังไม่ได้ push branch ไปที่ remote repo เท่านั้น
Reset
$ git reset
ใช้เอาไฟล์ที่ add ไปแล้วออกมาจาก staged
ให้กลับมาเป็น modified
ส่วนมากใช้เพื่อที่จะ checkout ทิ้งไฟล์ที่ add ไปแล้ว (revert) เพราะถ้า add ไปแล้วก่อนมันจะ checkout ไม่ออกครับ
Push
$ git push origin HEAD
เอาไว้ push feature branch ปัจจุบันที่ได้สร้างใหม่ในเครื่อง พร้อมกับไปสร้าง branch ใหม่ในชื่อเดียวกันบน remote repo
Pull
$ git pull
$ git fetch origin
$ git merge origin/<branch_name>
ถึงแม้ว่า pull
จะเป็น shorthand command ของ fetch+merge
แต่ผมกลับชอบ fetch+merge
มากกว่า เพราะ fetch มันจะยังไม่ทำอะไรกับไฟล์ในเครื่องเราจะกว่าเราจะ merge เอง
รู้สึกปลอดภัยดี
(แต่ก็มีบ้างเหมือนกันที่มึนๆ fetch มาอย่างเดียวแต่ลืม merge ก็มโนไปเองว่า local repo เราอัพเดทล่าสุดแล้ว)
Log
$ git log -p
ต่างจาก git log
ธรรมดาตรงที่จะแสดงผลทั้ง commit message
และส่วนของไฟล์ที่ถูกแก้ไขใน commit นั้น
Checkout
$ git checkout .
$ git checkout -
$ git checkout <commit_hash>
$ git checkout -b feature/xxx origin/feature/xxx
checkout .
revert ทุกไฟล์ที่ได้แก้ไปแล้วแต่ยังไม่ได้ commit (สถานะ modified -> clean) ใช้บ่อยเวลาทำไปสักพักแล้วพัง หรือไปผิดทางแล้วต้องกลับไปเริ่มใหม่checkout -
สลับ branch ไปยัง branch ก่อนหน้า ใช้บ่อยเวลาสลับระหว่าง main branch กับ feature branchcheckout <commit_hash>
ย้อนเวลาหาอดีต กลับไปยัง commit อันใดอันหนึ่ง ได้ใช้เวลาต้องหาว่า bug ที่กำลังเจอมันเริ่มพังตั้งแต่จาก commit ไหน ทำไมมันถึงพัง- สุดท้าย
checkout <branch_name> <remote_branch_name>
สร้าง branch ใหม่ในเครื่องเรา จาก remote branch ที่มีอยู่แล้ว
Clean
$ git clean -df
ใช้ลบไฟล์ที่ยังไม่ได้ add ทิ้งไป (ไฟล์ที่สร้างใหม่ยังเป็น untracked อยู่)
ใช้คู่กับ $ git checkout .
บ่อยๆ เวลาทำพังแล้วหัวร้อน ทิ้งหมดแล้วเอาใหม่
Stash
$ git stash
$ git stash pop
(อ่านวิธีการทำงานของ git stash ได้จากโพสต์นู้นจ้า)
นอกจากใช้รับมือกับงานด่วนแล้ว git stash ยังมีประโยชน์เวลาต้องการทดสอบไอเดียแบบรีบๆ
เช่น เวลาทำฟีเจอร์นึงโดย solution A ไปได้ครึ่งทางแต่ยังไม่เสร็จ ดันนึกหาวิธีทำอีกแบบโดยใช้ solution B ได้ แต่ก็ไม่แน่ใจว่ามันจะใช่รึเปล่า ผมมักจะใช้ git stash เก็บ solution A ไว้ แล้วลอง solution B ดู
ถ้ามันไม่เวิร์กจริงๆ ก็ยังเอา solution A ที่เก็บไว้ใน stash ออกมาทำต่อได้อยู่
Remote
$ git remote -v
$ git remote prune origin
remote -v
เพื่อดูว่า remote repo อยู่ที่ URL ไหนremote prune origin
เอาไว้ลบ feature branch ในเครื่องของเราออก หาก branch นั้นถูกลบไปจาก origin แล้ว
Branch
$ git branch -D feature/xxx
ลบ feature branch ในเครื่องเรา
Help
สุดท้ายคือ command ที่มีประโยชน์ที่สุด ก็หน้า help นั่นเอง
$ git help <sub-command>
$ man git-<sub_command>
สำหรับผมแล้ว git เป็นโปรแกรมที่ทำหน้า help (man page) ดีมากถึงมากที่สุด คำอธิบายละเอียดยิบพร้อมตัวอย่าง
Alias
นอกจาก sub-command ที่ใช้บ่อยๆ แล้ว ก็มี alias ที่ตั้งไว้ (หรือไปลอกชาวบ้านมา) และได้ใช้บ่อยๆ
[alias]
co = checkout
poh = push origin HEAD
pom = push orign master
undo = reset HEAD~1
(alias ทั้งหมดที่ผมมีอยู่ที่ dotfiles:.gitconfig
)
$ git cleanup
- เอาไว้ลบ branch ใน local repo ที่ถูก merge ไปแล้ว พร้อมๆ กันทีละหลายๆ branch$ git ldm
ดูว่าเมื่อวาน หรือวันล่าสุดที่ทำงาน ทำอะไรไปบ้าง มีประโยชน์มาก ก่อน daily standup meeting 😅
ใครมีเทคนิคการใช้ git ที่ชื่นชอบ มาแนะนำกันได้นะ ✌️