Skip to content

Git Daily Workflow: Bộ Sinh Tồn Cho Junior Developer

By Nhân Nguyễn on Apr 28, 2026

Git Daily Workflow: Bộ Sinh Tồn Cho Junior Developer

“Mất code, hoảng loạn khi rebase, không biết merge conflict là gì – mọi junior đều từng trải qua. Bài viết này sẽ giúp bạn xây dựng mô hình tư duy đúng về Git và làm chủ những tình huống hàng ngày.”

Trung bình mỗi tuần, một lập trình viên junior sẽ gặp ít nhất một trong những tình huống sau:

  • “Em commit nhầm vào main rồi push, làm sao undo?” (tim đập thình thịch)
  • “Em rebase mà thấy conflict tùm lum, không biết chọn theirs hay ours”
  • “Em pull về tự nhiên mất hết code local”
  • “Em lỡ xóa branch chưa merge, code biến mất rồi”
  • “Mở file ra thấy <<<<<<< HEAD, đầu óc trống rỗng”

Nếu bạn từng trải qua, bạn không hề đơn độc. Sự khác biệt giữa một junior loay hoay 2 tiếng và một senior xử lý trong 5 phút không nằm ở số lượng lệnh thuộc lòng, mà ở mô hình tư duy (mental model) về Git. Git không phải là “phần mềm save file lên cloud”, mà là một cây commit (DAG – Directed Acyclic Graph), nơi mỗi commit là một snapshot của toàn bộ dự án, và các branch chỉ là những con trỏ di động.

Bài viết này sẽ đồng hành cùng bạn xây dựng mô hình tư duy đó, đi kèm với các tình huống thực tế, lệnh hàng ngày, chiến lược branching và kỹ năng “cứu hỏa” khi có sự cố. Hãy coi đây như cuốn sổ tay sinh tồn Git cho junior developer.


1. Mô hình tư duy: Git là cây commit, không phải cloud save

1.1. Ba khu vực trong Git

Để hiểu Git, trước tiên bạn phải nắm vững ba “vùng đất” mà code của bạn di chuyển qua:

  ┌────────────────┐    git add      ┌────────────────┐    git commit   ┌─────────────────┐
  │  Working Dir   │ ──────────────> │  Staging Area  │ ──────────────> │  Local Repo     │
  │  (file đang    │                 │  (index)       │                 │  (lịch sử commit)│
  │   sửa)         │ <────────────── │                │                 │                 │
  └────────────────┘  git checkout   └────────────────┘                 └─────────────────┘

                                                                       git push  │

                                                                       ┌─────────────────┐
                                                                       │  Remote Repo    │
                                                                       │  (GitHub, GitLab)│
                                                                       └─────────────────┘
  • Working Directory: thư mục thực tế trên máy bạn, nơi bạn code.
  • Staging Area (Index): khu vực tạm chứa những thay đổi đã được chọn để đưa vào commit tiếp theo.
  • Local Repository: lịch sử commit được lưu trong thư mục .git, đây mới là “kho chứa” thực sự.
  • Remote Repository: phiên bản trên server (GitHub, GitLab,…) – chỉ thay đổi khi bạn push hoặc fetch.

Hiểu được sự di chuyển này giúp bạn tránh những lệnh nguy hiểm như git checkout . (xóa hết thay đổi ở working dir) hay nhầm lẫn giữa git resetgit restore.

1.2. Commit là snapshot, không phải diff

Nhiều bạn mới nghĩ commit lưu sự khác biệt (diff) so với trước đó. Thực tế, mỗi commit là một snapshot toàn bộ project tại thời điểm đó, kèm theo con trỏ tới commit cha (parent). Chính vì vậy lịch sử Git mới là một đồ thị không vòng (DAG):

A ─ B ─ C ─ D    (main)
     \
      E ─ F      (feature)

Mỗi node là một commit, mũi tên là parent pointer. Điều này lý giải vì sao bạn có thể checkout về bất kỳ commit nào và có toàn bộ code lúc đó.

1.3. Branch chỉ là con trỏ

Một hiểu lầm kinh điển: “branch là bản copy code”. Không! Branch thực chất chỉ là một label động trỏ tới một commit. Khi bạn tạo branch mới, Git chỉ tạo một con trỏ mới, không hề copy dữ liệu. Khi bạn commit trên branch đó, con trỏ tự động di chuyển tới commit mới.

                                  main (pointer)


A ─ B ─ C ─ D
         \
          E ─ F


          feature (pointer)
          HEAD ──> feature   ← HEAD trỏ vào branch hiện tại

HEAD là một con trỏ đặc biệt cho biết bạn đang “đứng” ở đâu. Thường thì HEAD trỏ vào một branch (gián tiếp tới commit), nhưng cũng có thể trỏ thẳng vào commit (detached HEAD). Khi ở trạng thái detached, nếu bạn commit, commit đó sẽ “mồ côi” vì không có branch nào trỏ vào. Giải pháp: ngay lập tức tạo branch mới git checkout -b new-branch để lưu lại.

2. Năm tình huống “thót tim” và cách xử lý

Tình huống 1: Pull về mất hết code local

$ git pull
error: Your local changes to the following files would be overwritten by merge:
        src/UserService.java
Please commit your changes or stash them before you merge.
Aborting.

Junior hoảng loạn gõ: git checkout .toàn bộ thay đổi chưa commit bốc hơi, khó khôi phục.

Cách đúng:

git stash                # cất tạm thay đổi
git pull                 # pull code mới về
git stash pop            # áp lại thay đổi, xử lý conflict nếu có

Tình huống 2: Commit nhầm vào branch main

Bạn đang cần làm feature/login nhưng lại commit ngay trên main.

git log --oneline -1
# abc1234 feat: add login endpoint

git branch feature/login           # tạo branch mới tại vị trí hiện tại (giữ commit)
git reset --hard HEAD~1            # lùi main về trước commit nhầm
git checkout feature/login         # chuyển sang branch đúng, code nguyên vẹn

Tình huống 3: Rebase và nỗi sợ conflict

Rebase là kỹ thuật “dời” các commit của branch hiện tại lên ngọn của branch khác, giúp lịch sử thẳng đẹp. Nhưng khi rebase, hash commit thay đổi, conflict có thể xuất hiện ở từng commit.

Trước rebase:                  Sau rebase:
main:    A — B — C             A — B — C
              \                         \
feature:       D — E — F                 D' — E' — F' (hash đổi)

Lệnh: git checkout feature && git rebase main

Quy tắc vàng: Không rebase branch đã push lên remote và có người khác cùng làm việc. Chỉ rebase branch local riêng trước khi push lần đầu.

Tình huống 4: Merge conflict – đừng hoảng

Khi gặp conflict, Git chèn các marker:

<<<<<<< HEAD
    return amount.multiply(BigDecimal.valueOf(0.05));
=======
    return amount.multiply(BigDecimal.valueOf(0.07));
>>>>>>> feature/new-rate
  • Đoạn từ <<<<<<< HEAD đến ======= là code của branch hiện tại (thường là main)
  • Đoạn từ ======= đến >>>>>>> feature/... là code của branch đang merge vào.

Bạn cần quyết định giữ bên nào, hoặc kết hợp cả hai, rồi xóa các marker. Sau đó:

git add <file>       # đánh dấu đã giải quyết
git commit           # hoàn tất merge

Nếu đang rebase thì dùng git rebase --continue.

Tình huống 5: Lỡ tay xóa branch chưa merge

git branch -D feature/login   # 200 dòng code "biến mất"

Bình tĩnh! Git vẫn giữ commit trong reflog (nhật ký tham chiếu) từ 30-90 ngày.

git reflog
# abc1234 HEAD@{1}: branch: Created from main
# def5678 HEAD@{2}: commit: feat: add login UI

git checkout -b feature/login abc1234   # khôi phục nguyên trạng

Reflog là cứu cánh tối thượng – nắm chắc nó, bạn sẽ không bao giờ mất code vĩnh viễn.


3. Bộ lệnh hàng ngày cho junior

3.1. Cấu hình ban đầu (chỉ một lần)

git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase false   # merge khi pull (an toàn)
git config --global core.editor "code --wait"

3.2. Workflow mỗi ngày

# Bắt đầu ngày
git checkout main
git pull

# Tạo branch tính năng
git checkout -b feature/JIRA-123-add-login

# Code... rồi kiểm tra
git status
git diff
git add -p                    # chọn từng phần thay đổi (khuyên dùng)
git commit -m "feat(auth): add JWT refresh token endpoint"

# Push lên remote và tạo PR
git push -u origin feature/JIRA-123-add-login
# (sau khi PR được merge)
git checkout main
git pull
git branch -d feature/JIRA-123-add-login
git push origin --delete feature/JIRA-123-add-login

Một số lệnh kiểm tra lịch sử hữu ích:

git log --oneline --graph --all   # nhìn toàn cảnh cây commit
git blame src/UserService.java    # ai viết từng dòng?
git reflog                        # lịch sử HEAD (cứu tinh)

3.3. Stash – cất tạm thay đổi

Khi bạn đang code dở mà cần chuyển gấp sang branch khác (ví dụ hotfix), stash cho phép cất toàn bộ thay đổi chưa commit vào một ngăn xếp.

git stash                   # cất đi
git stash pop               # lấy lại thay đổi gần nhất và xóa khỏi stack
git stash list              # xem danh sách stash
git stash apply stash@{1}   # áp dụng stash cụ thể mà không xóa

3.4. .gitignore – bảo vệ bí mật

Luôn đảm bảo file .gitignore có mặt từ commit đầu tiên, ít nhất với:

# Java / Spring Boot
target/
*.class
*.jar
.idea/
*.iml

# Secrets
.env
*.pem
*.key
application-secrets.yml

# OS
.DS_Store
Thumbs.db

Nếu lỡ commit file nhạy cảm, bạn phải xoay khóa ngay (vì nó đã lộ) và dùng git rm --cached để untrack, sau đó thêm vào .gitignore.


4. Chiến lược branching và commit message chuẩn

4.1. GitHub Flow – đơn giản, hiệu quả

Phần lớn team hiện đại dùng GitHub Flow: chỉ có một branch chính main (luôn sẵn sàng deploy) và các feature branch ngắn hạn.

main      ────●────────●────────●────  (deployable)
              │        │        │
feature/x ──●─┘        │        │
feature/y ────────────●─┘        │
feature/z ──────────────────────●─┘

Branch được đặt tên theo convention: feature/<ticket>-<mô tả>, bugfix/..., hotfix/....

4.2. Conventional Commits

Mỗi commit message nên tuân theo cấu trúc:

<type>(<scope>): <subject>

<body - tùy chọn>

<footer - tùy chọn>

Các type phổ biến: feat, fix, docs, style, refactor, test, chore, perf.

Ví dụ:

feat(auth): add JWT refresh token endpoint

- New endpoint POST /api/auth/refresh
- Refresh token rotation on use
- TTL: 7 days

Resolves: JIRA-123

Tránh những message vô nghĩa như “update”, “fix bug”, “WIP” (trừ phi bạn sẽ squash trước khi merge).


5. Merge, rebase và cherry-pick: chọn công cụ phù hợp

5.1. Merge vs Rebase

Tiêu chíMergeRebase
Cơ chếTạo merge commit gộp hai lịch sửViết lại lịch sử, đặt commit lên ngọn
Lịch sửGiữ nguyên, phân nhánh rõThẳng, sạch, dễ đọc
An toànAn toàn tuyệt đốiNguy hiểm nếu branch đã chia sẻ
Xử lý conflictTập trung một lần vào merge commitPhải giải quyết cho từng commit
Khi dùngBranch đã push, làm việc nhómBranch local riêng, trước khi push

Lệnh merge tiêu chuẩn: git checkout main && git merge feature

Rebase: git checkout feature && git rebase main

Quy tắc vàng rebase: Không bao giờ rebase branch đã push và có người khác pull. Nếu cần làm sạch lịch sử trước khi chia sẻ, hãy dùng rebase -i (interactive rebase) để squash, reword, drop commit.

git rebase -i HEAD~5   # chỉnh sửa 5 commit gần nhất

5.2. Cherry-pick – nhặt commit như hái cherry

Khi bạn chỉ muốn lấy một vài commit cụ thể từ branch khác mà không merge toàn bộ:

git checkout main
git cherry-pick abc1234

Thường dùng cho hotfix đang nằm trong feature branch chưa hoàn thiện, hoặc backport bản vá sang release branch cũ. Nhược điểm: cùng một thay đổi xuất hiện ở hai nơi với hash khác nhau, có thể gây conflict khi merge sau này.


6. Conflict resolution – kỹ năng sinh tồn

Các bước xử lý conflict khi merge:

  1. Xác định file conflict: git status
  2. Mở file, tìm marker <<<<<<<, =======, >>>>>>>. Phần trên là code của branch hiện tại, phần dưới là của branch đang merge vào.
  3. Chọn phương án giữ: có thể giữ HEAD, giữ feature, kết hợp hoặc đưa ra giá trị mới. Hỏi đồng đội nếu không chắc.
  4. Lưu file và đánh dấu resolved:
    git add <file>
    git commit   # (hoặc git rebase --continue)

Nếu bối rối quá, bạn có thể hủy bỏ:

git merge --abort
git rebase --abort

Các IDE như VS Code cung cấp giao diện trực quan với các nút “Accept Current”, “Accept Incoming”, “Accept Both”.


7. Undo – “cứu hỏa” cho mọi tình huống

7.1. Cấp độ nhẹ: sửa file chưa add, muốn về nguyên trạng

git restore src/UserService.java   # modern Git (>=2.23)
# hoặc
git checkout -- src/UserService.java

7.2. Unstage file đã add nhưng chưa commit

git restore --staged src/UserService.java
# hoặc
git reset HEAD src/UserService.java

7.3. Sửa commit message (chưa push)

git commit --amend -m "feat: correct message"

7.4. Lỡ commit trên main (chưa push)

git branch feature/add-login    # giữ commit ở branch mới
git reset --hard HEAD~1         # main lùi 1 commit
git checkout feature/add-login

7.5. Đã push commit sai lên branch riêng

Dùng force push an toàn:

git reset --hard HEAD~1
git push --force-with-lease     # kiểm tra remote chưa bị thay đổi từ lần fetch cuối

Tuyệt đối không dùng --force thuần.

7.6. Đã push lên branch chung

Không được force push. Hãy tạo commit đảo ngược:

git revert abc1234
git push

Commit revert sẽ ghi nhận lịch sử hoàn tác, không phá hỏng công việc của người khác.

7.7. Reflog – “bảo hiểm” của Git

Ngay cả khi bạn reset --hard, xóa branch, code vẫn tồn tại trong reflog tới 90 ngày:

git reflog
# tìm hash muốn khôi phục
git checkout -b recovered-branch <hash>

Hãy luôn nhớ “git reflog” mỗi khi tim bạn đập nhanh vì sợ mất code.


8. Git hooks và quy trình ngân hàng

Trong môi trường enterprise, đặc biệt là ngân hàng, Git thường được thắt chặt bằng các hook (script tự động chạy khi có sự kiện Git).

Ví dụ, pre-commit hook để chạy test:

#!/bin/sh
./mvnw test
if [ $? -ne 0 ]; then
    echo "Tests failed. Commit aborted."
    exit 1
fi

Ngoài ra, để đảm bảo audit trail, commit có thể được ký GPG (git commit -S). Khi đó GitHub/GitLab hiển thị badge “Verified”, đáp ứng yêu cầu compliance.

Quy trình PR chuẩn trong team lớn:

  1. Tạo branch, code, commit đúng chuẩn.
  2. Push, mở PR.
  3. CI chạy: build, unit test, security scan.
  4. Reviewer (thường cần 2 approvals) kiểm tra.
  5. Squash & merge vào main (giữ lịch sử gọn), tự động deploy dev.
  6. Cleanup branch.

Tuân thủ những quy ước này giúp bạn hòa nhập nhanh vào môi trường doanh nghiệp và giảm thiểu lỗi không đáng có.


9. Thực hành để thành thạo

Kiến thức chỉ thực sự thuộc về bạn khi đôi tay đã quen. Hãy thực hiện các bài tập sau trên repo cá nhân:

  • Exercise 1: Tạo branch, commit theo Conventional Commits, push, mở PR, cleanup.
  • Exercise 2: Tạo conflict giả, thực hành resolve bằng tay.
  • Exercise 3: Mô phỏng 5 kịch bản undo từ nhẹ đến nặng, sử dụng reflog để khôi phục.
  • Exercise 4: Interactive rebase: squash, reword, drop commit.

Ngoài ra, có thể thực hành trực quan qua các công cụ:

  • Learn Git Branching – game hóa mô phỏng thao tác Git.
  • Oh My Git! – game nhập vai học Git.
  • lazygit, GitLens, GitKraken – công cụ tăng năng suất.

10. Checklist thành thạo cho junior

Sau khi học và thực hành, hãy tự kiểm tra:

  • Vẽ được cây commit cho một kịch bản merge/rebase, giải thích HEAD/branch/commit.
  • Phân biệt Working Dir, Staging, Repo và các lệnh di chuyển.
  • Hoàn thành daily workflow mà không cần nhìn cheatsheet.
  • Xử lý được conflict mà không panic.
  • Thực hiện 5 undo scenario không cần Google.
  • Dùng reflog khôi phục branch đã xóa.
  • Squash, reword, drop commit với interactive rebase.
  • Đề xuất được chiến lược branch phù hợp cho team, viết commit message chuẩn.
  • Cấu hình .gitignore đúng cho dự án Java/Spring Boot.

Lời kết

Git không phải là rào cản, mà là công cụ mạnh mẽ nhất để bảo vệ và quản lý code của bạn. Một khi nắm vững mô hình “cây commit” và các thao tác cơ bản, bạn sẽ thấy rằng những tình huống từng gây hoảng loạn thực ra chỉ cần vài dòng lệnh đơn giản.

Hãy in cheatsheet cuối bài này ra và dán ngay trước màn hình. Khi gặp sự cố, đừng vội gõ bừa – git status, git log --oneline --graph --all, và git reflog là ba câu thần chú giúp bạn bình tĩnh nhìn rõ tình hình.

Điều cuối cùng: đừng bao giờ dùng git push --force thuần, git checkout ., hay rm -rf .git khi chưa hiểu rõ. Chúng có thể gây mất mát không thể cứu vãn. Hãy tìm hiểu --force-with-lease, restore, và reflog trước.

Chúc bạn làm chủ Git và tự tin trên hành trình phát triển sự nghiệp lập trình!


Phụ lục: Cheatsheet 1 trang

SETUP
  git config --global user.name "Name"
  git config --global user.email "email@x.com"

DAILY
  git status                      # Tình trạng hiện tại
  git diff                        # Thay đổi unstaged
  git diff --staged               # Thay đổi staged
  git add <file>                  # Stage file
  git add -p                      # Stage interactive
  git commit -m "message"
  git push

BRANCHES
  git branch
  git checkout -b <name>
  git switch <name>
  git branch -d <name>
  git push origin --delete <name>

SYNC
  git fetch
  git pull
  git pull --rebase

HISTORY
  git log --oneline --graph --all
  git blame <file>
  git reflog

UNDO
  git restore <file>
  git restore --staged <file>
  git commit --amend
  git reset HEAD~1
  git reset --hard HEAD~1
  git revert <hash>

STASH
  git stash
  git stash pop
  git stash list

REBASE
  git rebase main
  git rebase -i HEAD~5
  git rebase --abort

CONFLICT
  git status
  # sửa file
  git add <file>
  git commit / git rebase --continue

Hãy kết nối

Nếu bạn quan tâm tới việc hợp tác, có câu hỏi về bài viết, hay chỉ đơn giản muốn chuyện trò về backend — cứ ping mình nhé.