Category: reference
Git Advanced — Rebase, Stash, Bisect, Reflog
คำสั่ง Git ขั้นสูงที่ใช้บ่อยในงานจริง: interactive rebase, stash, cherry-pick, bisect, reflog, และ fixup workflow
สารบัญ
Interactive Rebase — แก้ประวัติ commit
# แก้ 3 commits ล่าสุด
git rebase -i HEAD~3
# หรือ rebase จาก specific commit
git rebase -i abc1234
Editor จะเปิดพร้อม:
pick a1b2c3d feat: add login page
pick e4f5g6h fix: typo in button text
pick i7j8k9l chore: update deps
เปลี่ยน pick เป็น action ต่างๆ:
| Action | ความหมาย |
|---|---|
pick / p | ใช้ commit นี้ (ไม่เปลี่ยน) |
reword / r | ใช้ commit นี้ แต่แก้ message |
edit / e | หยุดตรงนี้ให้แก้ไข |
squash / s | รวมกับ commit ก่อนหน้า (เก็บ message ทั้งคู่) |
fixup / f | รวมกับ commit ก่อนหน้า (ทิ้ง message นี้) |
drop / d | ลบ commit นี้ออก |
Commit Fixup Workflow
# สร้าง commit ปกติ
git commit -m "feat: add user profile page"
# ทำงานต่อ พบว่ามี typo ใน commit ก่อนหน้า
git add src/profile.ts
git commit --fixup HEAD~1 # สร้าง fixup commit อัตโนมัติ
# รวม fixup commits เข้ากับ commit หลัก
git rebase -i --autosquash HEAD~3
Stash — เก็บงานชั่วคราว
# เก็บ changes ทั้งหมด (tracked files)
git stash
# เก็บพร้อม untracked files
git stash -u
# เก็บพร้อมชื่อ (จำง่าย)
git stash push -m "wip: dark mode refactor"
# ดู stash list
git stash list
# stash@{0}: On main: wip: dark mode refactor
# stash@{1}: WIP on main: abc1234 feat: add login
# apply stash ล่าสุด (ไม่ลบ)
git stash apply
# apply แล้วลบทิ้ง
git stash pop
# apply stash เฉพาะ
git stash apply stash@{1}
# ลบ stash ทั้งหมด
git stash clear
# ดูว่า stash มีอะไร
git stash show -p stash@{0}
Cherry-pick — ย้าย commit เฉพาะ
# เอา commit เดียว
git cherry-pick abc1234
# เอาหลาย commits
git cherry-pick abc1234 def5678
# เอา range
git cherry-pick abc1234..def5678
# cherry-pick แบบไม่ commit (staged เท่านั้น)
git cherry-pick --no-commit abc1234
# เมื่อมี conflict
git cherry-pick --continue # หลังแก้ conflict
git cherry-pick --abort # ยกเลิก
Bisect — หา commit ที่ทำให้ bug เกิด
# เริ่ม bisect
git bisect start
# บอกว่า commit ปัจจุบัน (มี bug)
git bisect bad
# บอก commit ที่รู้ว่ายังดีอยู่
git bisect good v1.2.0
# Git จะ checkout commit กลาง → ทดสอบ → บอกผล
git bisect good # หรือ
git bisect bad
# ทำซ้ำจนเจอ commit แรกที่ bug เกิด
# Git จะ print: "abc1234 is the first bad commit"
# จบ bisect
git bisect reset
# Automated bisect ด้วย script
git bisect run npm test # ถ้า test pass = good, fail = bad
Reflog — Time Machine ของ Git
# ดู history ของ HEAD (รวม reset, rebase, checkout)
git reflog
# output:
# abc1234 HEAD@{0}: commit: feat: add search
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: fix: broken link
# กู้ commit ที่ accidentally reset ไป
git checkout ghi9012 # กู้มาดู
git branch recovered ghi9012 # สร้าง branch จาก commit นี้
# ยกเลิก rebase ที่ทำไปแล้ว
git reflog # หา HEAD ก่อน rebase
git reset --hard HEAD@{5} # ย้อนกลับ
Reset — ยกเลิก commits
# Soft reset — ยกเลิก commits แต่เก็บ changes ใน staging area
git reset --soft HEAD~2
# Mixed reset (default) — ยกเลิก commits, changes กลับเป็น unstaged
git reset HEAD~2
git reset --mixed HEAD~2
# Hard reset — ทิ้งทั้ง commits และ changes
git reset --hard HEAD~2 # ⚠️ ไม่สามารถกู้คืนได้ (ยกเว้นผ่าน reflog)
# Unstage file เดียว
git reset HEAD src/file.ts # mixed reset เฉพาะไฟล์
Revert — ยกเลิกแบบสร้าง commit ใหม่
# สร้าง commit ที่ undo commit ที่ระบุ (ปลอดภัยกว่า reset)
git revert abc1234
# revert แต่ไม่ commit ทันที
git revert --no-commit abc1234
# revert merge commit (ต้องระบุ parent)
git revert -m 1 abc1234 # m 1 = keep parent branch
Worktree — หลาย branches พร้อมกัน
# สร้าง worktree ใหม่ (branch แยก directory)
git worktree add ../project-hotfix hotfix/login-bug
# ทำงานใน directory แยก ไม่ต้อง stash
cd ../project-hotfix
git commit -m "fix: login timeout"
# กลับมา main worktree
cd ../project
git merge hotfix/login-bug
# ลบ worktree
git worktree remove ../project-hotfix
git worktree prune
Useful Aliases
# ~/.gitconfig
[alias]
# ดู log สวยงาม
lg = log --oneline --graph --decorate --all
# unstage file
unstage = reset HEAD --
# ดู diff ที่ staged แล้ว
staged = diff --cached
# แก้ commit message ล่าสุด
amend = commit --amend --no-edit
# ลบ branch ที่ merged แล้ว
cleanup = "!git branch --merged | grep -v '\\*\\|main\\|master\\|develop' | xargs -n1 git branch -d"
# stash แบบ quick
ss = stash push -u
sp = stash pop