Git
Lệnh hay dùng (đủ dùng hàng ngày)
Khởi tạo & Clone
# Khởi tạo repo mới
git init
# Clone repo từ remote
git clone <url>
# Clone một branch cụ thể
git clone -b <branch> <url>
Ví dụ thực tế:
# Bạn mới join project, clone repo về máy
git clone https://github.com/company/ecommerce-api.git
# Chỉ muốn clone branch develop (không cần toàn bộ)
git clone -b develop https://github.com/company/ecommerce-api.git
Kiểm tra trạng thái
# Xem trạng thái working directory
git status
# Xem lịch sử commit
git log
# Log ngắn gọn trên 1 dòng
git log --oneline
# Log dạng graph (nhìn được flow branch/merge)
git log --oneline --graph --all
# Xem thay đổi chưa stage
git diff
# Xem thay đổi đã stage (sẵn sàng commit)
git diff --staged
Ví dụ thực tế:
# Bạn sửa xong vài file, muốn xem đã thay đổi gì
git status
# Output:
# modified: src/UserService.java
# modified: src/UserController.java
# Untracked files:
# src/UserDTO.java
# Muốn xem cụ thể sửa gì trong UserService.java (chưa add)
git diff src/UserService.java
# Đã `git add`, muốn review lại trước khi commit
git diff --staged
# Xem 5 commit gần nhất dạng ngắn gọn
git log --oneline -5
# Output:
# a1b2c3d feat: add user registration
# e4f5g6h fix: null pointer in login
# i7j8k9l refactor: extract validation logic
# m0n1o2p feat: add email service
# q3r4s5t chore: update dependencies
Stage & Commit
# Stage một file
git add <file>
# Stage tất cả thay đổi
git add .
# Commit với message
git commit -m "message"
# Stage + Commit (chỉ với file đã tracked)
git commit -am "message"
# Sửa commit cuối (message hoặc thêm file)
git commit --amend -m "new message"
Ví dụ thực tế:
# Sửa xong UserService, chỉ muốn commit riêng file này
git add src/UserService.java
git commit -m "fix: handle null email in registration"
# Sửa nhiều file liên quan, add hết rồi commit
git add src/UserService.java src/UserController.java src/UserDTO.java
git commit -m "feat: add user registration endpoint"
# Oops! Commit xong mới thấy quên thêm file test
git add src/UserServiceTest.java
git commit --amend -m "feat: add user registration endpoint"
# → Commit trước bị thay thế, giờ gồm cả file test
# Đang code nhanh, sửa file đã tracked, commit luôn không cần add
git commit -am "fix: typo in error message"
Branch
# Liệt kê branch local
git branch
# Liệt kê tất cả branch (bao gồm remote)
git branch -a
# Tạo branch mới
git branch <branch-name>
# Chuyển sang branch khác
git checkout <branch-name>
# hoặc (Git 2.23+)
git switch <branch-name>
# Tạo và chuyển sang branch mới
git checkout -b <branch-name>
# hoặc
git switch -c <branch-name>
# Xoá branch local
git branch -d <branch-name>
# Xoá branch chưa merge (force)
git branch -D <branch-name>
# Đổi tên branch hiện tại
git branch -m <new-name>
Ví dụ thực tế:
# Bắt đầu làm feature mới, tạo branch từ develop
git switch develop
git switch -c feature/user-registration
# Code xong, push lên, tạo PR. Sau khi merge xong, xoá branch local
git switch develop
git pull
git branch -d feature/user-registration
# Branch tên sai, đổi lại
git branch -m feature/user-registraton feature/user-registration
# Muốn xoá branch mà chưa merge (ví dụ: thí nghiệm thất bại)
git branch -D experiment/new-auth-flow
Merge & Rebase
# Merge branch vào branch hiện tại
git merge <branch-name>
# Rebase branch hiện tại lên branch khác
git rebase <branch-name>
# Huỷ rebase đang dở
git rebase --abort
# Tiếp tục rebase sau khi resolve conflict
git rebase --continue
Ví dụ thực tế:
# === MERGE ===
# Feature đã xong, muốn merge vào develop
git switch develop
git pull origin develop
git merge feature/user-registration
# → Tạo merge commit, giữ lịch sử đầy đủ
# === REBASE ===
# Đang code trên feature branch, develop đã có commit mới
# Muốn cập nhật develop vào feature (lịch sử sạch, không merge commit)
git switch feature/payment
git rebase develop
# → Các commit của feature sẽ được đặt LÊN TRÊN commit mới nhất của develop
# Rebase bị conflict
# Git báo: CONFLICT in src/OrderService.java
# 1. Sửa conflict trong file
# 2. git add src/OrderService.java
# 3. git rebase --continue
# Rebase loạn quá, muốn huỷ quay về trước
git rebase --abort
Khi nào dùng Merge vs Rebase?
merge: an toàn, giữ lịch sử gốc. Dùng khi merge feature vào develop/main.rebase: lịch sử sạch, linear. Dùng khi cập nhật develop mới nhất vào feature branch CỦA BẠN.- Không rebase branch đã push và người khác đang dùng.
Remote (Push / Pull / Fetch)
# Xem danh sách remote
git remote -v
# Thêm remote
git remote add origin <url>
# Pull (fetch + merge)
git pull
# Pull với rebase thay vì merge
git pull --rebase
# Push branch lên remote
git push
# Push branch mới lên remote và set tracking
git push -u origin <branch-name>
# Fetch cập nhật từ remote (không merge)
git fetch
# Fetch tất cả remote
git fetch --all
Ví dụ thực tế:
# Sáng đi làm, pull code mới nhất từ develop
git switch develop
git pull origin develop
# Tạo feature branch, code xong, push lên lần đầu
git switch -c feature/order-export
# ... code ...
git commit -am "feat: add order CSV export"
git push -u origin feature/order-export
# → Lần sau chỉ cần `git push` là đủ (đã set tracking)
# Muốn xem remote có gì mới không, nhưng chưa muốn merge
git fetch origin
git log origin/develop --oneline -5
# → Xem commit mới trên develop mà không ảnh hưởng code local
# Pull nhưng dùng rebase cho sạch (không tạo merge commit thừa)
git pull --rebase origin develop
Stash (lưu tạm thay đổi)
# Lưu tạm thay đổi
git stash
# Lưu tạm với message
git stash push -m "message"
# Xem danh sách stash
git stash list
# Lấy lại stash gần nhất (xoá khỏi danh sách)
git stash pop
# Lấy lại stash gần nhất (giữ trong danh sách)
git stash apply
# Xoá tất cả stash
git stash clear
Ví dụ thực tế:
# Đang code feature dở, sếp bảo fix bug gấp trên develop
# Lưu tạm code đang làm
git stash push -m "WIP: user registration form"
# Chuyển sang develop, fix bug
git switch develop
git switch -c hotfix/login-crash
# ... fix bug, commit, push, tạo PR ...
# Quay lại feature, lấy code cũ ra tiếp tục
git switch feature/user-registration
git stash pop
# → Code đang làm dở quay lại đúng chỗ
# Xem có bao nhiêu stash đang giữ
git stash list
# Output:
# stash@{0}: On feature/payment: WIP: payment validation
# stash@{1}: On develop: experiment with caching
# Lấy stash cụ thể (không phải gần nhất)
git stash apply stash@{1}
Undo / Khôi phục
# Bỏ file khỏi stage (giữ thay đổi trong working directory)
git restore --staged <file>
# Khôi phục file về trạng thái commit cuối (mất thay đổi!)
git restore <file>
# Soft reset: quay về commit trước, giữ thay đổi ở staged
git reset --soft HEAD~1
# Mixed reset (mặc định): quay về commit trước, giữ thay đổi ở working directory
git reset HEAD~1
# Hard reset: quay về commit trước, XOÁ hết thay đổi
git reset --hard HEAD~1
Ví dụ thực tế:
# === Lỡ `git add` file không muốn ===
git add . # Oops! Add cả file debug vào
git restore --staged src/debug-test.java
# → File debug bị bỏ khỏi stage, các file khác vẫn staged
# === Sửa file lung tung, muốn quay về bản gốc ===
# CẢNH BÁO: mất hết thay đổi trong file này!
git restore src/UserService.java
# → File quay về đúng trạng thái commit cuối
# === Commit xong nhưng chưa muốn commit (muốn sửa thêm) ===
git reset --soft HEAD~1
# → Commit bị xoá, nhưng code vẫn ở staged, sửa thêm rồi commit lại
# === Commit sai, muốn uncommit và unstage luôn ===
git reset HEAD~1
# → Code vẫn trong working directory nhưng chưa staged
# === Commit sai hoàn toàn, muốn XOÁ SẠCH như chưa từng code ===
# CẢNH BÁO: MẤT HẾT CODE!
git reset --hard HEAD~1
# === Quay về 3 commit trước ===
git reset --soft HEAD~3
# → 3 commit bị gộp lại thành staged changes, commit lại 1 lần
Tóm tắt reset:
Lệnh Commit Stage Working Dir --soft❌ Xoá ✅ Giữ ✅ Giữ --mixed(mặc định)❌ Xoá ❌ Xoá ✅ Giữ --hard❌ Xoá ❌ Xoá ❌ Xoá
Lệnh nâng cao
Interactive Rebase
# Rebase tương tác n commit gần nhất (squash, reorder, edit, drop)
git rebase -i HEAD~<n>
Ví dụ thực tế:
# Bạn có 4 commit lộn xộn trên feature branch:
# a1b2c3d fix: typo
# e4f5g6h feat: add validation
# i7j8k9l fix: validation edge case
# m0n1o2p feat: add validation (tiếp)
# Muốn gộp thành 1 commit sạch trước khi tạo PR
git rebase -i HEAD~4
# Editor mở ra:
# pick m0n1o2p feat: add validation (tiếp)
# pick i7j8k9l fix: validation edge case
# pick e4f5g6h feat: add validation
# pick a1b2c3d fix: typo
# Sửa thành:
# pick m0n1o2p feat: add validation (tiếp)
# squash i7j8k9l fix: validation edge case
# squash e4f5g6h feat: add validation
# squash a1b2c3d fix: typo
# Save → Git gộp 4 commit thành 1, cho bạn sửa message mới
# Kết quả: 1 commit duy nhất "feat: add input validation"
Các action:
pick= giữ nguyên commitreword= giữ commit, sửa messagesquash= gộp vào commit trước, GIỮ cả 2 messagefixup= gộp vào commit trước, BỎ message commit nàydrop= xoá commitedit= dừng lại ở commit này, cho bạn sửa rồi--continue
Cherry-pick
# Áp dụng một commit cụ thể vào branch hiện tại
git cherry-pick <commit-hash>
# Cherry-pick nhiều commit
git cherry-pick <hash1> <hash2>
# Cherry-pick không tự động commit (chỉ stage)
git cherry-pick --no-commit <commit-hash>
Ví dụ thực tế:
# Team member fix bug trên branch khác, bạn cần lấy đúng commit đó
# về branch của mình mà không merge cả branch
# Tìm commit hash cần lấy
git log --oneline feature/other-branch
# x1y2z3a fix: solve timezone issue ← cần commit này
# b4c5d6e feat: something else
git switch feature/my-branch
git cherry-pick x1y2z3a
# → Commit fix timezone được copy vào branch của bạn
# Hotfix trên main cần backport về release/v2.0
git switch release/v2.0
git cherry-pick abc123f
Reflog (cứu cánh khi lỡ tay)
# Xem lịch sử mọi thao tác HEAD đã trỏ tới
git reflog
# Khôi phục về trạng thái trước đó
git reset --hard HEAD@{n}
Ví dụ thực tế:
# TRƯỜNG HỢP: Lỡ `git reset --hard` mất code!
git reflog
# Output:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3 ← lỡ tay reset
# e4f5g6h HEAD@{1}: commit: feat: important feature ← commit bị mất
# i7j8k9l HEAD@{2}: commit: fix: bug fix
# m0n1o2p HEAD@{3}: commit: chore: cleanup
# Muốn quay lại trạng thái trước khi reset
git reset --hard HEAD@{1}
# → Code quay lại! Commit "feat: important feature" sống lại
# TRƯỜNG HỢP: Lỡ xoá branch
git branch -D feature/important-work # Oops!
git reflog
# Tìm commit cuối của branch đó
git switch -c feature/important-work HEAD@{5}
# → Branch được khôi phục
Reflog giữ lịch sử ~90 ngày. Nếu mới lỡ tay, reflog hầu như luôn cứu được.
Bisect (tìm commit gây bug)
git bisect start
git bisect bad # commit hiện tại bị bug
git bisect good <commit-hash> # commit này chắc chắn OK
# Git checkout giữa → test → đánh dấu good/bad → lặp lại
git bisect reset # kết thúc
Ví dụ thực tế:
# Bug xuất hiện nhưng không biết commit nào gây ra
# Biết chắc version v2.1.0 (2 tuần trước) vẫn OK
git bisect start
git bisect bad # HEAD hiện tại bị bug
git bisect good v2.1.0 # tag v2.1.0 vẫn OK
# Git checkout giữa (ví dụ commit thứ 50/100)
# → Bạn test: bug có không?
# Nếu CÓ bug:
git bisect bad
# Nếu KHÔNG có bug:
git bisect good
# Git tiếp tục chia đôi... sau ~7 bước (log2 của 100)
# Git báo: "abc123f is the first bad commit"
# → Tìm được commit gây bug!
git bisect reset # quay về branch ban đầu
Tag
# Tạo lightweight tag
git tag <tag-name>
# Tạo annotated tag (khuyên dùng cho release)
git tag -a <tag-name> -m "message"
# Push tag lên remote
git push origin <tag-name>
# Push tất cả tag
git push --tags
# Xoá tag
git tag -d <tag-name> # local
git push origin --delete <tag-name> # remote
Ví dụ thực tế:
# Deploy xong version mới, đánh tag để đánh dấu
git tag -a v2.3.0 -m "Release 2.3.0: add payment module"
git push origin v2.3.0
# Checkout về 1 version cũ để debug
git checkout v2.1.0
# Xoá tag đánh nhầm
git tag -d v2.3.0-beta
git push origin --delete v2.3.0-beta
Submodule
# Thêm submodule
git submodule add <url> <path>
# Clone repo có submodule
git clone --recurse-submodules <url>
# Cập nhật submodule
git submodule update --init --recursive
Ví dụ thực tế:
# Project dùng shared library từ repo khác
git submodule add https://github.com/company/shared-utils.git libs/shared-utils
# Đồng nghiệp mới clone repo, submodule folder rỗng
git clone --recurse-submodules https://github.com/company/main-project.git
# hoặc nếu đã clone rồi:
git submodule update --init --recursive
Worktree (nhiều working directory cùng 1 repo)
# Tạo worktree mới cho branch
git worktree add <path> <branch>
# Liệt kê worktree
git worktree list
# Xoá worktree
git worktree remove <path>
Ví dụ thực tế:
# Đang code feature, cần fix hotfix mà không muốn stash/switch
# Tạo thêm 1 thư mục riêng cho hotfix
git worktree add ../hotfix-login hotfix/login-crash
# Giờ có 2 thư mục:
# ~/projects/ecommerce/ → feature/user-reg (đang code)
# ~/projects/hotfix-login/ → hotfix/login-crash (fix bug)
# Fix xong, xoá worktree
git worktree remove ../hotfix-login
Lợi ích: Không cần stash, không cần switch branch. Mỗi worktree là 1 thư mục riêng biệt với branch riêng.
Clean (xoá untracked files)
# Xem file nào sẽ bị xoá (dry run)
git clean -n
# Xoá untracked files
git clean -f
# Xoá untracked files + directories
git clean -fd
Ví dụ thực tế:
# Sau khi build, có nhiều file generated rác
git clean -n
# Output:
# Would remove build/output.jar
# Would remove temp/debug.log
# Would remove generated/
# Chắc chắn muốn xoá
git clean -fd
# → Xoá hết untracked files và thư mục
# CHÚ Ý: file trong .gitignore không bị xoá
# Muốn xoá cả file ignored:
git clean -fdx
Blame & Log nâng cao
# Xem ai sửa dòng nào
git blame <file>
# Log thay đổi của 1 file
git log -- <file>
# Log với diff
git log -p
# Tìm commit chứa text trong message
git log --grep="keyword"
# Tìm commit thêm/xoá string trong code
git log -S "string"
# Log giữa 2 branch
git log <branch1>..<branch2>
Ví dụ thực tế:
# Ai sửa dòng 50-60 trong UserService?
git blame -L 50,60 src/UserService.java
# Output:
# a1b2c3d (Linh 2026-03-15) public void register(User user) {
# e4f5g6h (Minh 2026-04-01) validate(user.getEmail());
# ...
# Xem lịch sử thay đổi của 1 file cụ thể
git log --oneline -- src/UserService.java
# Tìm commit nào đã xoá function "processPayment"
git log -S "processPayment" --oneline
# → Hiện commit thêm và commit xoá function đó
# Xem feature branch có gì mà develop chưa có
git log develop..feature/payment --oneline
# Tìm commit liên quan đến "fix" trong message
git log --grep="fix" --oneline
Cấu hình
# Xem config hiện tại
git config --list
# Set username/email (global)
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
# Set default branch name
git config --global init.defaultBranch main
# Set editor mặc định
git config --global core.editor "code --wait"
# Alias (shortcut tiện dụng)
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
Ví dụ thực tế:
# Sau khi cài alias, dùng lệnh ngắn hơn:
git co develop # = git checkout develop
git br # = git branch
git st # = git status
git lg # = git log --oneline --graph --all
# Set config riêng cho 1 project (không dùng --global)
git config user.email "linh@company.com"
# → Chỉ áp dụng cho repo hiện tại (work email)
# Các repo khác vẫn dùng personal email từ global config
Một số mẹo hữu ích
# Xem remote URL
git remote get-url origin
# Đổi remote URL (ví dụ: đổi từ HTTPS sang SSH)
git remote set-url origin git@github.com:company/project.git
# Xem branch nào đã merge vào branch hiện tại (safe to delete)
git branch --merged
# Xem branch nào chưa merge (đừng xoá!)
git branch --no-merged
# Tạo patch (gửi thay đổi qua email/chat)
git format-patch -1 HEAD
# Apply patch
git apply 0001-fix-something.patch
Ví dụ thực tế:
# Dọn dẹp branch cũ: tìm branch đã merge xong rồi xoá
git branch --merged
# feature/user-reg ← đã merge, xoá được
# feature/payment ← đã merge, xoá được
# * develop
git branch -d feature/user-reg feature/payment
# Đổi remote từ HTTPS sang SSH (không cần nhập password nữa)
git remote set-url origin git@github.com:linh/ecommerce-api.git