ข้ามไปเนื้อหาหลัก

Category: reference

Git Advanced — Rebase, Stash, Bisect, Reflog

คำสั่ง Git ขั้นสูงที่ใช้บ่อยในงานจริง: interactive rebase, stash, cherry-pick, bisect, reflog, และ fixup workflow

· อ่านประมาณ 4 นาที

สารบัญ

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