Chương 08 — Git & SSH
Git là toolkit hằng ngày của dev: track lịch sử code, hợp tác qua remote, undo nhầm lẫn. SSH là kênh bảo mật để bạn nói chuyện với GitHub, server, máy đồng nghiệp. Học chương này, bạn không còn lệ thuộc vào nút bấm trong VS Code/SourceTree nữa — terminal nhanh hơn và mạnh hơn rất nhiều.
1. Git CLI cơ bản
Git theo dõi snapshot của project theo thời gian. Mỗi commit là một snapshot có hash riêng. Bạn có thể quay về bất kỳ commit nào.
Khởi tạo & commit đầu tiên
$ git init # tạo repo .git/ trong folder hiện tại
$ git status # xem file nào đã/chưa track, đã/chưa stage
$ git add README.md # stage 1 file
$ git add . # stage tất cả thay đổi
$ git commit -m "init project" # tạo commit với message
$ git log --oneline --graph # xem lịch sử ngắn gọn dạng đồ thị
3 vùng quan trọng nhất
| Vùng | Mô tả | Lệnh đưa vào |
|---|---|---|
| Working dir | File bạn đang sửa | (chỉnh sửa file) |
| Staging (index) | Đã chọn để commit | git add |
| Repository | Đã commit, có hash | git commit |
g co = git checkout), và mọi tính năng Git đều có sẵn — không có chuyện
"GUI chưa hỗ trợ feature đó".
2. Branch & merge
Branch là nhánh phát triển song song. Bạn làm feature X trên một branch riêng, không đụng tới main, đến khi xong thì merge vào.
$ git branch # liệt kê branch local
$ git branch feature-login # tạo branch mới (nhưng chưa chuyển sang)
$ git checkout feature-login # chuyển sang branch đó (cách cũ)
$ git checkout -b feature-login # tạo + chuyển trong 1 lệnh
$ git switch feature-login # chuyển branch (cách mới, rõ nghĩa hơn)
$ git switch -c feature-login # tạo + chuyển (tương đương -b)
Merge vào main
$ git switch main # về main
$ git merge feature-login # gộp feature vào main
$ git branch -d feature-login # xóa branch đã merge xong
$ git log --all --graph --decorate # xem mọi branch dạng đồ thị
switch hay checkout?checkout thành 2 lệnh rõ nghĩa hơn: switch (chuyển branch) và
restore (khôi phục file). Dùng cái mới — đỡ nhầm lẫn, vì checkout làm quá nhiều việc.
3. Rebase & cherry-pick
Cả hai đều là cách "viết lại lịch sử" — mạnh nhưng phải dùng cẩn thận.
git rebase — đặt lại nhánh lên trên
$ git switch feature-x
$ git rebase main # đem các commit của feature-x lên trên main mới nhất
So với merge: rebase tạo lịch sử thẳng, không có merge commit. Nhánh trông gọn hơn.
Interactive rebase — "biên tập" lịch sử
$ git rebase -i HEAD~5 # sửa 5 commit gần nhất
Editor mở ra, bạn chọn cho từng commit:
pick— giữ nguyênreword— đổi messagesquash— gộp vào commit trênfixup— gộp + bỏ messagedrop— xóa commit
git cherry-pick — bốc lẻ commit
$ git cherry-pick a1b2c3d # mang commit a1b2c3d sang branch hiện tại
$ git cherry-pick a1b2c3d..f4e5d6c # range commit
Hữu ích khi bạn cần fix bug từ branch khác mà không muốn merge cả branch đó.
git push --force, commit cũ của họ biến mất khỏi server. Họ pull lại sẽ conflict kinh khủng.
Quy tắc: chỉ rebase commit chưa share. Đã push lên branch chung (main, develop) thì
dùng merge hoặc revert.
4. Remote & sync
Remote là bản sao repo trên server (GitHub, GitLab, BitBucket). origin là tên remote mặc định khi clone.
$ git remote -v # xem các remote đang config
$ git remote add origin git@github.com:user/repo.git
$ git fetch origin # tải về dữ liệu mới nhưng KHÔNG merge
$ git pull origin main # = fetch + merge
$ git pull --rebase origin main # = fetch + rebase (lịch sử thẳng hơn)
$ git push origin main # đẩy commit local lên remote
$ git push -u origin feature-x # -u: set upstream để lần sau chỉ cần `git push`
fetch vs pull — khác chỗ nào?
| Lệnh | Tải về | Áp vào branch local | Khi nào dùng |
|---|---|---|---|
git fetch | ✅ | ❌ | Muốn xem có gì mới mà chưa muốn merge |
git pull | ✅ | ✅ (merge) | Muốn cập nhật ngay, có thể tạo merge commit |
git pull --rebase | ✅ | ✅ (rebase) | Muốn lịch sử thẳng, đặt commit mình lên trên |
Workflow chuẩn cho team
git switch -c feature-x— tạo nhánh từmain.- Code, commit nhiều lần, message rõ ràng.
git fetch origin && git rebase origin/main— đồng bộ với main mới nhất.git push -u origin feature-x— đẩy lên remote.- Mở Pull Request trên GitHub, review, merge.
git switch main && git pull && git branch -d feature-x— dọn dẹp.
git push --force — vũ khí hủy diệt--force ghi đè branch trên remote, xóa mọi commit người khác đã đẩy lên trong lúc bạn không nhìn.
Nếu thực sự phải force push (vd sau khi rebase nhánh feature của riêng mình), dùng:
$ git push --force-with-lease
Cờ này check xem remote có thay đổi từ lần fetch cuối không — nếu có (ai đó vừa push), nó từ chối ghi đè.
An toàn hơn nhiều. KHÔNG BAO GIỜ force push lên main/master.
5. Stash & undo — sửa sai
Đây là phần "cứu sống" — biết undo đúng cách giúp bạn tự tin thử nghiệm.
git stash — cất tạm thay đổi
$ git stash # cất working dir + staging vào ngăn kéo
$ git stash list # xem các stash đang có
$ git stash pop # lấy lại stash mới nhất + xóa khỏi list
$ git stash apply stash@{1} # lấy stash thứ 1 (giữ nguyên trong list)
$ git stash drop stash@{0} # xóa stash
Dùng khi: đang dở dang feature thì sếp bảo "fix bug production gấp" — stash, switch sang main, fix, push, rồi stash pop quay lại.
Bảng "tôi muốn undo X → dùng lệnh nào"
| Tình huống | Lệnh |
|---|---|
| Bỏ thay đổi chưa stage trên 1 file (về như commit cuối) | git restore file.js |
Bỏ git add (unstage), giữ thay đổi | git restore --staged file.js |
| Sửa message commit cuối (chưa push) | git commit --amend |
| Bỏ commit cuối, giữ thay đổi đã staged | git reset --soft HEAD~1 |
| Bỏ commit cuối, giữ thay đổi nhưng unstage | git reset --mixed HEAD~1 (mặc định) |
| Bỏ commit cuối + xóa luôn thay đổi (NGUY HIỂM) | git reset --hard HEAD~1 |
| Đảo commit đã push (tạo commit mới đảo ngược) | git revert <sha> |
reset --soft vs --hard--soft: chỉ lùi con trỏ commit, file giữ nguyên + còn trong staging. An toàn.--mixed (default): lùi commit, file giữ nguyên nhưng unstage.--hard: lùi commit + xóa hết thay đổi trong working dir. Mất sạch nếu chưa stash. Nghĩ kỹ.
revert — undo "công khai" cho commit đã push
$ git revert a1b2c3d # tạo commit MỚI đảo ngược a1b2c3d
Khác reset: revert không xóa lịch sử, chỉ thêm commit mới — an toàn cho branch public.
6. SSH cho git & server
SSH (Secure Shell) là cách xác thực không mật khẩu bằng cặp key public/private. Bạn cho server giữ public key, máy bạn giữ private key — server gửi thử thách, máy bạn ký bằng private key để chứng minh là bạn.
Bước 1 — Sinh SSH key
$ ssh-keygen -t ed25519 -C "your.email@example.com"
-t ed25519: thuật toán hiện đại, ngắn hơn và an toàn hơn RSA.-C "...": comment để dễ nhận diện key (thường là email).- Khi hỏi passphrase: gõ một mật khẩu để bảo vệ private key (khuyến nghị) hoặc Enter để bỏ qua.
Kết quả ra 2 file trong ~/.ssh/:
id_ed25519— private key. KHÔNG share, không commit.id_ed25519.pub— public key. Đem dán cho GitHub/server.
Bước 2 — Copy public key vào GitHub/GitLab
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3Nza... your.email@example.com
$ pbcopy < ~/.ssh/id_ed25519.pub # macOS: copy thẳng vào clipboard
Vào GitHub → Settings → SSH and GPG keys → New SSH key → paste public key → Save.
Bước 3 — Test kết nối
$ ssh -T git@github.com
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.
Thấy "successfully authenticated" = thành công. Giờ clone repo bằng URL git@github.com:user/repo.git sẽ không bị hỏi password nữa.
~/.ssh/config — alias cho host
Khi có nhiều server hoặc nhiều account GitHub, file config giúp gõ ngắn gọn:
# ~/.ssh/config
Host github
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
Host myserver
HostName 203.0.113.42
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_server
Sau khi config, bạn gõ:
$ ssh myserver # thay vì ssh -p 2222 deploy@203.0.113.42
$ git clone github:user/repo.git # dùng alias trong git URL
chmod 600 ~/.ssh/config và chmod 600 ~/.ssh/id_ed25519. SSH sẽ từ chối dùng key
nếu permission quá lỏng — đó là tính năng bảo mật, không phải bug. Folder ~/.ssh nên là 700.
scp & rsync — copy file qua mạng
$ scp file.txt myserver:~/uploads/ # copy 1 file lên server
$ scp -r dist/ myserver:/var/www/ # copy folder (đệ quy)
$ scp myserver:~/log.txt ./ # tải về máy local
$ rsync -avzP dist/ myserver:/var/www/ # sync folder, chỉ copy file đã đổi
$ rsync -avzP --delete dist/ myserver:/var/www/ # xóa file thừa ở dest
Bảng so sánh scp vs rsync
| Tiêu chí | scp | rsync |
|---|---|---|
| Tốc độ khi sync lại folder lớn | Copy lại từ đầu | Chỉ copy file/khúc đã đổi (delta) |
| Resume khi mất mạng | ❌ Bắt đầu lại từ 0 | ✅ Có (-P) |
| Compress khi truyền | ❌ | ✅ (-z) |
| Hiển thị progress | Có (basic) | Có (-P) — chi tiết hơn |
| Đồng bộ 2 chiều / xóa file thừa | ❌ | ✅ (--delete) |
| Có sẵn mặc định | Hầu hết hệ thống | Hầu hết hệ thống Unix |
Quy tắc đơn giản: copy 1 file nhỏ một lần → scp. Sync folder lớn / deploy / backup định kỳ → rsync -avzP.
7. Thử ngay trong terminal giả lập
Tập commit cycle ngay trong simulator — không sợ phá repo thật:
mkdir myrepo && cd myrepogit initecho "# My Repo" > README.mdgit status— thấy README.md untrackedgit add README.mdgit commit -m "init"git log --onelinegit switch -c feature-1git branch— xác nhận đang ở feature-1
8. Bài tập về nhà
Làm trên Terminal thật. Repo này thuộc về bạn — sai cứ rm -rf .git rồi làm lại:
- Tạo folder
~/Developer/git-practice,cdvào,git init. TạoREADME.md, commit đầu với message"init". - Tạo branch
feature/hellobằnggit switch -c. SửaREADME.md, commit. Quay vềmain, mergefeature/hellovào. - Trên branch mới
feature/oops, làm 3 commit lung tung. Dùnggit rebase -i HEAD~3để squash 3 commit thành 1, đổi message thành"feat: hello message". - Tạo file
secret.txt,git addnó, rồi nhận ra không nên commit. Unstage bằnggit restore --staged secret.txt. - Sinh SSH key ed25519 mới (nếu chưa có):
ssh-keygen -t ed25519 -C "email". Copy public key lên GitHub. Test bằngssh -T git@github.com. - Tạo repo trống trên GitHub (qua web UI). Trên local, chạy
git remote add origin git@github.com:user/repo.gitrồigit push -u origin main. - Tạo file
~/.ssh/configvới một Host aliasgithubtrỏ đếngithub.com.chmod 600 ~/.ssh/config. Test bằngssh -T github. - Tạo 2 folder
~/tmp/src(chứa vài file) và~/tmp/dst(rỗng). Sync bằngrsync -avzP ~/tmp/src/ ~/tmp/dst/. Sửa 1 file ở src, chạy lại — thấy rsync chỉ copy file vừa đổi. - (Bonus) Cố tình tạo conflict: trên 2 branch sửa cùng dòng, merge, đọc dấu
<<<<<<<, resolve thủ công, commit.
9. Quiz — Kiểm tra kiến thức
10. Tổng kết
- ✅ Vòng lặp git:
status → add → commit → log - ✅ Branch & merge:
switch -c,merge, xem bằnglog --all --graph - ✅ Rebase & cherry-pick — dùng cẩn thận trên branch chưa public
- ✅ Sync với remote:
fetch≠pull;--force-with-leasethay cho--force - ✅ Undo đúng cách:
restore,reset --soft/--hard,revert,stash - ✅ SSH key ed25519 cho GitHub,
~/.ssh/configvới Host alias - ✅
scpcho file lẻ,rsync -avzPcho folder lớn / deploy
Chương 9 chúng ta học tmux & productivity — chia terminal thành nhiều panel, để session chạy ngầm trên server, và những thói quen làm dev "đa nhiệm" thực sự.