Mục lục
- Mục Tiêu Bài Học
- Tại Sao Git Quan Trọng Với AI Engineer
- Workflow Solo Project
- Conventional Commits
- Commit Type Mở Rộng Cho AI/ML
- Atomic Commits — Tần Suất Commit
- Branch Naming Convention
- .gitignore Cho AI Project
- Data Và Model Lớn — LFS Và DVC
- .gitattributes — Line Ending
- Rebase Vs Merge
- Pull Request Workflow Cho Solo Project
- Tagging Release
- Track Experiment Với Commit Hash
- Khi Lỡ Commit Secret
- Anti-Pattern Thường Gặp
- Tóm Tắt
- Bài Tiếp Theo
Mục Tiêu Bài Học
Sau khi hoàn thành bài này, bạn sẽ:
- Biết khi nào dùng main-only, khi nào dùng feature branch
- Viết commit message theo Conventional Commits — kể cả cho experiment ML
- Cấu hình
.gitignorechặn data, model weight, và secret - Hiểu atomic commits và tần suất commit hợp lý
- Track experiment MLflow/W&B liên kết với git commit hash
- Biết cách xử lý khi lỡ commit secret vào repo
Bài này giả định bạn đã biết git init, git add, git commit, git push, git pull, git checkout -b. Nếu chưa, nên xem qua Git basics trước.
Tại Sao Git Quan Trọng Với AI Engineer
Git không chỉ là nơi lưu code. Với AI project, nó còn giải quyết 4 vấn đề thực tế:
Experiment tracking qua code state
Khi chạy experiment, model đạt accuracy=0.84. Hai tuần sau bạn thử cấu hình khác — accuracy xuống còn 0.79. Nếu không commit đúng thời điểm, bạn không biết code state nào tương ứng với con số 0.84 kia. Commit hash giải quyết vấn đề này: mỗi experiment ghi lại SHA, sau đó git checkout <sha> để reproduce.
Reproducibility sau 6 tháng
AI project thường có vòng đời dài. Bạn có thể cần quay lại project sau nửa năm để demo cho employer, hoặc để so sánh baseline với approach mới. Nếu không có version control rõ ràng, thường phải bắt đầu lại từ đầu.
Collaboration trong team nhỏ
Ngay cả project 2 người cũng cần tránh overwrite code của nhau. Branch + PR workflow giải quyết điều này mà không cần công cụ phức tạp.
GitHub history là bằng chứng
Recruiter nhìn commit history để đánh giá workflow. Một repo có commit history đều đặn (nhiều commit nhỏ theo thời gian) khác hẳn repo chỉ có 1 commit "initial commit" chứa 200 file. Cái sau cho thấy code được dump lên sau khi xong, không phải develop iteratively.
Workflow Solo Project
a) Main branch only
Tất cả commit đẩy thẳng lên main. Phù hợp cho:
- Prototype nhanh trong 1–2 ngày
- Experiment cá nhân không có người khác đọc code
Nhược điểm: không có safety net — commit lỗi lên thẳng main. CI không được trigger per-feature. Không dùng cho portfolio repo vì không có evidence của iterative development.
b) Feature branch workflow (khuyến nghị)
Đây là pattern phù hợp nhất cho portfolio project và project cá nhân nghiêm túc:
main: stable, deployable bất kỳ lúc nào. Không commit trực tiếp.feature/x,experiment/y: develop trên đây, merge vàomainkhi xong và đã test.
# Bắt đầu 1 feature mới
git checkout main
git pull origin main
git checkout -b feature/add-rag-retrieval
# ... develop, commit nhiều lần trên branch này ...
# Merge khi xong
git checkout main
git pull origin main
git merge feature/add-rag-retrieval
git push origin main
# Xóa branch sau merge
git branch -d feature/add-rag-retrieval
git push origin --delete feature/add-rag-retrieval
Với portfolio repo, mở PR trên GitHub trước khi merge: CI sẽ chạy, bạn có thể self-review diff trước khi code vào main. GitHub sẽ giữ lại PR history — recruiter nhìn thấy bạn có workflow có tổ chức.
Conventional Commits
Conventional Commits là spec commit message được dùng rộng rãi trong open source và team engineering. Format:
<type>(<scope>): <description>
Các type chuẩn
| Type | Khi nào dùng |
|---|---|
feat |
Thêm tính năng mới |
fix |
Sửa bug |
docs |
Thay đổi docs, README — không đụng code |
refactor |
Tái cấu trúc code, không thêm feature / không fix bug |
test |
Thêm hoặc sửa test |
chore |
Bảo trì: cập nhật dependency, config, CI |
perf |
Cải thiện performance — không thêm feature |
style |
Formatting, linting — không đổi logic |
Ví dụ thực tế trong AI project
# Thêm model mới
feat(model): add XGBoost baseline classifier
# Fix bug xử lý input rỗng
fix(api): handle empty input in /predict endpoint
# Cập nhật README
docs(readme): add quick start section and architecture diagram
# Tách data_loader ra module riêng
refactor(data): split data_loader into separate module
# Cập nhật version PyTorch
chore(deps): bump torch to 2.3.0
# Tối ưu inference
perf(inference): cache tokenizer initialization to reduce latency 40ms
Scope là gì
Scope (trong dấu ngoặc) là phần code bị ảnh hưởng: model, api, data, config, training, eval... Không bắt buộc nhưng giúp history dễ scan hơn. Bỏ qua nếu change nhỏ và rõ ràng:
fix: correct typo in error message
docs: add license file
Breaking change
Nếu commit phá vỡ backward compatibility, thêm ! hoặc ghi BREAKING CHANGE: ở footer:
feat(api)!: change /predict response schema to include confidence scores
BREAKING CHANGE: field "result" renamed to "prediction", field "confidence" added.
Commit Type Mở Rộng Cho AI/ML
Conventional Commits cho phép team tự thêm type. Trong AI/ML project, 2 type sau hữu ích:
experiment
Dành cho commit thử nghiệm hyperparameter hoặc architecture — commit này thường không merge vào main, chỉ tồn tại trên nhánh experiment riêng để ghi lại code state tương ứng với 1 run.
experiment(model): try learning_rate=1e-4 with cosine scheduler
experiment(model): test LoRA rank=16 vs rank=32 on llama3-8b
experiment(retrieval): compare BM25 vs dense retrieval on 100 queries
data
Dành cho pipeline xử lý dữ liệu: cleaning step, feature engineering, schema change.
data(prep): add cleaning step to remove rows with missing age
data(features): add rolling 7-day average feature for churn model
data(schema): add "source" column to track data origin
Hai type này không nằm trong spec chính thức của Conventional Commits nhưng được chấp nhận khi team đã thống nhất. Nếu làm solo, dùng chúng nhất quán trong project.
Atomic Commits — Tần Suất Commit
Atomic commit nghĩa là 1 commit = 1 thay đổi có ý nghĩa độc lập — có thể revert mà không ảnh hưởng đến commit khác, và message mô tả đủ để hiểu mà không cần đọc diff.
Tránh hai thái cực
Commit quá to:
# Xấu — không biết thay đổi nào gây ra bug nào
git commit -m "did a lot of stuff"
# diff: 15 file, 600 dòng thay đổi, gộp cả feature mới + fix bug + refactor
Commit quá nhỏ:
# Spam — lịch sử vô nghĩa
git commit -m "fix typo"
git commit -m "fix typo again"
git commit -m "oops"
git commit -m "ok now"
Sweet spot cho feature development
Khoảng 30–60 phút work = 1 commit. Một commit tốt:
- Code có thể chạy hoặc test được (không commit broken state vào
main) - Message tóm tắt được cái gì thay đổi trong 1 dòng
- Không gộp nhiều concern không liên quan (ví dụ: không vừa fix bug vừa thêm feature trong 1 commit)
Khi đang spike / explore
Trong giai đoạn EDA hay thử nghiệm model, commit thường xuyên hơn — mỗi checkpoint đáng ghi lại (thêm visualization mới, thử 1 model mới, phát hiện data issue). Dùng experiment/ branch và commit type experiment như đã nói ở trên.
Branch Naming Convention
Branch name cần đủ mô tả để 6 tháng sau bạn biết branch đó làm gì.
| Prefix | Khi nào dùng | Ví dụ |
|---|---|---|
feature/ |
Feature mới, sẽ merge vào main | feature/add-rag-retrieval |
fix/ |
Sửa bug cụ thể | fix/api-empty-input-crash |
experiment/ |
Thử nghiệm, thường không merge | experiment/larger-context-window |
refactor/ |
Tái cấu trúc code không thêm feature | refactor/extract-config-class |
docs/ |
Chỉ chỉnh docs / README | docs/add-architecture-diagram |
Tránh: branch1, test, wip, tmp, new-branch. Những tên này không mô tả được gì và tích tụ thành "nợ" trong repo.
Quy tắc viết tên: lowercase, dùng - thay khoảng trắng, ngắn gọn nhưng đủ nghĩa. Tối đa 4–5 từ sau prefix là đủ.
.gitignore Cho AI Project
Tạo .gitignore ngay khi git init, không chờ sau. Một khi file đã được commit, xóa khỏi index tốn công hơn nhiều.
Template cho AI/ML project (Git 2.40+):
# Python
__pycache__/
*.pyc
*.pyo
.venv/
venv/
*.egg-info/
dist/
build/
# Jupyter
.ipynb_checkpoints/
*.ipynb_checkpoints
# Environment / Secrets
.env
.env.*
*.key
*.pem
# Data — không commit data lớn vào git
data/raw/
data/processed/
*.csv
*.parquet
*.tsv
*.json.gz
!data/sample.csv # ngoại lệ: sample nhỏ để reproduce
# Model weights
*.pkl
*.pth
*.pt
*.h5
*.bin
*.safetensors
*.onnx
*.engine
models/checkpoints/
# ML tracking
mlruns/
wandb/
.neptune/
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
# Logs
logs/
*.log
3 nguyên tắc cốt lõi
- Không commit data lớn. Dataset thực tế thường hàng trăm MB đến GB. Git không xử lý tốt binary lớn — repo sẽ phình to và clone chậm. Dùng DVC hoặc lưu ngoài (S3, GCS) và track bằng pointer.
- Không commit model weight. File
.pthhoặc.safetensorsvài trăm MB sẽ làm repo unusable. Dùng Git LFS hoặc Hugging Face Hub. - Không commit secret.
.env, API key, private key — một khi đã vào git history, chúng tồn tại ngay cả sau khi xóa file. Cần rewrite history để xóa thực sự (xem mục 15).
Kiểm tra .gitignore đang hoạt động
# Xem file nào sẽ bị tracked nếu bạn git add .
git status
# Kiểm tra 1 file cụ thể có bị ignore không
git check-ignore -v data/raw/train.csv
# output: .gitignore:15:data/raw/ data/raw/train.csv
# Xem list tất cả file đang bị ignore
git ls-files --others --ignored --exclude-standard
Data Và Model Lớn — LFS Và DVC
Khi cần version control cho file lớn, có 2 lựa chọn chính:
Git LFS (Large File Storage)
Git LFS thay thế file lớn bằng pointer text trong git history, file thực tế lưu trên LFS server (GitHub, GitLab hỗ trợ sẵn). Phù hợp cho asset binary không thay đổi thường xuyên: ảnh dataset, font, model checkpoint nhỏ.
# Cài Git LFS
git lfs install
# Track file .pth
git lfs track "*.pth"
git lfs track "*.safetensors"
# File .gitattributes tự động được tạo
git add .gitattributes
git commit -m "chore: configure git lfs for model weights"
DVC (Data Version Control)
DVC track data và model qua pipeline, lưu file thực tế ra remote storage (S3, GCS, Azure Blob, local NFS). Phù hợp cho ML project vì:
- Track pipeline (data → features → model) chứ không chỉ file
- Reproduce experiment với
dvc repro - Share dataset qua team mà không commit vào git
DVC được giới thiệu chi tiết trong Series 6 (MLOps). Ở bài này, điều quan trọng là biết không commit file lớn trực tiếp, và DVC là công cụ chuẩn cho vấn đề này trong ML workflow.
Khuyến nghị
- Asset binary tĩnh (ảnh mẫu, font) → Git LFS
- Dataset và model weight trong ML project → DVC
- File nhỏ (<1MB) dùng để reproduce (sample data, config) → commit trực tiếp vào git
.gitattributes — Line Ending
AI project thường có contributor dùng cả Windows, macOS, và Linux. Line ending khác nhau (CRLF trên Windows, LF trên Unix) gây ra diff giả — git thấy file thay đổi dù nội dung không đổi thực chất.
File .gitattributes đặt ở root repo kiểm soát vấn đề này:
* text=auto
*.py text eol=lf
*.md text eol=lf
*.ipynb text eol=lf
*.csv text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.sh text eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.pkl binary
*.pth binary
*.h5 binary
text=auto cho phép git tự phát hiện text file và normalize. Các dòng tiếp theo override cho từng loại file cụ thể. Dòng binary nói git không cố chuyển đổi line ending và không diff các file này.
Commit .gitattributes ngay khi tạo repo, cùng lúc với .gitignore:
git add .gitignore .gitattributes
git commit -m "chore: add gitignore and gitattributes"
Rebase Vs Merge
Đây là câu hỏi hay gây tranh luận. Hiểu trade-off để chọn đúng context:
Merge
Tạo merge commit, giữ nguyên history graph với cả 2 branch. History phản ánh đúng những gì thực sự xảy ra — khi nào tách branch, khi nào merge lại.
A---B---C---M (main)
\ /
D---E (feature/add-rag)
Ưu điểm: dễ hiểu, an toàn. Nhược điểm: history "noisy" nếu có nhiều branch nhỏ merge liên tục.
Rebase
Replay các commit của feature branch lên đỉnh main — tạo ra linear history.
A---B---C---D'---E' (main, sau rebase + merge)
Ưu điểm: history sạch, dễ đọc với git log --oneline. Nhược điểm: viết lại commit hash — nếu branch đã được push và người khác đang dùng, rebase sẽ gây conflict.
Pattern khuyến nghị cho solo/small team
- Feature branch local:
git rebase mainthường xuyên để cập nhật với main, tránh conflict lớn khi merge. - Merge PR vào main: dùng merge (hoặc squash merge) để giữ context của PR trên GitHub.
Squash merge
Squash merge gom toàn bộ commit của feature branch thành 1 commit trên main. Giúp main history sạch, mỗi entry = 1 feature/fix hoàn chỉnh:
git checkout main
git merge --squash feature/add-rag-retrieval
git commit -m "feat(retrieval): implement RAG with ChromaDB and text-embedding-3-small"
GitHub UI cũng có nút "Squash and merge" khi merge PR. Đây là lựa chọn tốt cho portfolio project.
Pull Request Workflow Cho Solo Project
Mở PR ngay cả khi làm một mình có 3 lợi ích cụ thể:
- Trigger CI: GitHub Actions chạy test tự động mỗi khi có PR — phát hiện broken code trước khi vào
main. - Self-review: Nhìn diff trên GitHub UI thường phát hiện issue mà nhìn trong editor bỏ sót.
- History: GitHub lưu PR history — recruiter/collaborator sau này có thể xem reasoning đằng sau mỗi thay đổi lớn.
Quy trình đầy đủ:
# 1. Tạo branch
git checkout -b feature/add-rag-retrieval
# 2. Develop, commit nhiều lần
git add src/retrieval.py
git commit -m "feat(retrieval): implement vector search with ChromaDB"
git add tests/test_retrieval.py
git commit -m "test(retrieval): add unit tests for chunk and embed pipeline"
# 3. Push branch lên remote
git push origin feature/add-rag-retrieval
# 4. Mở PR trên GitHub
# GitHub sẽ gợi ý tạo PR khi detect branch mới
# 5. CI chạy, tự review diff, merge (squash recommended)
# 6. Xóa branch sau merge (GitHub có nút "Delete branch")
PR description nên có: mô tả ngắn về thay đổi, cách test, screenshot nếu có UI change. Không cần dài — 3–5 dòng là đủ cho solo project.
Tagging Release
Tag đánh dấu 1 commit là "phiên bản" — hữu ích khi bạn deploy v1.0 và cần rollback sau này, hoặc khi muốn gắn release note cho 1 milestone.
# Tạo annotated tag (khuyến nghị — có message và author)
git tag -a v1.0.0 -m "Initial release: RAG chatbot on internal docs"
git push origin v1.0.0
# Hoặc push tất cả tag
git push --tags
# Xem danh sách tag
git tag -l
# Checkout về code ở 1 tag cụ thể
git checkout v1.0.0
Trên GitHub, tag tự động tạo trang Releases. Thêm changelog và attach artifact (model config, requirements.txt snapshot) để người khác biết cách reproduce đúng phiên bản đó.
Dùng Semantic Versioning: vMAJOR.MINOR.PATCH. MAJOR: breaking change. MINOR: feature mới backward-compatible. PATCH: bug fix.
Track Experiment Với Commit Hash
Khi dùng MLflow hoặc W&B để log experiment, luôn include git commit SHA trong metadata của run. Điều này đảm bảo: nhìn vào bất kỳ run nào cũng biết code state nào đã tạo ra nó.
MLflow
import subprocess
import mlflow
def get_git_sha() -> str:
try:
return subprocess.check_output(
["git", "rev-parse", "HEAD"]
).strip().decode()
except subprocess.CalledProcessError:
return "unknown"
with mlflow.start_run():
mlflow.set_tag("git_sha", get_git_sha())
mlflow.log_param("learning_rate", 1e-4)
mlflow.log_metric("f1", 0.84)
W&B (Weights & Biases)
import wandb
import subprocess
git_sha = subprocess.check_output(
["git", "rev-parse", "HEAD"]
).strip().decode()
run = wandb.init(
project="churn-prediction",
config={
"learning_rate": 1e-4,
"epochs": 10,
},
notes=f"git_sha={git_sha}",
)
run.config.update({"git_sha": git_sha})
W&B cũng có tính năng tự động detect git repo nếu chạy trong thư mục có .git — check wandb.run.metadata sau khi init để xem git info đã được capture chưa.
Điều kiện để reproduce hoạt động
- Commit code trước khi chạy experiment — nếu code uncommitted thì SHA hiện tại không phản ánh đúng state chạy.
- Không rebase / force push branch đã chứa experiment SHA đó.
- Lưu cả
requirements.txtsnapshot tương ứng với SHA (hoặc dùng virtual environment + lock file).
Khi Lỡ Commit Secret
Secret đã commit vào git history vẫn tồn tại ngay cả sau khi bạn xóa file đó và commit lại. Lý do: git lưu toàn bộ object history — commit cũ vẫn accessible qua SHA.
Bước 1 — Revoke key ngay lập tức. Đây là bước quan trọng nhất và phải làm trước. Vào dashboard của service tương ứng (OpenAI, AWS, GitHub) và rotate / revoke key. Xem key đó là compromised — không chờ cleanup git xong rồi mới revoke.
Bước 2 — Rewrite history với git filter-repo.
# Cài git-filter-repo (pip install git-filter-repo)
pip install git-filter-repo
# Xóa file chứa secret khỏi toàn bộ history
git filter-repo --invert-paths --path config/secrets.yml
# Hoặc xóa 1 pattern cụ thể (ví dụ: key string)
git filter-repo --replace-text replacements.txt
# replacements.txt nội dung:
# sk-proj-xxxxxxxxxxxx==>REDACTED_KEY
# Force push tất cả branches
git push origin --force --all
git push origin --force --tags
Bước 3 — Verify trên GitHub. Vào Security tab → Secret scanning alerts (nếu repo public, GitHub sẽ alert nếu phát hiện pattern API key). Confirm secret đã không còn xuất hiện trong history.
Lưu ý khi làm trong team: Force push lên shared branch yêu cầu tất cả member xóa local clone và clone lại. Thông báo trước khi thực hiện để tránh mất commit của người khác.
Cách tốt nhất: đừng để xảy ra ngay từ đầu. Dùng pre-commit với hook detect-secrets để chặn commit secret trước khi vào history.
Anti-Pattern Thường Gặp
Các lỗi này hay gặp ở người mới chuyển sang AI/ML từ ngành khác, hoặc người chưa quen với Git trong project thực tế:
- "Initial commit" với 50+ file — dump toàn bộ code sau khi xong project. History không cho thấy development process. Recruiter nhìn vào thấy 1 commit duy nhất ngay lập tức biết đây không phải workflow thực tế.
-
Commit message không có nghĩa.
"update","fix","changes","final","final2"— những message này không trả lời được "cái gì thay đổi và tại sao". -
Commit model weight vào git. File
.pth200MB sẽ làm repo phình to —git clonemất nhiều phút, GitHub có thể từ chối push nếu vượt 100MB/file. -
git push --forcelênmaintrong team. Force push lên shared branch có thể xóa commit của người khác nếu họ đã pull và làm việc trên đó. Chỉ force push lên branch của bạn và chỉ khi thực sự cần thiết. -
Quên
.gitignoretừ đầu. Commit.envlần đầu rồi mới xóa — secret vẫn còn trong history. Cleanup tốn thêm nhiều bước (xem mục 15). -
Commit khi code chưa chạy được.
mainbranch bị broken, CI fail liên tục — người clone repo sau không clone được code working. -
Quên
git pulltrước khi push. Kết quả: conflict khi push, hoặc push lên một base cũ làm mất commit của người khác khi không dùng merge strategy đúng. -
Branch tên không mô tả.
test1,dev,wiptích tụ mà không bao giờ merge hoặc xóa. Sau 3 tháng không ai biết branch đó để làm gì.
Tóm Tắt
- Feature branch workflow là lựa chọn tốt cho portfolio project:
mainstable, develop trên nhánh riêng, merge qua PR. - Conventional Commits (
feat,fix,refactor,chore...) giúp history có ý nghĩa và dễ scan. Thêmexperimentvàdatacho ML-specific change. - Atomic commits: 1 commit = 1 thay đổi có ý nghĩa, không quá to cũng không spam.
- Branch naming:
feature/,fix/,experiment/,refactor/+ tên mô tả. .gitignorephải có trước khigit inithoàn tất. Chặn data lớn, model weight, secret,.env.- File lớn (dataset, model): dùng DVC cho ML pipeline, Git LFS cho asset binary tĩnh.
.gitattributeskiểm soát line ending — tránh diff giả giữa Windows/Mac/Linux.- Track experiment: log git SHA vào MLflow/W&B metadata — đảm bảo mọi run đều biết code state nào tạo ra nó.
- Lỡ commit secret: revoke key ngay, rồi dùng
git filter-repođể rewrite history.
