Skip to content

Advanced Repository Management 🏗️

ROLE: HPN (Architecture & DevOps).
AUDIENCE: Engineers quản lý monorepos, shared libraries, và large assets.

Khi dự án của bạn lớn lên, bạn sẽ gặp các vấn đề mà git add && git commit không giải quyết được: shared libraries, multiple parallel workstreams, và large binary files. Module này trang bị cho bạn các công cụ nâng cao để xử lý chúng.


🎯 Mục tiêu

Sau module này, bạn sẽ:

  • Quản lý Submodules - repos lồng nhau
  • Dùng Worktrees - làm việc trên nhiều branches cùng lúc
  • Setup Git LFS - xử lý large binaries hiệu quả

📦 Git Submodules - Repos Inside Repos

The Problem

"Team A đang maintain thư viện ui-components. Team B, C, D đều muốn dùng. Copy-paste? Vendor folder? Fork?"

Solution: Git Submodules - embed một repo vào trong repo khác.

How It Works

Key insight: Main repo chỉ lưu pointer (commit SHA) đến submodule, không phải toàn bộ code.


Basic Commands

bash
# 1. Thêm submodule
git submodule add https://github.com/team/ui-components.git libs/ui-components

# 2. Clone repo có submodules
git clone --recurse-submodules https://github.com/company/main-project.git

# Hoặc nếu đã clone rồi:
git submodule update --init --recursive

# 3. Cập nhật submodule lên version mới
cd libs/ui-components
git checkout v2.0.0  # hoặc git pull origin main
cd ../..
git add libs/ui-components
git commit -m "chore: update ui-components to v2.0.0"

# 4. Xem status của submodules
git submodule status

The Pain: Detached HEAD

Vấn đề phổ biến nhất: Sau khi git submodule update, bạn ở trạng thái detached HEAD.

bash
$ cd libs/ui-components
$ git status
HEAD detached at abc123  # 😱 Không ở branch nào!

Tại sao? Submodule checkout exact commit, không phải branch tip.

Cách fix:

bash
# 1. Checkout branch thay vì commit
cd libs/ui-components
git checkout main  # or develop, etc.

# 2. Hoặc config để tự động track branch
git config -f .gitmodules submodule.libs/ui-components.branch main
git submodule update --remote --merge

Submodule Workflows

Update All Submodules

bash
# Cập nhật tất cả submodules lên latest commit của tracked branch
git submodule update --remote --merge

# Recursive (nested submodules)
git submodule update --init --recursive --remote

Remove a Submodule

bash
# 1. Deinit
git submodule deinit -f libs/ui-components

# 2. Remove from .git/modules
rm -rf .git/modules/libs/ui-components

# 3. Remove from working tree
git rm -f libs/ui-components

# 4. Commit
git commit -m "chore: remove ui-components submodule"

Foreach - Run Command in All Submodules

bash
# Pull all submodules
git submodule foreach 'git pull origin main'

# Check status of all
git submodule foreach 'git status'

# Recursive foreach
git submodule foreach --recursive 'git fetch --all'

.gitmodules File

ini
[submodule "libs/ui-components"]
    path = libs/ui-components
    url = https://github.com/team/ui-components.git
    branch = main

[submodule "libs/shared-utils"]
    path = libs/shared-utils
    url = git@github.com:team/shared-utils.git

COMMON PITFALL

Sau khi git pull main repo, luôn chạy:

bash
git submodule update --init --recursive

Nếu không, submodule của bạn sẽ ở version cũ!


🌳 Git Worktrees - Multiple Branches, One Repo

The Problem

"Đang code dở feature branch, có bug urgent cần fix trên main. Stash? Commit WIP? Cả hai đều messy."

Solution: Git Worktrees - checkout nhiều branches vào nhiều folders khác nhau, từ cùng một repo.

How It Works

Key insight: Tất cả worktrees share cùng .git/ directory → không duplicate objects.


Basic Commands

bash
# 1. Tạo worktree cho branch có sẵn
git worktree add ../app-hotfix hotfix/urgent-fix

# 2. Tạo worktree với branch mới
git worktree add -b feature/new-ui ../app-new-ui main

# 3. List all worktrees
git worktree list
# /home/user/projects/app              abc123 [main]
# /home/user/projects/app-hotfix       def456 [hotfix/urgent-fix]
# /home/user/projects/app-new-ui       ghi789 [feature/new-ui]

# 4. Remove worktree khi xong
git worktree remove ../app-hotfix

# 5. Prune stale worktrees
git worktree prune

Real-World Workflow

bash
# Scenario: Đang code feature, có bug urgent

# 1. Bạn đang ở main worktree, working on feature
cd ~/projects/myapp
git switch feature/login-redesign
# ... đang code dở ...

# 2. Có bug urgent! Tạo worktree mới, không disturb work hiện tại
git worktree add ../myapp-hotfix hotfix/critical-bug

# 3. Switch sang folder mới, fix bug
cd ../myapp-hotfix
# fix bug...
git add . && git commit -m "fix: critical security issue"
git push origin hotfix/critical-bug
# Create PR, merge...

# 4. Quay lại feature work
cd ../myapp
# Code của feature vẫn y nguyên! Không cần stash/unstash

# 5. Cleanup
cd ~/projects/myapp
git worktree remove ../myapp-hotfix

Worktree Use Cases

ScenarioWorktree Solution
Fix urgent bug mid-featureTạo worktree cho hotfix branch
Test two versions side-by-sideWorktree cho main và develop
Review PR mà không làm gián đoạn workWorktree checkout PR branch
Long-running experimentWorktree cho experiment branch

PRO TIP

Đặt worktrees ở cùng parent folder với main repo:

~/projects/
├── myapp/           (main worktree)
├── myapp-hotfix/    (hotfix worktree)
└── myapp-feature/   (feature worktree)

🗃️ Git LFS - Large File Storage

The Problem

"Designer commit file PSD 500MB. Repo clone từ 100MB lên 2GB. Mọi người muốn giết designer."

Tại sao Git xử lý binary kém?

  • Git lưu full snapshot, không delta compression cho binaries
  • Mỗi version = thêm full file size
  • Clone phải download toàn bộ history

The Solution: Git LFS

Git LFS lưu pointer trong repo, actual file trên LFS server riêng.


Setup Git LFS

bash
# 1. Install (một lần trên máy)
# macOS
brew install git-lfs

# Windows
winget install GitHub.GitLFS

# Ubuntu
sudo apt install git-lfs

# 2. Initialize trong user profile (một lần)
git lfs install

# 3. Track file types trong repo
cd your-repo
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "*.mp4"
git lfs track "assets/**"

# 4. Commit .gitattributes
git add .gitattributes
git commit -m "chore: configure Git LFS"

How LFS Works

Pointer File

Thay vì actual binary, Git repo chứa pointer file:

version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 536870912

Workflow


Essential Commands

bash
# Xem files đang được track
git lfs track

# Xem LFS files trong repo
git lfs ls-files

# Pull LFS files (nếu chưa có)
git lfs pull

# Clone với LFS files
git clone <url>  # Tự động pull LFS nếu đã install

# Clone KHÔNG có LFS files (lightweight)
GIT_LFS_SKIP_SMUDGE=1 git clone <url>

# Migrate existing files to LFS
git lfs migrate import --include="*.psd" --everything

# Fetch specific LFS files
git lfs fetch --include="assets/logo.png"

.gitattributes Configuration

gitattributes
# Images
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text

# Videos
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text

# Archives
*.zip filter=lfs diff=lfs merge=lfs -text
*.tar.gz filter=lfs diff=lfs merge=lfs -text

# ML Models
*.bin filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text

# Specific folders
assets/** filter=lfs diff=lfs merge=lfs -text

LFS Considerations

AspectDetails
StorageGitHub: 1GB free, 50GB với $5/mo data pack
BandwidthGitHub: 1GB/mo free, paid tiers available
CI/CDCần git lfs install + git lfs pull trong pipeline
SubmodulesLFS works với submodules, cần config đúng

LFS GOTCHA

Nếu forget để git lfs install trước khi commit large files:

bash
# Migrate những files đã commit nhầm
git lfs migrate import --include="*.psd" --include-ref=refs/heads/main
git push --force  # ⚠️ Rewrites history!

📊 Quick Reference

Submodules

CommandPurpose
git submodule add <url> <path>Add submodule
git submodule update --init --recursiveInitialize & update
git submodule update --remoteUpdate to latest
git submodule foreach '<cmd>'Run command in all
git submodule statusCheck versions

Worktrees

CommandPurpose
git worktree add <path> <branch>Create worktree
git worktree add -b <new> <path> <base>Create with new branch
git worktree listList all worktrees
git worktree remove <path>Remove worktree
git worktree pruneCleanup stale

LFS

CommandPurpose
git lfs installSetup LFS
git lfs track "<pattern>"Track file pattern
git lfs ls-filesList tracked files
git lfs pullDownload LFS files
git lfs migrate importMigrate existing files

💡 Key Takeaways

HPN'S INSIGHT

"Submodules cho shared code, Worktrees cho parallel work, LFS cho binaries. Đúng tool cho đúng problem."

  1. Submodules lưu pointers: Main repo chỉ track commit SHA, không phải code
  2. Always --recursive: git submodule update --init --recursive là bạn tốt
  3. Worktrees share objects: Không duplicate data, instant checkout
  4. LFS là mandatory cho binaries: Files >10MB nên dùng LFS
  5. CI/CD needs setup: Submodules và LFS cần explicit commands trong pipelines

CHOOSE WISELY

ProblemWrong ToolRight Tool
Shared libraryCopy-pasteSubmodule
Parallel branchesStashWorktree
Large binariesRegular commitLFS
Forked customizationSubmoduleSubtree or fork

🧠 Quiz

Câu 1: Khi nào nên dùng Git Submodules thay vì copy-paste shared library?

  • [x] A) Khi cần include một repository bên ngoài và muốn pin tại commit cụ thể, tự động update
  • [ ] B) Khi shared library chỉ có 1 file
  • [ ] C) Khi không có internet access
  • [ ] D) Khi repository quá lớn

💡 Giải thích: Submodules cho phép include repository khác như dependency, pin tại commit cụ thể, và update khi cần. Copy-paste tạo code duplication, mất version tracking, và khó update. Submodules là "right tool" cho shared libraries.

Câu 2: Git LFS (Large File Storage) giải quyết vấn đề gì?

  • [ ] A) Tăng tốc độ git clone
  • [ ] B) Encrypt large files trong repository
  • [x] C) Lưu large binary files (images, videos, models) trên server riêng, chỉ giữ pointer trong Git
  • [ ] D) Nén toàn bộ repository

💡 Giải thích: Git không hiệu quả với large binary files (mỗi thay đổi lưu full copy). LFS thay thế binary files bằng text pointers trong Git, lưu actual files trên LFS server. Repository nhẹ hơn, clone nhanh hơn.

Câu 3: Sự khác biệt chính giữa Submodule và Subtree là gì?

  • [ ] A) Submodule mới hơn Subtree
  • [ ] B) Subtree không hoạt động với remote repositories
  • [x] C) Submodule giữ reference đến repo ngoài, Subtree merge code trực tiếp vào repository
  • [ ] D) Submodule chỉ hỗ trợ một level nesting

💡 Giải thích: Submodule giữ pointer đến commit của repo bên ngoài (separate history). Subtree copy/merge code trực tiếp vào repo chính (single history). Subtree đơn giản hơn cho consumer, Submodule cho phép hai-chiều collaboration.