Coding Gun

Top 20 Git commands ที่ต้องรู้จัก

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

git add

คำสั่งนี้ใช้ในการเปลี่ยนจาก stage working directory(สถานะเป็น untracked) ไปเป็น staging(ถูก track การเปลี่ยนแปลง) เป็นการเตรียมสิ่งที่จะ commit เข้าไปใน local repository เราเลยจะใช้คำสั่ง add ก่อน commit เสมอแต่ก็ไม่จำเป็นต่้อง commit หลังจาก add เสมอไป

git add README.md   # add ทีละไฟล์
git add section*    # add ไฟล์หรือ folder ที่ชึ้นต้นด้วยคำว่า section
git add .           # add ทุกไฟล์ที่อยู่ใน status modified เข้าไปใน index

git branch

คำสั่งนี้ใช้ในการจัดการ branch เราสามารถสร้าง branch หรือเปลี่ยนชื่อ branch ด้วยคำสั่งนี้

# แสดงรายชื่อ local branch ทั้งหมด
git branch
git branch --list

# แสดงรายชื่อ remote branch ทั้งหมด
git branch -r

# แสดงรายชื่อ branch ทั้ง local branch และ remote branch
git branch -a

# สร้าง branch ใหม่
git branch [ชื่อ branch]

# เปลี่ยนชื่อ branch ปัจจุบันเป็นชื่อที่เราตั้ง 
# โดยที่เราต้องตั้งชื่อที่ไม่ซ้ำกับ branch ที่มีอยู่แล้ว
git branch -m [ชื่อ branch]
# เปลี่ยนชื่อโดยที่ไม่สนใจว่า branch นั้นจะมีอยู่แล้วรึเปล่า
git branch -M [ชื่อ branch] 

# ลบ branch ทิ้ง -d จะเป็นการลบ branch ที่ได้ merge แล้วเท่านั้น
git branch -d [ชื่อ branch]
# ลบโดยที่ไม่จำเป็นต้อง merge ลบทิ้งไปเฉยๆเลย
git branch -D [ชื่อ branch]

อ่านเรื่องการจัดการ branchต่อได้ที่นี่

git blame

คำสั่งที่ใช้ในการตวรจสอบว่าใครเป็นคนแก้ไข code ในส่วนที่เรามีปัญหา

git blame จะแสดง commit และ ชื่อผู้เขียนที่อ่านจาก user.name ใน git config

ถึงจะใช้คำว่า blame ที่แปลว่า ตำหนิ แต่การทำงานเป็นทีมควรจะเน้นที่การป้องกันไม่ให้ปัญหานี้เกิดซ้ำขึ้นมาอีกแทนที่จะเน้นที่การตำหนิคนทำผิด

git blame [ชื่อไฟล์]

git checkout

เราจะใช้คำสั่ง checkout ในการดึง ข้อมูลเก่ากลับมาจากใน repository ซึ่งจะ reference ด้วย commit ซึ่ง commit ที่ว่านี้จะมาจากใน branch เดียวกันหรือต่าง branch กันก็ได้ ดังนั้นเราจึงใช้ git checkout ใน 2 กรณีคือ

  1. ใช้ในการดึง commit ก่อนหน้า(ใน branch เดียวกัน) ซึ่งเราสามารถใช้ git restore แทนได้
  2. ใช้ในการดึง commit ล่าสุดใน brach อื่น(เปลี่ยน branch นั่นเอง) ซึ่งเราสามารถใช้ git switch แทนได้

เนื่องจาก git checkout นั้นใช้งานหลากหลาย ปัจจุบันจึงมี git restore และ git switch ให้ใช้งานเพื่อง่ายในการทำความเข้าใจสำหรับมือใหม่

# ดึงเอา commit ล่าสุดของ branch ที่เรากำหนดลงมาใน working tree
git checkout [ชื่อ branch]

# สร้าง branch ใหม่ก่อนที่จะทำการ checkout
# เราจะได้ไม่ต้อง run คำสั่ง git branch ก่อน
git checkout -b [ชื่อ branch ใหม่]

# ดึงเอา commit ก่อนหน้ากลับมา (เป็น commit ใน branch เดียวกัน)
git checkout --source=[commit] [file-or-path]

git clean

เป็นคำสั่งที่ใช้ในการลบ untracked file ที่อยู่ใน working directory ซึ่ง git clean จะเป็นคำสั่งที่คู่กับ git reset ซึ่ง git clean จะใช้กับ untracked file แต่ git reset จะใช้กับ tracked file

git commit

เมื่อเราสั่ง git commit จะเป็นการย้ายจาก index(tracked file) ไปยัง repository ในทุกๆการ commit เราจะต้องใส่ message หรือคำอธิบายลงไปว่า commit นี้คุณได้ทำอะไรลงไป

git commit -m "ข้อความที่อธิบายการเปลี่ยนแปลงที่เกิดขึ้นใน commit นี้"

แต่ถ้าเราต้องการให้ code ที่เราแก้ไขหรือเพิ่มเติมเข้าไป อยู่ใน commit เดิม เราจะใช้ option –amend เพื่อไม่ให้ commit นั้นเยอะเกินไป

# ถ้าไม่ใส่ -m git ถามว่าเราจะใช้ message เดิมหรือเขียนมหท่
git commit --amend

git commit --amend -m "เปลี่ยนแปลงคำอธิบายใหม่"

# ระบุไปเลยว่าใช้ message เดิม ไม่เปลี่ยนแปลง
git commit --amend --no-edit 

# ระบุว่าจะใช้ message ของ commit ไหน
git commit --amend -C [commit hash]

จำนวน commit ที่เยอะเกินไปจะทำให้ตอนย้อนกลับนั้นทำได้ยาก

git config

ใช้ในการ set configuration ของ git เช่น username และ password

# การ set ค่า
git config --global user.name "Phanupong Permpimol"
git config --global user.email "phanupong@irobust.co.th"

# การอ่านค่า config แต่ละตัว
git config --global user.name
git config --global user.email

ึซึ่ง git config จะสามารถ set ได้ 3 ระดับ

  1. local กำหนด config ตามแต่ละ repository ไฟล์ config จะถูกเก็บอยู่ใน .git/config
  2. global กำหนด config ไว้ตาม user ไฟล์ config จะถูกเก็บไว้ใน user direcrory เช่น Users/phanupong/.gitconfig
  3. system กำหนด config ให้กับทุก user ในเครื่อง(ตาม installation) แต่ละ os จะมี path ที่เก็บไฟล์ไม่เหมือนกัน เช่นบน macos จะเป็น /usr/local/etc/gitconfig

เราสามารถดึง config ปัจจุบันออกมาได้ด้วยคำสั่ง

# list config ทั้งหทด
git config --list

# list config เฉพาะใน scope ที่เราต้องการ
git config --list --local
git config --list --global
git config --list --system

# list config โดยระบุด้วยว่าไฟล์ที่เก็บอยู่ที่ไหน
git config --list --show-origin

git diff

คำสั่งนี้ช่วยให้เราเปรียบเทียบว่าแต่ละ จุดแตกต่างกันอย่างไร

# เทียบความแตกต่างระหว่าง working tree ปัจจุบันกับ index
# ก่อน git add กับหลัง git add
git diff
git diff HEAD # ใส่ HEAD หรือไม่ใส่มีค่าเท่ากัน

# เทียบความแตกต่างระหว่าง index กับ repository
# หลังจาก git add แล้วกับใน repository
git diff --cached
git diff --cahced HEAD # ใส่ HEAD หรือไม่ใส่มีค่าเท่ากัน

# ignore white space(ไม่สนใจ diff ที่เกิดจาก space, tab หรือขึ้นบรรทัดใหม่)
git diff -w

# เทียบ index(หลังจาก git add แล้ว) กับ commit ที่ต้องการ
git diff [commit]
# ระบุ commit ที่เราต้องการเทียบว่าต่างกันอย่างไร
git diff [commit] [commit]
# เทียบระหว่าง HEAD กับ commit ก่อนหน้า HEAD 2 commits
git diff HEAD..HEAD^^

# เทียบความแตกต่างระหว่าง 2 branch
git diff [branch A] [branch B]

git log

เป็นอีกคำสั่งที่เราต้องใช้ในการแก้ปัญหา โดย git log จะแสดงสิ่งที่ได้ commit เข้าไป

# แสดง commits ที่เกิดขึ้นใน repository
git log
# แสดงผลเป็นบรรทัดเดียว และถ้ามีความสัมพันธ์กันจะมีเส้นบอกเหมือนใน source tree
git log --graph --decorate —-oneline
# ระบุ commit ที่มีการเปลี่ยนแปลงของ file หรือ folder ที่เรากำหนด
git log -- [file-or-folder]
# จะเพิ่ม diff เข้ามาเพิ่ม เราจะเห็นว่าแต่ละ commit มีความแตกต่างจาก commit ก่อนหน้ายังไง
git log --patch
# ค้นหาโดยใช้ส่วนในส่วนหนึ่งของข้อความ
git log --grep [text]
# ค้นหาตาม regular expression
git log -G"regular expression" --patch
# ดึง commit ออกมาเฉพาะของผู้เขียนที่เราต้องการ ส่วนใหญ่ใช้คู่กับ git blame
git log --author "Phanupong*"
# ระบุจำนวนของ commit ที่แสดงผลออกมา
git log -3 --oneline
# ดู log ตั้งแต่ commit ที่ย้อนหลังจาก HEAD ไป 5 commit ไปจนถึง commit ล่าสุด
git log HEAD~5..HEAD
# นับจากเวลา 2 นาทีที่ผ่านมา
git log --since=2.minutes.ago
# ระบุช่วงวันที่เริ่มต้นและสิ้นสุด
git log --since="2022-10-01" --until="2022-10-05"
# แสดง commit ที่อยู่ใน master แต่ไม่อยู่ใน branch dev
git log dev..master --oneline

git merge

เป็นคำสั่งที่ใช้ในการรวม 2 branches เข้าด้วยกัน หลังจากเรา fork project ออกด้วยคำสั่ง git branch เราจะรวม ทั้ง 2 branches เข้าด้วยกันด้วยคำสั่ง

git merge

แต่นอกจากการที่เราสั่ง merge branch ด้วยคำสั่ง git merge แล้ว กระบวนการ merge จะเกิดขึ้นตอนที่เรา run คำสั่ง git pull ด้วย ยกเว้นว่าเราสั่ง git pull -r เวลา pull ลงมาจะใช้การ rebase แทนการ merge

การแตก branch มีได้หลายรูปแบบ อ่านบทความเรื่องการจัดการ branch ต่อได้ที่นี่

git pull

เป็นการดึงเอา version ล่าสุดจาก remote reppository บน server ลงมาใน local repository ในเครื่องเรา การทำงานจะแบ่งออกเป็น 2 ขั้นตอนคือ

  1. Downlod branch ที่เรากำหนดลงมาจาก remote repository
  2. Merge branch ที่ download ลงมาเข้ากับ branch ปัจจุบัน
git pull origin [ชื่อ branch]

# ใช้่การ rebase แทนที่การ merge ในขั้นตอนที่ 2
git pull -r 
git pull --rebase

# ทำการ reabase แบบ interactive mode
# เหมือนกับ git rebase -i
git pull --rebase=interactive

# ทำการเก็บ commit ที่ใช้ในกรณีที่มีการ merge
# ถ้าไม่ใส่ commit ที่เกิดจากการ merge จะถูกลบออก
git pull --rebase=preserve

git push

เป็นการส่งการเปลี่ยนแปลงที่เกิดขึ้นใน local repository ขึ้นไปไว้บน remote reppository บน server และตอนที่ส่งขึ้นไปจะเป็นการส่ง commits ที่มีทั้งหมด(series of commits) ขึ้นไปยัง remote repository

# ก่อนจะ push ต้องระบุ url ของ remote repository ก่อน
git remote add origin [remote-repository-url]

# push ขึ้นไปยัง remote repository ที่เรากำหนดในคำสั่งก่อนหน้า
git push origin [ชื่อ branch]

# -u เป็น option ที่ใช้ผู้ branch ใน local repository กับ remote repository
# ถ้าใส่ -u แล้วครั้งต่อไปคุณสามารถใช้ git pull และ git push เฉยๆได้เลย
git push -u origin [ชื่อ branch]

git reset

เป็นการนำไฟล์ย้อนกลับไปสู่จุดเริ่มต้น(undo) โดยการใช้ git reset จะมีการ reset ได้ 3 แบบ

  1. HARD เป็นการ clear ไฟล์และ commit ออกไปเลย
  2. SOFT เป็นการลบ commit ออกและย้อนกลับไปอยู่ใน index
  3. MIXED เป็นการย้อนกลับสู่สถานะเริ่มต้นของไฟล์
git reset --hard HEAD
git reset --hard [commit]
git reset --mixed [commit]
git reset --soft [commit]

# reset เฉพาะไฟล์หรือ path ที่กำหนด
git reset HEAD [file-or-path]

git rebase

เป็นการย้าย commits ที่อยู่ใน branch ปัจจุบัน ไปยัง branch อื่นๆ พูดง่ายๆคือการยก commits ไปไว้ใน branch อื่น

นอกจากจะใช้ในการย้าย commits แล้วเรายังใช้ git rebase ในการปรับ commits เช่นการ รวม commits เข้าด้วยกัน หรือลบ commit ที่ไม่ต้องการออกทำให้ commit ของเราไม่เยอะจนเกินไป

การแก้ไข commits จะเป็นการแก้ไข history ดังนั่้นเราจะไม่แก้ commits ที่ได้ push ขึ้นไปบน remote repository แล้ว เราจะแก้เฉพาะ commits ของเราใน local repository เท่านั้น

# นำเอา commit จาก branch ไหน มาไว้ใน branch ปัจจุบัน
git rebase [ชื่อ branch]

ใน git rebase จะมี option -i ให้เราใช้ interactive mode ช่วยให้เราทำงานได้ง่ายขึ้น

# ใช้งาน git rebase แบบ interactive โดยระบุ commit เริ่มต้น
git rebase -i [commit] # การ rebase จะเริ่มตั้งแต่ commit นี้ไปจนถึง HEAD(commit ล่าสุด)
# ระบุ branch ที่ต้องการนำมาใส่ไว้ใน branch ปัจจุบัน
git rebase -i origin/[branch-name]

# ถ้าการ run แบบ interactive นั้นถูกหยุดระหว่างทาง 
# ด้วยคำสั่ง break หรือเรายกเลิกด้วย Ctrl + C 
# เราจะสามารถกลับมาทำงานต่อในขั้นตอนถัดไปด้วยคำสั่ง
git rebase --continue

# ยกเลิกการ rebase ในกรณีที่เกิด conflict
git rebase --abort

git revert

เป็นคำสั่งที่ใช้ในการย้อนกลับไปที่จุดตั้งต้น คล้ายๆจะเป็นการ undo แต่การ revert นั้นจะใช้การ commit สิ่งที่ตรงข้ามกันเข้าไป เช่น ถ้า commit ที่เราต้องการ revert นั้นทำการเขียน code เพิ่มเข้าไป 1 บรรทัด พอเราทำการ revert git จะทำการลบบรรทัดที่เพิ่มเข้าไปออก

# ระบุ commit ที่ต้องการย้อนกลับไป
git revert [commit]

git restore

เป็นคำสั่งที่ทำให้ working tree หรือ index ของเรา นั้นย้อนกลับไปตรงกับใน repository

# เอา file หรือ ทุกไฟล์ใน path ที่กำหนดออกจาก index กลับสู่ working tree
# เหมือนกับการยกเลิกการ run คำสั่ง git add
git restore --staged [file-or-path]
# restore ทุกไฟล์ใน folder layouts
git restore --staged layouts/*

# restore  working tree กลับมาที่เดียวกับ HEAD
git restore .

# นำเอา HEAD กลับไปไว้ที่ index และ working tree (เหมือน git checkout)
git restore --staged --worktree [file-or-path]

# ถ้าไม่นำ HEAD กลับมาเราสามารถระบุ commit ที่ต้องการ restore ได้
git restore --source [commit] [file-or-path]

เราสามารถใช้ git restore แทนที่ git checkout ได้ดังนั้นคำสั่งจะคล้ายกัน เช่น

# ทั้ง 2 คำสั่งนี้จะเป็นการนำ HEAD กลับมาไว้ใน index แต่ไม่ได้นำมาไว้ใน working tree
git checkout HEAD .
git restore --worktree --staged .

git reflog

คำสั่ง git reflog มีไว้สำหรับแก้ปัญหา โดย git reflog จะต่างจาก git log ตรงที่ git log จะเห็นการเปลี่ยนแปลงทุกอย่างที่เกิดขึ้นเพราะถูกเก็บอยู่ใน repository แต่ git reflog จะเก็บเฉพาะตัวล่าสุดในแต่ละ branch และสิ่งที่ถูกเก็บอยู่ใน git reflog จะไม่ได้ถูกส่งขึ้นไปยัง remote repository(เก็บไว้ที่ฝั่ง client เท่านั้น)

git log จะเห็นเหตุการณ์ทั้งหมด git log จะทำงานทุกครั้งที่มีการ fetch, push หรือ pull ส่วน git reflog จะทำงานเฉาะตอน switch brach, reset หรีือ rebase

# แสดงเหตุการณ์ที่เกิดขึ้นทั้งหมด
git reflog show --all

# ดึงเฉพาะเหตุการณ์ที่เกิดขึ้นที่ HEAD
git reflog show HEAD

# ดึงเฉพาะเหตุการณ์ที่เกิดขึ้นกับไฟล์ที่เราระบุ
git reflog -- [ชื่อไฟล์]

git show

เป็นคำสั่งที่แสดงรายละเอียดในแต่ละ commit ส่วนใหญ่จะใช้ต่อจาก git log เพื่อลงรายละเอียดมากขึ้น

# แสดงรายละเอียดของ commit ก่อน commit ล่าสุด
# มีเครื่องหมาย ^ (caret) กี่ครั้งให้ย้อนกลับไปตามจำนวนนั้น
git show HEAD^

# แสดงรายละเอียดของ commit ที่ย้อนกลับไป 5 commit ก่อนหน้า
git show HEAD~5

# ระบุ commit ที่ย้อนหลังกลับไป 1 เดือนก่อนหน้า
git show HEAD@{"1 month ago"}

git status

เป็นคำสั่งที่ใช้ในการตรวจสอบสถานะว่าตอนนี้มีไฟล์ไหนอยู่ในสถานะไหนบ้าง

ซึ่งสถานะต่างๆจะถูกแบ่งออกเป็น

  1. untracked ไฟล์ถูกสร้างขึ้นมาใหม่
  2. staged ไฟล์จะเป็น staged เมื่อเราสั่ง git add
  3. unmodified ไฟล์จะเป็น unmodified เมื่อเราสั่ง git commit
  4. modified ไฟล์จะเป็น modified เมื่อเราแก้ไขไฟล์ที่เราเคย commit มาแล้ว

ไฟล์ที่เคย commit มาแล้วเราเลยใช้ git commit -am “ข้อความ” ได้เลย

git status

ลองอ่านเพิ่มเติมได้ที่ git คืออะไร

git stash

เป็นคำสั่งที่ใช้ในการเก็บข้อมูลไว้ชั่วคราว เหมือนเป็น temp table ใน database สมมุติเราอยากจะ checkout version ล่าสุดลงมา แต่เราไม่อยากจะให้สิ่งที่เรากำลังทำงานหายไป เราจะใช้ git stash ในการเก็บข้อมูล

# สร้าง stash ขึ้นมาใหม่ โดยที่เก็บส่วนที่อยู่ใน working tree(ส่วนที่ยังไม่ได้ git add) ด้วย
git stash --include-untracked

# ดึง stah ที่มีอยู่ในเครื่องนี้ออกมาดู
git stash list

# เอา git stash กลับมา
git stash apply
# ระบุชื่อ stash ที่ต้องการนำกลับมา
git stash apply [stash-name]

# ลบ stash ทิ้ง
git stash clear

git switch

ใช้ในการ switch brach ถูกนำมาแทนที่การใช้งาน git checkout ซึ่งมีการใช้งานที่หลากหลายกว่า

git switch [ชื่อ brach]

git tag

เป็นคำสั่งที่ใช้ในการระบุ version เมื่อเราต้องการ release product ของเราออกไป ซึ่งเมื่อเราสั่ง git tag มันจะไปแสดงผลในหน้า release

git tag จะเป็นคำสั่งที่สำคัญมาก เราจะได้รู้ว่าตอนย้อนกลับไป version ก่อนหน้าจะกลับไปที่ commit ไหน

ส่วนเราจะใช้ semver ในการระบุ version โดยจะใช้เป็น MAJOR.MINOR.PATCH

  1. MAJOR การเปลี่ยนแปลงโครงสร้างหรือการทำงานหลัก
  2. MINOR การเพิ่ม function หรือ feature เล็กๆน้อยๆ
  3. PATCH เป็นการแก้ไข bug หรือ defect ที่เกิดขึ้น ในส่วนนี้อาจใส่ -rc1, -rc-2 หรือ -beta เข้าไปเพื่อเป็นการระบุ stage ให้ผู้ใช้เข้าใจมากขึ้นได้ เช่น v2.0.0-rc2

git tag มี 2 ประเภทคือ

  1. lightweight tag จะเป็น tag ที่ใช้ภายในทีม ในกรณีที่เราต้องการจุดที่เราต้องการย้อนกลับไป แต่ไม่ได้เป็น version ใหม่
  2. annotated tag จะเก็บ meta data เยอะกว่าเช่น บังคับให้เราใส่ message หรือคำอธิบายลงไป ดังนั้นถ้าเราต้องการ release version ใหม่ออกไปเราจะใช้ annotated tag

เราจะเลือกใช้ annotated tag ก่อน lightweight tag เสมอ

# สร้าง lightweight tag (soft)
git tag [tagname] # default จะเป็น HEAD
git tag [tagname] [commit] # ระบุ commit ที่ต้องการสร้าง tag

# สร้าง annotated tag (hard)
git tag -a [tagname] # จะเปิด editor ขึ้นมาให้เราใส่ message
git tag -a [tagname] -m "add some message here" [commit]
# -f จะบังคับให้แก้ tag เดิมที่มีอยู่แล้วหรือใส่ tag ย้อนหลัง
git tag -a -f [tagname] [commit]

# list ทุก tag ออกมา
git tag
# list tag ออกมา โดยที่เริ่มต้นด้วย v1.0
git tag -l "v1.0*"

# ดูรายละเอียดของ tag
git show [tagname]

# ปกติตอน git push จะไม่เอา tag ขึ้นไปด้วย
# เราต้องระบุ tag ที่ต้องการ push ขึ้นไป ตอนคนอื่น pull ลงมาจะได้มี tag ด้วย
git push origin [tagname]
# push ทุกๆ tag ขึ้นไป
git push origin --tags

# ตอน checkout คุณสามารถระบุ tag ที่ต้องการ checkout ลงมาได้เลย
# ยิ่งเราสร้าง tag ได้ดีเท่าไหร่ ตอนเลือก checkout ก็จะง่ายขึ้น
git checkout [tagname]

การใช้ tag ต้องใช้แบบพอดีๆ ไม่เยอะหรือไม่น้อยจนเกินไปจะได้สามารถย้อนกลับไปได้ง่าย พยายามทำการ review ก่อนที่จะสร้าง tag เสมอ

อ่านเพิ่มเติมต่อได้ที่นี่

Phanupong Permpimol
Follow me