คำสั่ง Git Commands
Sirichai Teerapattarasakul / October 19, 2022
9 min read
บันทึกคำสั่ง Git เครื่องมือสำคัญสำหรับงานเขียนโปรแกรม โดยมี Command ที่ใช้บ่อย มีทั้งคำสั่งเบื้องต้นและเฉพาะทาง รวมถึงคำสั่งที่ต้องใช้เพื่อแก้ปัญหาของ Git บางอย่างพร้อมตัวอย่างการใช้งาน ตามรายการดังนี้
git init
สำหรับสร้าง Git Repository ให้กับโปรเจ็คของเรา โดยเมื่อสร้าง Directory ขึ้นมาแล้ว จากนั้นให้เข้าไปใน Directory และใช้คำสั่งตามด้านล่างนี้
git init
git clone
กรณีมีโปรเจ็คใน Repository อยู่แล้ว เราสามารถดึงลงมาในเครื่องเราได้ 2 รูปแบบ ดังนี้
- ดึงข้อมูลจาก Repository ที่อยู่บนเครื่องของเราเอง
git clone <remote_url>
- ดึงข้อมูลจาก Repository จากเครื่องอื่น
git clone <username>@<host>:<path_to_repository>
git config
การตั้งค่า Git โดยใส่ชื่อและอีเมล์
# เฉพาะ Repository นี้
git config user.name "your_name"
git config user.email "[email protected]" # เฉพาะ Repository นี้
# Repository ทั้งหมด
git config --global user.name "your_name"
git config --global user.email "[email protected]"
แสดงสถานะการตั้งค่า
git config --list # แสดงเฉพาะ Repository นี้
git config --global --list # แสดงทั้งหมด
ตั้งค่าให้ git บันทึก username, password จำไว้ในเครื่องเราเอง เพื่อไม่ให้ถาม username, password ในตอนเวลาเรา pull โค้ดลงมา
git config credential.helper store # จำเฉพาะ Repository นี้
git config --global credential.helper store # จำ Repository ทั้งหมด
git add
คำสั่งเพื่อบันทึกเปลี่ยนสถานะ (Stage) รอการ Commit
git add <file_name> # ระบุไฟล์ เช่น `git add index.html about.html`
git add . # ทุกไฟล์ที่อยู่ภายใต้ Directory ปัจจุบัน
git add --all หรือ git add -A # ทุกไฟล์ใน Project
git add *.html # หลายไฟล์ระบุนามสกุล
git commit
คำสั่งเพื่อบันทึกไฟล์ทั้งหมด ขั้นตอนนี้จะ Commit ไปที่ Repository ภายในเครื่องของเรา (Local) ส่วน -m เป็น Option “เพื่อใส่ข้อความว่าได้แก้ไขอะไรลงไปบ้าง”
git commit -m "ข้อความอธิบายการเปลี่ยนแปลง"
หรือจะรวมทั้ง add และ commit ในคราวเดียวกันก็ใช้ -am
git commit -am "ข้อความอธิบายการเปลี่ยนแปลง"
กรณีที่ commit ไปแล้ว แต่ลืมและมีแก้อะไรเล็กน้อย ทำให้ต้อง commit เพิ่มเติมเข้าไป กลายเป็นว่า commit สิ่งที่เหมือนกันเข้าไปซ้ำๆ “ทำให้ Commit มันรกโดยใช่เหตุ” ให้แก้ไขโดยเพิ่ม –amend เข้าไปด้วย เพื่อที่มันจะได้รวบเป็น commit เดียวกัน
git commit -m --amend "ข้อความอธิบายการเปลี่ยนแปลง"
# หรือ
git commit -am --amend "ข้อความอธิบายการเปลี่ยนแปลง"
Conventional Commits
เพื่อกำหนดข้อความอธิบายของ Commit ให้เข้าใจง่ายโดยใช้รูปแบบดังนี้
<type>(<scope>): <description>
ตัวอย่าง
git commit -m "feat: add blog tags"
ยกตัวอย่าง type (อาจจะมีนอกเหนือจากนี้ก็ได้ แต่ในทีมต้องเข้าใจตรงกัน)
- feat: เพิ่ม/แก้ไข feature
- fix: แก้ไข bug
- docs: เพิ่ม/แก้ไข documentation
- style: แก้ไข formatting เช่น semi colons หาย, เว้นวรรค, เคาะบรรทัด เป็นต้น
- refactor: ทำ refactoring code เช่น เปลี่ยนชื่อตัวแปร, ฟังก์ชั่น, หรือชื่อคลาส เป็นต้น
- test: เพิ่ม/แก้ไข adding missing tests, refactoring tests; no production code change)
- chore: อื่นๆ
ในส่วน scope เป็น optional สามารถปล่อยว่างได้ ใช้ในกรณีเพื่ออธิบายเพิ่มเติมย่อยเข้าไปอีก เช่น
feat(lang): add Thai language
git push
เพื่อส่งไฟล์ขึ้นไปที่ Repository (Remote)
git push <remote_name> <branch_name> # ตัวอย่าง git push origin master
คำสั่งเพื่อส่งไฟล์ขึ้นไปที่ Repository บน Git (Remote) โดยอ้างอิง Tag
git push origin <tag_name> # แบบระบุ tag
git push origin --tags # tag ทั้งหมด
git stash
คำสั่งเพื่อซ่อนการเปลี่ยนแปลง โดยประโยชน์ของมันคือ ในขณะที่เรากำลังทำงานแล้วบังเอิญมีงานแก้ Bug ด่วนแทรกเข้ามาพอดี ปัญหาคือถ้าเราแก้ Bug เวลา Push ขึ้นไปก็จะติด Code ที่เรากำลังทำอยู่ตอนนั้นไปด้วย Git stash จึงเข้ามาเพื่อช่วยเก็บซ่อน Code นี้ไว้ก่อนในเครื่องเราชั่วคราว เพื่อไม่ให้มีผลกระทบตามที่กล่าวมาโดยเราสามารถใช้คำสั่ง
git stash
git stash -u # ให้ stash ไฟล์ที่ untracked ไปด้วย
หลังจากใช้คำสั่งด้านบน จะสังเกตได้ว่า Code ที่เพิ่งเขียนไปจะหายไป เพราะมันถูกซ่อนนั่นเอง โดยเราสามารถดูรายการทั้งหมดได้โดยสั่ง
git stash list
เมื่อเราแก้ Bug และ Push ขึ้นไปแล้ว เราอยากจะนำ Code ที่ซ่อนไว้กลับมาทำต่อเราเพียงใช้คำสั่ง
git stash pop
สามารถ Clear รายการที่ถูกซ่อน โดยใช้คำสั่ง
git stash clear
git reset
คำสั่งยกเลิกเมื่อเราสั่ง git add ไปแล้ว
git reset <file_name>
คำสั่งยกเลิกเมื่อเราสั่ง git commit ไปแล้วบน Local
–hard คือ จะลบสิ่งที่เคย commit ออกไปเลย กลับไปยัง commit ก่อนหน้า
git reset --hard HEAD~1
–soft คือ จะนำสิ่งที่เคย commit กลับมายังสถานะ staged เหมือนก่อน commit แต่ add ไปหมดแล้ว
git reset --soft HEAD~1
git rm
คำสั่งลบไฟล์หรือโฟลเดอร์ออกจาก git (ไม่ใช่การลบไฟล์ออกจากโปรเจ็ค)
git rm <file_name> # ลบไฟล์
git rm -r <folder_name> # ลบโฟลเดอร์
ลบไฟล์ที่ถูกทำดัชนี หรือลบออกจาก stage (ยกเลิกเมื่อเราสั่ง git add ไปแล้ว)
git rm -r --cached . # ทั้งหมด
git rm --cached <file_name> # ระบุไฟล์
git status
คำสั่งเพื่อดูสถานะการเปลี่ยนแปลงของ Repository บนเครื่องเรา (Local) เอง เช่น เพิ่ม ,แก้ไข,ลบ ไฟล์ต่างๆ
git status
git diff
ใช้สำหรับดูว่า Code มีอะไรเปลี่ยนแปลงและแตกต่างไปบ้างเช่น มี เพิ่ม ลบ แก้ไข อะไรไปบ้าง โดยเทียบกับ Commit ที่ผ่านมา
git diff # เฉพาะ Branch หรือ Commit ID ที่เราใช้อยู่ ณ ขณะนี้
git diff <commit_id> # แบบระบุ Commit ID
git diff <commit_id> <commit_id> # เปรียบเทียบระหว่างสอง Commit
git log
คำสั่งใช้ดูประวัติการ commit ต่างๆ ของRepo โดยจะแสดง เลขcommit, commit message, ชื่อผู้เขียน, email, และเวลาที่ commit นั้นๆ
git log
git log --stat # มีสรุปการเปลี่ยนแปลงของแต่ละไฟล์
git log -p # แสดงรายละเอียดการเปลี่ยนแปลงเพิ่มหรือลบอะไรไป
git log --oneline # แสดงแต่ละlog เหลือบรรทัดเดียว git log --pretty=oneline # แสดงแต่ละlog เหลือบรรทัดเดียว แต่แสดง commit เต็มไม่ซ่อน git log --graph # แสดงเป็นเส้น Branch ให้ดูง่ายขึ้น git log --oneline --graph # ถ้าใช้แบบนี้จะดูง่ายขึ้นมาก
ส่วนแบบนี้แสดงเป็นสี บอกวัน และคนที่ Commit รวมถึงเส้น Branch
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
ซึ่งแน่นอนคำสั่งมันค่อนข้างยาวไป ดังนั้นให้เพิ่ม Alias คำสั่งเหล่านั้น ไว้ใช้ใน Git Config และ Set เป็น Global เพื่อไว้ใช้งานได้ทุกๆที่บนเครื่องของเรา ดังนี้
git config --global alias.logline "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
เวลาจะเรียกดู ก็ใช้คำสั่งสั้นๆ
git logline
git reflog
คำสั่ง git reflog เก็บประวัติการเปลี่ยนแปลงเหมือนกับ git log แตกต่างตรงที่ git log เก็บประวัติทั้งหมดที่เกิดขึ้น และเก็บไว้ที่ Repository ส่วน git reflog จะเก็บเฉพาะประวัติล่าสุดในแต่ละ Branch และเก็บไว้ที่ Local เท่านั้น
git reflog show --all # แสดงประวัติที่เกิดขึ้นทั้งหมด
git reflog show HEAD # แสดงเฉพาะประวัติที่เกิดขึ้นที่ HEAD
git reflog -- <file_name> # เลือกแสดงเฉพาะที่เกิดขึ้นกับไฟล์ที่ระบุ
git show
แสดงข้อมูลแสดงรายละเอียดแบบเฉพาะเจาะจงของแต่ละ commit
git show # แสดงรายละเอียด commit ล่าสุด
git show <commit_id> # แสดงรายละเอียดจาก commit_id
git show <tag_name> # แสดงรายละเอียดจากชื่อ tag
git show HEAD^ #แสดงรายละเอียดย้อนกลับไป 1 commit (จำนวนเครื่องหมาย ^ มีเท่าไหร่ให้ย้อนกลับไปตามจำนวนนั้น เช่น ย้อนไป 3 commit = git show HEAD^^^)
git show HEAD~3 # แสดงรายละเอียดย้อนกลับไป 3 commit
git show HEAD@{"1 month ago"} # แสดงรายละเอียดย้อนกลับไป 1 เดือน
git fetch
เพื่อใช้ดึงความเปลี่ยนแปลงจาก Remote Repository มายัง Local Repository
git fetch
git pull
คำสั่งดึงไฟล์ หรืออัพเดท Source Code ภายในเครื่อง (Local) ให้ตรงกับ Repository (Remote) โดยคำสั่ง git pull นั้นจะทำการ git fetch และ git merge ไปด้วย
git pull <remote_name> <branch_name> # ตัวอย่าง git pull origin master
git branch
Branch มีเพื่อเราจะได้แยกการพัฒนาออกจาก Branch หลัก โดย Branch เริ่มต้นจะชื่อว่า Master จากนั้นเมื่อพัฒนาเสร็จเราจึงทำการ Merge กลับมายัง Master อีกครั้ง
คำสั่งสร้าง Branch
git branch <branch_name>
คำสั่งดูรายชื่อ branch ทั้งหมด
git branch
สมมติเรามี Branch : master , dev , fixbug ผลลัพท์จะแสดง ประมาณนี้ เครื่องหมายดอกจัน * หมายถึงเรากำลังอยู่ที่ Branch นั้น
* master
dev
fixbug
ลบ Branch (Local)
git branch -d <branch_name>
ลบ Branch โดยไม่คำนึงถึงสถานะการ Merge (Local)
git branch -D <branch_name>
ลบ Branch บน Remote Repository
git push origin --delete <branch_name>
# หรือ
git push origin :<branch_name>
อัปเดต Remote Tracking Branches ทั้งหมดบน Remote Repository ลงใน Local Repository
git fetch -p
# หรือ
git fetch --prune
แสดงรายการของ Remote Tracking Branches ทั้งหมดใน Local Repository
*Remote tracking branches จะถูกอัปเดตเมื่อรันคำสั่ง _git fetch_
, _git fetch -p_
, หรือ _git pull_
git branch -r
# หรือ
git branch --remotes
แสดงรายการของ Remote Tracking Branches ทั้งหมดใน Local Repository และ Remote Repository
git branch -a
git checkout
ออกจาก Branch ปัจจุบัน ไปยัง Branch ที่มีอยู่แล้ว
git checkout <branch_name>
ออกจาก Branch ปัจจุบัน ไปยัง Branch ที่ไม่มี นั่นคือ ทำการสร้าง Branch ใหม่และสลับไป Branch นั้นทันที
git checkout -b <branch_name>
เปลี่ยนไปยัง Branch ที่ไม่มี นั่นคือทำการสร้าง Branch ใหม่และสลับไป Branch นั้นทันที โดยอ้างอิง Commit ID (บางครั้งเราอาจจะต้องย้อนกลับไปยัง Code ชุดเก่า)
git checkout -b <branch_name> <commit_id>
ยกเลิกการแก้ไขไฟล์ที่ยังไม่ได้เพิ่มไปใน staging
git checkout -— <file_name>
สลับไปมาระหว่าง Branch ก่อนหน้า (ไม่ต้องใส่ชื่อ Branch)
git checkout -
git merge
คำสั่งรวม Branch จะรวมเป็นเส้นเดียวกัน ส่วน Branch ที่มารวมจะหายไป (ข้อเสียคือ หากดูย้อนหลังจะหาจุด Merge หรือ Rollback ยาก)
git merge <branch_name>
คำสั่งรวม Branch แบบ --no-ff (no fast forward) โดย Branch ที่มารวมจะคงอยู่ (ข้อดีคือ ดูย้อนหลังหาจุด Merge เพื่อจัดการได้ง่าย)
git merge --no-ff <branch_name>
สรุปความแตกต่างระหว่างการใช้และไม่ใช้ –no-ff ดังรูปนี้
รูปจาก : Stackoverflow, Atlassian Blog
git remote
แสดงข้อมูล URL ของ Repository (Remote)
git remote -v
เพิ่ม URL ของ Repository (Remote)
git remote add origin <remote_url>
เปลี่ยน URL ของ Repository (Remote)
git remote set-url origin <remote_url>
ตั้งชื่อ Remote ใหม่ (เช่น เปลี่ยนจาก origin ไปเป็น destination จะใช้ git remote rename origin destination)
git remote rename <old_name> <new_name>
ลบ Remote
git remote rm <remote_name>
git tag
แสดง Tag ทั้งหมด
git tag
แสดงเรียงจากวันที่สร้างล่าสุด
git tag --sort=-creatordate
ค้นหา Tag
git tag -l "keyword" # ระบุคีย์เวิร์ด git tag -l "keyword*" # ระบุคีย์เวิร์ดและตามหลังด้วยอะไรก็ได้
สร้าง Tag
git tag -a <tag_name> -m "ข้อความหมายเหตุ"
สำหรับดูคำสั่ง Git ว่ามีคำสั่งอะไรบ้างและใช้งานอย่างไร
git clean
แสดงไฟล์ที่อยู่ในสถานะ Untracked
git clean -n
ลบไฟล์ที่อยู่ในสถานะ Untracked
git clean -df
git version
สำหรับแสดง Version ของ Git
git version
git help
git help # ดูว่ามีคำสั่งอะไรบ้าง
git help <command_name> # ดูวิธีการใช้งานของคำสั่งนั้นๆ
การแก้ไขกรณีเกิด Git merge conflicts
คือกรณีที่มีการแก้ไขโค้ดที่จุดเดียวกัน โดย Git ไม่สามารถที่จะรวมโค้ด (Merge) ให้เราได้ เพราะไม่สามารถตัดสินใจว่าจะเลือกการเปลี่ยนแปลงไหนกันแน่
ตัวอย่างแจ้งเตือน
Auto-merging test.html
CONFLICT (content): Merge conflict in test.html
Automatic merge failed; fix conflicts and then commit the result.
โดยในโค้ดจะแสดงเครื่องหมาย <<<<<<<
HEAD และ >>>>>>>
เราจำเป็นที่จะต้องจัดการ การเปลี่ยนแปลง เช่น เลือกการเปลี่ยนแปลงอย่างใดอย่างหนึ่งหรือแก้ไขใหม่
<<<<<<< HEAD
<div>
<h1>Sample text 1</h1>
</div>
=======
<div>
<h1>Sample text 2</h1>
</div>
>>>>>>> feature-branch
จากตัวอย่างด้านบน คือ A เปลี่ยนข้อมูล h1 เป็น Sample text 1 และ B เปลี่ยนข้อมูล h1 เป็น Sample text 2 สมมติว่ากรณีนี้ต้องการใช้โค้ดของ B ก็จะต้องลบโค้ดส่วนของ A รวมถึงเครื่องหมาย <<<<<<<
HEAD และ >>>>>>>
ออกจากโค้ดด้วย ดังนี้
<div>
<h1>Sample text 2</h1>
</div>
ทำการบันทึกนำไฟล์เข้า staging และ commit ต่อไปตามขั้นตอนปกติ
git add test.html && git commit -m "Updated h1 in test.html" && git push
การย้อนกลับไปยัง commit ก่อนหน้า
สำหรับกรณีเขียนโค้ดผิดพลาดแล้วต้องการ Undo ย้อนกลับ ไปยัง Source Code ก่อนหน้า (ควร Commit บ่อยๆ)
ระบุ commit ก่อนหน้า 1 commit ย้อนกลับไปดู code ที่ commit ก่อนหน้า (detached HEAD)
git checkout HEAD^
หรือ
git checkout HEAD~1
ระบุจำนวน commit ก่อนหน้า n commit ย้อนกลับไปดู code ที่ย้อนหลัง n commit (detached HEAD)
git checkout HEAD~n
ตัวอย่าง
git checkout HEAD~1 ย้อนกลับ 1 commit ก่อนหน้า
git checkout HEAD~2 ย้อนกลับ 2 commit ก่อนหน้า
git checkout HEAD~3 ย้อนกลับ 3 commit ก่อนหน้า
ระบุ commit ID ย้อนกลับไปดู code ของ commit ที่ระบุ (detached HEAD)
git checkout <commit-id>
ย้อนกลับถาวรโดยไม่เก็บการเปลี่ยนแปลง ย้อนกลับสถานะของ branch ไปยัง commit ที่ระบุ แบบลบการเปลี่ยนแปลงทั้งหมด
git reset --hard HEAD^
หรือ
git reset --hard <commit-id>
ย้อนกลับถาวรแต่เก็บการเปลี่ยนแปลงไว้ใน staging ย้อนกลับไป commit ก่อนหน้าแต่เก็บการเปลี่ยนแปลงใน staging
git reset --soft HEAD^
หรือ
git reset --soft <commit-id>
ย้อนกลับไปยัง tag โดยตรง ย้อนกลับไปดูสถานะโค้ดใน commit ของ tag นั้น (detached HEAD)
git checkout <tag-name>
หรือรูปแบบเจาะจง ใกล้เคียงคำสั่งแรก
git checkout tags/<tag-name>
หรือสร้าง branch ใหม่จากสถานะของ tag เพื่อพัฒนาต่อ
git checkout -b <branch-name> <tag-name>
ความหมายและผลกระทบของ detached HEAD:
- HEAD ชี้ไปที่ commit เฉพาะจุด ไม่ได้ชี้ที่สาขาใด ๆ
- การทำงานหรือ commit ในสถานะ detached HEAD จะไม่ผูกกับ branch ใดๆ ทำให้ถ้าสร้าง commit ใหม่ ๆ ขึ้นใน detached HEAD แล้วสลับไปที่ branch อื่น commits เหล่านั้นจะถูก "ลอย" อาจหายไปถ้าไม่ได้บันทึกเป็น branch ใหม่
- เหมาะกับการทดลองแก้ไขหรือย้อนดูโค้ดใน commit เก่า ๆ โดยไม่กระทบกับ branch หลัก
วิธีออกจาก detached HEAD:
- กลับมาที่ branch ปกติด้วยคำสั่ง เช่น
git checkout main
หรือgit switch main
- ถ้าต้องการเก็บการเปลี่ยนแปลงในสถานะ detached ให้สร้าง branch ใหม่ เช่น
git switch -c new-branch
สรุป detached HEAD คือสถานะที่ HEAD ชี้ไปยัง commit โดยตรง แทนที่จะชี้ไปยัง branch ทำให้การ commit ที่ทำในสถานะนี้ไม่ผูกกับ branch และอาจสูญหายได้หากไม่บันทึกเป็น branch ใหม่
ภาคผนวก
- HEAD: คือ pointer ที่เก็บทุกๆ commit โดยปกติแล้ว HEAD จะชี้ไปที่ commit ล่าสุด reference ของ HEAD จะอยู่ในรูปแบบ SHA
- detached HEAD: หมายถึงสถานะที่ตัวชี้ HEAD ของ Git ไม่ได้ชี้ไปยัง branch ใดๆ โดยตรง แต่ชี้ไปยัง commit ตัวใดตัวหนึ่งโดยตรงแทน ซึ่งต่างจากสถานะปกติที่ HEAD จะชี้ไปยัง commit ล่าสุดบน branch ปัจจุบัน