
บทความนี้นำเสนอแนวคิดและการใช้งานระบบควบคุมเวอร์ชัน (Version Control System – VCS) โดยเน้น Git ตั้งแต่พื้นฐานการติดตั้ง คำสั่งทั่วไป การแตกสาขา (Branching) การทำงานกับ Remote Repository ไปจนถึง Workflow ระดับองค์กรและเทคนิคขั้นสูง พร้อมตัวอย่างปฏิบัติที่นำไปทดลองใช้ได้จริง
ระบบควบคุมเวอร์ชัน (Version Control System – VCS) คือเครื่องมือที่ใช้บันทึกการเปลี่ยนแปลงของไฟล์ (โดยเฉพาะซอร์สโค้ด) ตลอดเวลา ทำให้สามารถย้อนดูประวัติ เปรียบเทียบความต่าง กลับไปยังเวอร์ชันก่อนหน้า และทำงานร่วมกับผู้อื่นได้อย่างเป็นระบบ
VCS ถูกพัฒนามาในสามรูปแบบหลัก ซึ่งสะท้อนวิวัฒนาการของวิธีจัดเก็บประวัติการเปลี่ยนแปลง
Local VCS เป็นรูปแบบดั้งเดิมที่สุด เก็บประวัติไว้ในเครื่องของผู้ใช้เพียงเครื่องเดียว โดยใช้ฐานข้อมูลในเครื่อง (Local Database) เพื่อจดจำการเปลี่ยนแปลงของไฟล์ในแต่ละ Revision
CVCS เก็บประวัติทั้งหมดไว้บน Server กลาง เพียงตัวเดียว และผู้พัฒนาแต่ละคนต้อง Checkout ไฟล์มาที่เครื่องตนเอง
DVCS ทุก Client มีสำเนา (Clone) ของ Repository เต็มรูปแบบ รวมประวัติทั้งหมด ทำให้สามารถทำงานแบบ Offline ได้ และไม่มี Single Point of Failure
flowchart LR
subgraph LOCAL["Local VCS (เครื่องเดียว)"]
DEV1[Developer
ผู้พัฒนา] --- DB1[(Local DB
ฐานข้อมูลในเครื่อง)]
end
subgraph CENTRAL["Centralized VCS (SVN, CVS)"]
DEV2[Developer A] --> SRV[Central Server
เซิร์ฟเวอร์กลาง]
DEV3[Developer B] --> SRV
DEV4[Developer C] --> SRV
SRV --- DB2[(Repository
คลังเก็บ)]
end
subgraph DIST["Distributed VCS (Git, Mercurial)"]
DEV5[Developer A] --- DB3[(Full Clone)]
DEV6[Developer B] --- DB4[(Full Clone)]
DEV7[Developer C] --- DB5[(Full Clone)]
DEV5 -.sync.- DEV6
DEV6 -.sync.- DEV7
DEV5 -.sync.- DEV7
end
style LOCAL fill:#3c3836,stroke:#d79921,color:#ebdbb2
style CENTRAL fill:#3c3836,stroke:#458588,color:#ebdbb2
style DIST fill:#3c3836,stroke:#98971a,color:#ebdbb2
| คุณสมบัติ (Feature) | Local VCS | Centralized (SVN) | Distributed (Git) |
|---|---|---|---|
| ที่เก็บประวัติ (History Storage) | เครื่องเดียว | Server กลาง | ทุกเครื่อง (Clone เต็ม) |
| ทำงาน Offline | ✓ | ✗ | ✓ |
| Single Point of Failure | ✓ (เครื่องตัวเอง) | ✓ (Server) | ✗ |
| ความเร็วในการ Branch | ช้า/ไม่มี | ช้า (สร้าง folder) | เร็วมาก (pointer) |
| Merge ซับซ้อน | ยาก | ปานกลาง | รองรับดี |
| ความยากในการเรียนรู้ | ง่าย | ปานกลาง | ปานกลาง-ยาก |
| ตัวอย่างเครื่องมือ | RCS, SCCS | CVS, SVN, Perforce | Git, Mercurial, Fossil |
ประโยชน์หลักของการใช้ VCS โดยเฉพาะ Git สามารถสรุปได้ดังนี้
สูตรประเมินคุณค่าของ VCS สามารถมองในเชิง Mean Time To Recovery (MTTR) เมื่อเกิดปัญหา:
โดยที่ Time(Detect) คือเวลาที่ใช้ตรวจพบปัญหา, Time(Revert) คือเวลาในการย้อน commit ผ่าน git revert หรือ git reset (โดยทั่วไป Git ใช้เวลาน้อยกว่า 1 นาที), Time(Deploy) คือเวลาที่ใช้ deploy เวอร์ชันที่กลับไปแล้ว, และ N(Incidents) คือจำนวนเหตุการณ์ทั้งหมด ยิ่ง MTTR ต่ำ ยิ่งแสดงว่าระบบฟื้นตัวได้เร็ว
Git ถือกำเนิดในปี ค.ศ. 2005 จากความจำเป็นของโครงการ Linux Kernel หลังจากที่ผู้พัฒนา Linux เคยใช้ระบบเชิงพาณิชย์ชื่อ BitKeeper ฟรีอยู่ระยะหนึ่ง แต่ผู้พัฒนา BitKeeper ตัดสินใจถอนสิทธิ์การใช้งานฟรี Linus Torvalds จึงตัดสินใจสร้าง VCS ใหม่ที่มีคุณสมบัติตรงตามที่ Linux Kernel ต้องการ
flowchart TB
subgraph ERA1["ยุค 1970s-1980s Local VCS"]
SCCS[SCCS
1972]
RCS[RCS
1982]
end
subgraph ERA2["ยุค 1990s-2000s Centralized VCS"]
CVS[CVS
1990]
SVN[Subversion
2000]
PERFORCE[Perforce
1995]
end
subgraph ERA3["ยุค 2005+ Distributed VCS"]
BK[BitKeeper
2000]
GIT[Git
2005]
HG[Mercurial
2005]
FOSSIL[Fossil
2007]
end
subgraph ERA4["ยุค Cloud Hosting 2008+"]
GH[GitHub
2008]
BB[Bitbucket
2008]
GL[GitLab
2011]
FORGE[Forgejo/Gitea
2016+]
end
SCCS --> RCS
RCS --> CVS
CVS --> SVN
SVN --> BK
BK --> GIT
BK --> HG
GIT --> GH
GIT --> BB
GIT --> GL
GIT --> FORGE
style ERA1 fill:#3c3836,stroke:#cc241d,color:#ebdbb2
style ERA2 fill:#3c3836,stroke:#d79921,color:#ebdbb2
style ERA3 fill:#3c3836,stroke:#98971a,color:#ebdbb2
style ERA4 fill:#3c3836,stroke:#458588,color:#ebdbb2
ชื่อ "Git" Linus Torvalds เคยให้สัมภาษณ์ขำ ๆ ว่ามาจากภาษา British Slang ที่หมายถึง "คนน่ารำคาญ" และเล่นมุกว่า "ผมตั้งชื่อโปรแกรมตามตัวเองทุกครั้ง คราวก่อนคือ Linux คราวนี้คือ Git"
การติดตั้ง Git แตกต่างกันตามระบบปฏิบัติการ
# Debian/Ubuntu - ใช้ APT
sudo apt update
sudo apt install -y git
# Fedora/RHEL/Rocky - ใช้ DNF
sudo dnf install -y git
# Arch Linux/Manjaro - ใช้ Pacman
sudo pacman -S git
# openSUSE - ใช้ Zypper
sudo zypper install git
# Alpine Linux - ใช้ APK
sudo apk add git
# ตรวจสอบเวอร์ชันหลังติดตั้ง
git --version
# ตัวอย่าง output: git version 2.43.0
# ผ่าน Homebrew (แนะนำ)
brew install git
# ผ่าน Xcode Command Line Tools
xcode-select --install
# ตรวจสอบ
git --version
# ผ่าน winget (ตัวจัดการแพ็กเกจของ Windows 10/11)
winget install --id Git.Git -e
# ผ่าน Chocolatey
choco install git -y
# ผ่าน Scoop
scoop install git
# หรือดาวน์โหลด Git for Windows จาก https://git-scm.com/
# ซึ่งมาพร้อม Git Bash, MinTTY, Git GUI
git config ระดับ --system, --global, --localGit มี config 3 ระดับ ที่ override ซึ่งกันและกันตามลำดับ (ระดับ local มีลำดับสูงสุด)
| ระดับ (Level) | Flag | ตำแหน่งไฟล์ | ขอบเขต |
|---|---|---|---|
| System | --system |
/etc/gitconfig |
ผู้ใช้ทุกคนในเครื่อง |
| Global (User) | --global |
~/.gitconfig หรือ ~/.config/git/config |
ผู้ใช้ปัจจุบัน ทุก repo |
| Local (Repo) | --local |
.git/config |
repo ปัจจุบันเท่านั้น |
# ดูค่า config ทั้งหมด พร้อมแหล่งที่มา
git config --list --show-origin
# ตั้งค่าระดับ Global (ใช้บ่อยที่สุด)
git config --global user.name "Moo Lecturer"
git config --global user.email "moo@example.ac.th"
# ตั้งค่าเฉพาะ repo (เช่น ใช้ email งานในโปรเจกต์งาน)
cd ~/projects/work-project
git config --local user.email "moo.work@company.com"
# ลบค่าที่ตั้งไว้
git config --global --unset user.email
# แก้ไข config ผ่าน editor
git config --global --edit
user.name, user.email, core.editor, init.defaultBranch, pull.rebaseค่า config สำคัญที่ควรตั้งหลังติดตั้ง Git
# ตัวตนของผู้ใช้ (จะติดไปกับทุก commit)
git config --global user.name "Moo Lecturer"
git config --global user.email "moo@rmutsv.ac.th"
# Editor ที่ใช้ในการเขียน commit message
git config --global core.editor "vim" # Vim
git config --global core.editor "nano" # Nano
git config --global core.editor "code --wait" # VS Code
git config --global core.editor "zed --wait" # Zed
# ชื่อ Branch เริ่มต้นเมื่อใช้ git init (ปัจจุบันนิยม main แทน master)
git config --global init.defaultBranch main
# พฤติกรรมของ git pull - เลือก rebase แทน merge เพื่อ history สะอาด
git config --global pull.rebase true
# จัดการ line ending ระหว่างระบบ (สำคัญสำหรับทีมข้ามแพลตฟอร์ม)
git config --global core.autocrlf input # บน Linux/macOS
git config --global core.autocrlf true # บน Windows
# เปิดสีในเอาต์พุต
git config --global color.ui auto
# Alias ช่วยพิมพ์สั้นลง
git config --global alias.st "status -sb"
git config --global alias.co "checkout"
git config --global alias.br "branch"
git config --global alias.lg "log --oneline --graph --all --decorate"
# Push เฉพาะ branch ปัจจุบัน
git config --global push.default current
# ใช้ rerere (reuse recorded resolution) ลดการแก้ conflict ซ้ำ
git config --global rerere.enabled true
Credential Helper ช่วยจดจำ username/password หรือ token เพื่อไม่ต้องพิมพ์ทุกครั้งที่ push/pull จาก Remote
# Cache ใน RAM (default 15 นาที, ปรับเวลาเป็นวินาที)
git config --global credential.helper "cache --timeout=3600"
# เก็บใน plain text (ไม่แนะนำสำหรับเครื่อง shared)
git config --global credential.helper store
# บน macOS - ใช้ Keychain (เข้ารหัสในระบบ)
git config --global credential.helper osxkeychain
# บน Windows - ใช้ Git Credential Manager
git config --global credential.helper manager-core
# บน Linux - ใช้ libsecret (GNOME Keyring/KWallet)
sudo apt install libsecret-1-0 libsecret-1-dev # Debian/Ubuntu
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret
# ใช้ SSH key แทน HTTPS (วิธีที่แนะนำที่สุด)
# สร้าง key
ssh-keygen -t ed25519 -C "moo@rmutsv.ac.th"
# เพิ่ม public key ใน GitHub/GitLab
cat ~/.ssh/id_ed25519.pub
# สร้าง repository ใหม่ในโฟลเดอร์ปัจจุบัน
mkdir my-project && cd my-project
git init
# ผลลัพธ์: สร้างโฟลเดอร์ .git/ ที่เก็บข้อมูลทั้งหมด
# สร้าง repository พร้อมระบุ branch เริ่มต้น
git init --initial-branch=main
# สร้าง bare repository (สำหรับใช้เป็น remote บน server)
git init --bare /srv/git/my-project.git
# Clone จาก remote (HTTPS)
git clone https://github.com/torvalds/linux.git
# Clone ผ่าน SSH (แนะนำเมื่อมี SSH key)
git clone git@github.com:torvalds/linux.git
# Clone ลง folder ชื่ออื่น
git clone https://github.com/user/repo.git my-folder
# Clone เฉพาะ branch
git clone -b develop --single-branch https://github.com/user/repo.git
# Shallow clone (เอาเฉพาะ commit ล่าสุด ลดเวลา/ขนาด)
git clone --depth 1 https://github.com/user/repo.git
# ดูสถานะของ Working Directory และ Staging Area
git status
git status -s # short format (สั้น เห็นง่าย)
git status -sb # short + branch info
# เพิ่มไฟล์เข้า Staging Area
git add file1.txt # ไฟล์เดียว
git add file1.txt file2.py # หลายไฟล์
git add . # ทุกไฟล์ในโฟลเดอร์ปัจจุบัน
git add -A # ทุกไฟล์ที่เปลี่ยน (รวม deleted)
git add *.py # ใช้ wildcard
git add -p # interactive: เลือก hunk ที่จะ stage
git add -u # เฉพาะไฟล์ที่ tracked อยู่แล้ว
# Commit
git commit -m "feat: add user login feature"
# Commit ทุกไฟล์ที่ tracked โดยไม่ต้อง add (ข้าม staging)
git commit -am "fix: typo in README"
# แก้ commit ล่าสุด (เพิ่มไฟล์ที่ลืม หรือแก้ message)
git add forgotten-file.txt
git commit --amend --no-edit # ไม่แก้ message
git commit --amend -m "new message" # แก้ message
# Commit แบบเขียน message ยาว (เปิด editor)
git commit
มาตรฐานการเขียน commit message ที่นิยมในโปรเจกต์โอเพนซอร์ส มีรูปแบบ:
<type>(<scope>): <subject>
<body>
<footer>
| Type | ความหมาย |
|---|---|
feat |
ฟีเจอร์ใหม่ |
fix |
แก้ไขบั๊ก |
docs |
เปลี่ยนเอกสาร |
style |
จัดรูปแบบ ไม่กระทบ logic |
refactor |
ปรับโครงสร้างโค้ดโดยไม่เปลี่ยนพฤติกรรม |
test |
เพิ่ม/แก้ test |
chore |
งานบำรุงรักษา (build, dep update) |
perf |
ปรับปรุงประสิทธิภาพ |
ci |
เปลี่ยน CI/CD config |
ตัวอย่าง:
feat(auth): add OAuth2 login with Google
- เพิ่ม endpoint /auth/google
- ใช้ passport-google-oauth20
- เพิ่ม environment variable GOOGLE_CLIENT_ID
Closes #123
Git มี 3 พื้นที่หลัก (Three Trees) ที่ไฟล์เดินทางผ่าน
flowchart LR
WD["Working Directory
โฟลเดอร์ทำงาน
(ไฟล์จริงในเครื่อง)"]
SA["Staging Area / Index
พื้นที่เตรียม commit
(.git/index)"]
REPO["Repository
คลังเก็บ commit
(.git/objects)"]
REMOTE["Remote Repository
(GitHub/GitLab/...)"]
WD -->|"git add"| SA
SA -->|"git commit"| REPO
REPO -->|"git push"| REMOTE
REMOTE -->|"git fetch / pull"| REPO
REPO -->|"git checkout / restore"| WD
SA -->|"git restore --staged"| WD
style WD fill:#3c3836,stroke:#d79921,color:#ebdbb2
style SA fill:#3c3836,stroke:#d65d0e,color:#ebdbb2
style REPO fill:#3c3836,stroke:#98971a,color:#ebdbb2
style REMOTE fill:#3c3836,stroke:#458588,color:#ebdbb2
.git/--oneline, --graph, --all, --author)# ดูประวัติทั้งหมด (ละเอียด)
git log
# แบบบรรทัดเดียว สั้นกระชับ
git log --oneline
# แสดงกราฟของ branch
git log --oneline --graph --all --decorate
# กรองตาม author
git log --author="Moo"
# กรองตามช่วงเวลา
git log --since="2 weeks ago" --until="yesterday"
git log --after="2024-01-01" --before="2024-12-31"
# ค้นหาใน commit message
git log --grep="bug"
# ค้นหาใน source code (pickaxe)
git log -S"function_name"
# ดูว่า commit ใดแก้ไขไฟล์ใด (พร้อม diff)
git log -p file.txt
# สถิติ insertion/deletion
git log --stat
git log --shortstat
# Format แบบกำหนดเอง (ใช้ใน script)
git log --pretty=format:"%h - %an, %ar : %s"
# %h = short hash, %an = author name, %ar = relative time, %s = subject
# alias ที่นิยม
git config --global alias.lg "log --color --graph \
--pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' \
--abbrev-commit"
git lg
# ความต่างระหว่าง Working Directory กับ Staging Area
git diff
# ความต่างระหว่าง Staging Area กับ commit ล่าสุด (HEAD)
git diff --staged
git diff --cached # alias เดียวกัน
# ความต่างระหว่าง Working Directory กับ HEAD (รวม unstaged + staged)
git diff HEAD
# เปรียบเทียบสอง commit
git diff abc123 def456
# เปรียบเทียบ branch
git diff main..feature
git diff main...feature # 3 dots = หา common ancestor
# เปรียบเทียบเฉพาะไฟล์
git diff main feature -- src/app.py
# แสดงเฉพาะชื่อไฟล์ที่เปลี่ยน
git diff --name-only
git diff --name-status # พร้อมสถานะ A/M/D
# นับ insertion/deletion
git diff --shortstat
# ใช้ word-level diff (เห็นการเปลี่ยนคำ ไม่ใช่บรรทัด)
git diff --word-diff
ไฟล์ .gitignore บอก Git ว่า "ไฟล์ใดไม่ต้อง track" – มักเก็บไฟล์ build, secret, IDE config, dependency
# ตัวอย่าง .gitignore สำหรับโปรเจกต์ Python + Node + IDE
cat > .gitignore <<'EOF'
# === Python ===
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
env/
*.egg-info/
dist/
build/
.pytest_cache/
.mypy_cache/
.ruff_cache/
# === Node.js ===
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# === Environment & Secrets ===
.env
.env.local
.env.*.local
*.pem
*.key
secrets.yml
# === IDE / Editor ===
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
Thumbs.db
# === Build artifacts ===
*.log
*.tmp
coverage/
.nyc_output/
# === ยกเว้นบางไฟล์จากการ ignore (negation) ===
!important.log
EOF
# Global gitignore (ใช้กับทุก repo ของผู้ใช้)
git config --global core.excludesfile ~/.gitignore_global
cat > ~/.gitignore_global <<'EOF'
# OS-specific files
.DS_Store
Thumbs.db
desktop.ini
# IDE-specific (ส่วนตัว ไม่ควรใส่ใน repo)
.idea/
.vscode/settings.json
EOF
# ถ้าไฟล์ถูก track ไปแล้ว ต้องลบออกก่อน .gitignore จะมีผล
git rm --cached secret.env
git commit -m "chore: stop tracking secret.env"
# ตรวจว่าไฟล์ใดถูก ignore เพราะ rule ไหน
git check-ignore -v node_modules/foo.js
ดู template .gitignore สำเร็จรูปสำหรับภาษาต่าง ๆ ได้ที่ https://github.com/github/gitignore
ใน Git Branch คือ pointer (ตัวชี้) ไปยัง commit หนึ่ง ไม่ใช่สำเนาของไฟล์ ทำให้แตก branch เร็วและประหยัดพื้นที่มาก
gitGraph
commit id: "C1: init"
commit id: "C2: add README"
branch feature/login
checkout feature/login
commit id: "C3: form HTML"
commit id: "C4: validate"
checkout main
commit id: "C5: fix typo"
checkout feature/login
commit id: "C6: connect API"
checkout main
merge feature/login id: "C7: merge login"
commit id: "C8: release v1.0"
# ดู branch ทั้งหมด
git branch # local branch
git branch -r # remote branch
git branch -a # ทั้ง local + remote
git branch -v # พร้อม commit hash ล่าสุด
git branch --merged # branch ที่ merge เข้า branch ปัจจุบันแล้ว
git branch --no-merged # branch ที่ยังไม่ merge
# สร้าง branch ใหม่ (ยังไม่ switch ไป)
git branch feature/user-auth
# สร้างและ switch ไปทีเดียว (วิธีเก่า)
git checkout -b feature/user-auth
# สร้างและ switch (วิธีใหม่ แนะนำ - ตั้งแต่ Git 2.23)
git switch -c feature/user-auth
# Switch กลับไป branch อื่น
git switch main
git checkout main # วิธีเก่า
# เปลี่ยนชื่อ branch
git branch -m old-name new-name
git branch -m new-name # เปลี่ยนชื่อ branch ปัจจุบัน
# ลบ branch (ปลอดภัย - ห้ามลบถ้ายังไม่ merge)
git branch -d feature/user-auth
# ลบ branch แบบบังคับ (ใช้ระวัง)
git branch -D feature/user-auth
# สร้าง branch จาก commit ที่ระบุ
git switch -c hotfix/v1.2 abc1234
# Detached HEAD - ชี้ commit โดยตรง (เพื่อดูประวัติ ไม่ควร commit)
git checkout abc1234
git switch -d abc1234 # วิธีใหม่
มี 2 รูปแบบหลัก ของการ merge
เกิดเมื่อ target branch ไม่มี commit ใหม่ที่ source branch ไม่มี – Git แค่เลื่อน pointer
# scenario: main ไม่มี commit ใหม่หลังจาก feature แตกออก
git switch main
git merge feature/login
# Git จะ "fast-forward" main pointer ไปที่ commit ของ feature
เกิดเมื่อ ทั้งสอง branch มี commit ใหม่ตั้งแต่จุดแยก – Git สร้าง merge commit ใหม่ที่มี 2 parents
git switch main
git merge feature/login
# Git สร้าง merge commit ใหม่
# จะเปิด editor ให้ใส่ message
# บังคับ merge commit แม้จะ fast-forward ได้
git merge --no-ff feature/login
# บังคับ fast-forward เท่านั้น (ถ้าไม่ได้จะ error)
git merge --ff-only feature/login
# Squash merge - รวมทุก commit ของ feature เป็น 1 commit
git merge --squash feature/login
git commit -m "feat: complete login feature"
gitGraph
commit id: "A"
commit id: "B"
branch feature
checkout feature
commit id: "C"
commit id: "D"
checkout main
commit id: "E"
merge feature id: "F (merge)"
commit id: "G"
Merge Conflict เกิดเมื่อสอง branch แก้ไขบรรทัดเดียวกันในไฟล์เดียวกันด้วยวิธีต่างกัน Git จะหยุดและให้คนตัดสินใจ
# จำลองสถานการณ์
git switch main
echo "main version" > config.txt
git add . && git commit -m "main: edit config"
git switch -c feature
git switch main && echo "back" > /dev/null # ทำต่อบน main
# main ไม่ได้แก้ไขใหม่ ลองเล่นแบบจริง:
git switch -c feature
echo "feature version" > config.txt
git add . && git commit -m "feature: edit config"
git switch main
echo "main updated" > config.txt
git add . && git commit -m "main: update config"
git merge feature
# CONFLICT (content): Merge conflict in config.txt
# Automatic merge failed; fix conflicts and then commit the result.
ไฟล์ที่ conflict จะมี conflict marker:
<<<<<<< HEAD
main updated
=======
feature version
>>>>>>> feature
ขั้นตอนการแก้:
# 1. ดูสถานะและไฟล์ที่ conflict
git status
# 2. แก้ไขไฟล์ - เลือกเวอร์ชัน หรือผสมทั้งสอง
vim config.txt
# ลบเครื่องหมาย <<<<<<<, =======, >>>>>>> ออก เก็บเฉพาะที่ต้องการ
# 3. add ไฟล์ที่แก้แล้ว
git add config.txt
# 4. commit เพื่อจบ merge
git commit # editor จะเปิดให้แก้ message
# หรือถ้าอยากยกเลิก merge ทั้งหมด
git merge --abort
# ใช้ tool ช่วยแก้ conflict
git mergetool
git config --global merge.tool vimdiff
git config --global merge.tool meld # หรือ kdiff3, vscode
Rebase ย้าย commit ของ branch ปัจจุบันไป "วาง" บนยอดของ branch อื่น ทำให้ history เป็นเส้นตรง สวย แต่ rewrite history (เปลี่ยน hash)
# Merge - คง history แท้จริง สร้าง merge commit
git switch feature
git merge main
# Rebase - ทำให้ history เป็นเส้นตรง
git switch feature
git rebase main
# ถ้า conflict: แก้ -> git add -> git rebase --continue
# หรือยกเลิก: git rebase --abort
# ข้าม commit ที่ conflict: git rebase --skip
# Interactive Rebase (จะอธิบายในหัวข้อ 8.8)
git rebase -i HEAD~3
กฎทอง: อย่า rebase commit ที่ push ไป shared branch แล้ว เพราะจะทำให้คนอื่นปวดหัว
| ลักษณะ | Merge | Rebase |
|---|---|---|
| รักษา history | ✓ ตรงตามจริง | ✗ เขียนใหม่ |
| Merge commit | สร้าง | ไม่มี |
| ความสะอาดของกราฟ | ซับซ้อน | เป็นเส้นตรง |
| ความปลอดภัย | สูง (ไม่เปลี่ยน hash) | ต่ำ (เปลี่ยน hash) |
| เหมาะกับ | shared branch | private/feature branch |
นำเฉพาะ commit ที่เลือก จาก branch หนึ่งไปอีก branch หนึ่งโดยไม่ต้อง merge ทั้ง branch
# ดู commit ของ branch ที่ต้องการ
git log feature --oneline
# Cherry-pick commit เดียว
git cherry-pick abc1234
# Cherry-pick หลาย commit
git cherry-pick abc1234 def5678
# Cherry-pick ช่วง commit
git cherry-pick abc1234..def5678 # ไม่รวม abc1234
git cherry-pick abc1234^..def5678 # รวม abc1234
# Cherry-pick โดยไม่ commit (ให้ทำต่อใน staging)
git cherry-pick -n abc1234
# จัดการ conflict
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --skip
Use case ทั่วไป: นำ hotfix จาก main ไปใส่ branch ของรุ่น release เก่าที่ยังต้อง support
Remote Repository คือ repository ที่อยู่บน server อื่น (เช่น GitHub) ที่เราเชื่อมต่อกับ local repo
# ดู remote ทั้งหมด
git remote
git remote -v # พร้อม URL
# เพิ่ม remote ชื่อ "origin"
git remote add origin git@github.com:moo/my-project.git
# เพิ่ม remote ที่สอง (กรณี fork)
git remote add upstream git@github.com:original-author/my-project.git
# เปลี่ยน URL ของ remote
git remote set-url origin git@gitlab.com:moo/my-project.git
# เปลี่ยนชื่อ remote
git remote rename origin github
# ลบ remote
git remote remove old-remote
# ดูข้อมูลละเอียด
git remote show origin
# Fetch - ดาวน์โหลด commit จาก remote มาที่ local แต่ไม่ merge
git fetch origin
git fetch --all # ทุก remote
git fetch --prune # ลบ remote-tracking ที่ไม่มีบน remote แล้ว
# Pull = Fetch + Merge (โดย default) หรือ Fetch + Rebase
git pull origin main
git pull --rebase origin main
git pull --ff-only origin main # ยอมเฉพาะ fast-forward (ปลอดภัย)
# Push - ส่ง commit ของ local ไปที่ remote
git push origin main
git push -u origin feature/new # -u = ตั้ง upstream ครั้งแรก
# Push ทุก branch
git push --all origin
# Push tags
git push --tags origin
# Force push (อันตราย!) - ใช้เมื่อ rebase แล้วและรู้ว่าทำอะไร
git push --force origin feature # อันตรายมาก
git push --force-with-lease origin feature # ปลอดภัยกว่า
# ลบ branch บน remote
git push origin --delete feature/old
Tracking Branch คือ local branch ที่ผูกกับ remote branch เฉพาะตัว ทำให้ git pull/git push ไม่ต้องระบุชื่ออีก
# ตั้ง upstream ครั้งแรก
git push -u origin feature/login
# หลังจากนี้ใช้ git push / git pull ได้เลย
# เปลี่ยน upstream ของ branch ปัจจุบัน
git branch --set-upstream-to=origin/main
# ดูสถานะ ahead/behind เทียบกับ upstream
git status -sb
# ## main...origin/main [ahead 2, behind 1]
# ดู tracking ทั้งหมด
git branch -vv
ในวัฒนธรรม OSS (Open Source Software) เมื่อ contribute โปรเจกต์ของผู้อื่น:
origin# Workflow Fork & Pull Request
git clone git@github.com:moo/awesome-lib.git # fork ของเรา
cd awesome-lib
git remote add upstream git@github.com:original/awesome-lib.git
# อัปเดตจาก upstream
git fetch upstream
git switch main
git merge upstream/main
git push origin main
# สร้าง feature branch แล้ว push ขึ้น fork
git switch -c feature/cool-feature
# ... แก้โค้ด commit ...
git push -u origin feature/cool-feature
# เปิด Pull Request จาก fork ไปยัง upstream ผ่าน GitHub UI
Refspec บอก Git ว่า "อ้างอิง local จะตรงกับอ้างอิง remote อย่างไร" รูปแบบ: <src>:<dst> หรือ +<src>:<dst> (force)
# Push branch local ชื่อ feature ไปเป็น branch ชื่อ wip บน remote
git push origin feature:wip
# Pull เฉพาะ branch
git fetch origin main:main
# ดู refspec ใน config
git config --get-all remote.origin.fetch
# +refs/heads/*:refs/remotes/origin/*
# เพิ่ม refspec - fetch tag ทั้งหมด
git config --add remote.origin.fetch "+refs/tags/*:refs/tags/*"
ทีมพัฒนาแต่ละแบบเลือก Workflow ต่างกัน ขึ้นกับขนาดทีม ความถี่ในการ release และความซับซ้อนของผลิตภัณฑ์
ทีมเล็ก ทุกคนทำงานบน main โดยตรง ใช้ git pull --rebase ก่อน push
# ทุกคนใน main
git switch main
git pull --rebase origin main
# แก้โค้ด
git add . && git commit -m "feat: ..."
git pull --rebase origin main
git push origin main
ข้อเสีย: เสี่ยงโค้ดพังบน main, ไม่มี code review
ทุก feature ทำใน branch แยก แล้ว merge กลับ main ผ่าน Pull Request
git switch main && git pull
git switch -c feature/payment-integration
# ... ทำงาน + commit ...
git push -u origin feature/payment-integration
# เปิด PR ใน GitHub/GitLab
# หลัง review ผ่าน → merge
โครงสร้างซับซ้อน เหมาะกับโปรเจกต์ที่มี release schedule ชัดเจน
main – production ready (tag ทุก release)develop – integration ของ featurefeature/* – แยกจาก develop merge กลับ developrelease/* – เตรียม release จาก develop merge ไปทั้ง main + develophotfix/* – แก้บั๊ก production ด่วน จาก main merge กลับ main + develop
gitGraph
commit id: "init"
branch develop
checkout develop
commit id: "dev-1"
branch feature/A
commit id: "feat-A1"
commit id: "feat-A2"
checkout develop
merge feature/A
branch release/1.0
commit id: "rel-prep"
checkout main
merge release/1.0 tag: "v1.0"
checkout develop
merge release/1.0
branch hotfix/1.0.1
commit id: "hotfix"
checkout main
merge hotfix/1.0.1 tag: "v1.0.1"
checkout develop
merge hotfix/1.0.1
เรียบง่าย เหมาะกับ Continuous Deployment
mainเพิ่ม environment branch (เช่น pre-production, production) ระหว่าง main กับ deployment
main → pre-production → production
ทุก deploy คือ merge จาก branch หนึ่งไปอีก branch หนึ่ง ทำให้ track ได้ว่ามี code ไหนอยู่ environment ไหน
ทุกคน commit เข้า main (trunk) ผ่าน short-lived branch (ภายใน 1-2 วัน) ใช้ Feature Flag ซ่อนของที่ยังไม่พร้อม
ข้อดี: integration ถี่ ลด merge hell, เหมาะกับ DevOps/CI ระดับสูง
ข้อเสีย: ต้องการ test coverage และวินัยสูงมาก
| Workflow | ขนาดทีม | ความถี่ Release | ความซับซ้อน |
|---|---|---|---|
| Centralized | 1-3 | ต่ำ | ต่ำมาก |
| Feature Branch | 3-20 | ปานกลาง | ต่ำ |
| Git Flow | 20+ | ตามรอบ | สูง |
| GitHub Flow | 5-50 | สูง (CD) | ต่ำ-ปานกลาง |
| GitLab Flow | 10-50 | สูง | ปานกลาง |
| Trunk-based | 5-1000+ | สูงมาก | ต่ำ (แต่ต้องวินัยสูง) |
Pull Request (GitHub, Gitea) หรือ Merge Request (GitLab) คือ feature ของ git host ที่ให้ผู้พัฒนาเสนอให้ merge branch ของตนเข้าสู่อีก branch หนึ่ง พร้อมระบบ Review, Discussion, CI Integration
git push -u origin feature/xmain) และ compare branch (feature/x)## What
อธิบายสิ่งที่เปลี่ยนแปลงในระดับสูง
## Why
ที่มาของการเปลี่ยนแปลง อ้างอิง Issue (Closes #123)
## How
รายละเอียดทางเทคนิคที่ผู้รีวิวควรรู้
## Test
- [ ] Unit test ผ่าน
- [ ] ทดสอบบน staging แล้ว
- [ ] อัปเดต documentation
## Screenshot
แนบรูปก่อน/หลัง ถ้าเป็น UI
แนวปฏิบัติของการรีวิวที่ดี
nit:, suggestion:, question:, issue:, praise:)ดู 8.5.4 + เพิ่มเติม
# Sync fork กับ upstream เป็นประจำ (ผ่าน rebase)
git fetch upstream
git switch main
git rebase upstream/main
git push origin main
# หรือ merge
git merge upstream/main
สำหรับโครงการแบบ Linux Kernel ที่ใช้ email-based workflow
# สร้าง patch จาก commit
git format-patch -1 HEAD # patch เดียวจาก HEAD
git format-patch main..feature # patch ทุก commit ใน feature ที่ไม่มีใน main
# ผลลัพธ์: 0001-feat-add-x.patch, 0002-fix-y.patch, ...
# ส่ง patch ทาง email
git send-email --to=mailing-list@kernel.org *.patch
# Apply patch ที่ได้รับ
git am < patch.eml
git am 0001-feat-add-x.patch
git am --abort # ยกเลิก
git am --continue # หลังแก้ conflict
ไฟล์มาตรฐานในรากของ repo:
CONTRIBUTING.md – คู่มือสำหรับผู้ที่อยาก contribute (สไตล์โค้ด, ขั้นตอน PR, การรัน test).github/CODEOWNERS หรือ docs/CODEOWNERS – กำหนด reviewer อัตโนมัติตามไฟล์/โฟลเดอร์# .github/CODEOWNERS
# Default owners
* @moo @team-lead
# Backend code
/backend/ @backend-team
# Specific file
/Dockerfile @devops-team
# Frontend
*.tsx @frontend-team
*.css @design-team
--soft, --mixed, --hardgit reset ย้าย HEAD ไปยัง commit ที่ระบุ มี 3 mode ตามผลกระทบต่อ Staging Area และ Working Directory
| Mode | HEAD | Staging Area | Working Directory |
|---|---|---|---|
--soft |
ย้าย | คงเดิม | คงเดิม |
--mixed (default) |
ย้าย | reset ตาม HEAD ใหม่ | คงเดิม |
--hard |
ย้าย | reset | reset (อันตราย!) |
# ยกเลิก commit ล่าสุด เก็บการเปลี่ยนแปลงไว้ใน staging
git reset --soft HEAD~1
# ยกเลิก commit ล่าสุด เก็บการเปลี่ยนแปลงไว้ใน working dir แต่ unstage
git reset HEAD~1 # หรือ git reset --mixed HEAD~1
# ยกเลิก commit + ทิ้งการเปลี่ยนแปลง (ใช้ระวังสุดขีด)
git reset --hard HEAD~1
# Reset ไป commit ที่ระบุ
git reset --hard abc1234
# Unstage ไฟล์ (ใหม่ ใช้ git restore)
git reset HEAD file.txt
git restore --staged file.txt # คำสั่งใหม่ ตั้งแต่ Git 2.23
# สร้าง commit ใหม่ที่ลบล้าง commit เก่า (history ปลอดภัย)
git revert abc1234
# Revert หลาย commit
git revert abc1234..def5678
# Revert merge commit (ต้องระบุ parent)
git revert -m 1 <merge-commit>
# Revert โดยไม่ commit (เก็บใน staging)
git revert -n abc1234
reflog คือบันทึกของทุกครั้งที่ HEAD หรือ branch reference เปลี่ยน เก็บเฉพาะใน local แต่ช่วยกู้ commit ที่ "หาย"
# ดู reflog ทั้งหมด
git reflog
# ตัวอย่าง output:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: feat: add feature X
# ...
# กู้ commit ที่ลบทิ้งไปด้วย reset --hard
git reset --hard HEAD@{1}
# หรือ
git reset --hard def5678
# reflog ของ branch
git reflog show feature/login
# reflog เก็บนานเท่าใด (default 90 วัน, expired 30 วัน)
git config gc.reflogExpire
git config gc.reflogExpireUnreachable
git rebase -i): pick, squash, fixup, reword, edit, dropเครื่องมือทรงพลังที่สุดในการจัดระเบียบ history
# rebase 5 commit ล่าสุด แบบ interactive
git rebase -i HEAD~5
Editor จะเปิดให้แก้:
pick abc1234 feat: add login
pick def5678 fix: typo
pick ghi9012 feat: add logout
pick jkl3456 wip: testing
pick mno7890 fix: bug in login
# Commands:
# p, pick = ใช้ commit นี้
# r, reword = ใช้แต่แก้ message
# e, edit = หยุดเพื่อแก้ commit
# s, squash = รวมเข้า commit ก่อนหน้า (รวม message)
# f, fixup = รวมเข้า commit ก่อนหน้า (ทิ้ง message)
# x, exec = รัน shell command
# d, drop = ลบ commit นี้
แก้เป็น:
pick abc1234 feat: add login
fixup def5678 fix: typo
pick ghi9012 feat: add logout
drop jkl3456 wip: testing
fixup mno7890 fix: bug in login
ผลลัพธ์: รวมจาก 5 commit เหลือ 2 commit สะอาด
# Auto-squash workflow
git commit --fixup=abc1234 # สร้าง commit "fixup! ..."
git rebase -i --autosquash HEAD~10 # จัดเรียงและรวมอัตโนมัติ
Stash = พักงานที่ยังไม่พร้อม commit ไว้ที่ "ชั้นวางชั่วคราว"
# พักงานปัจจุบัน
git stash
git stash push -m "WIP: refactoring auth" # มี message
git stash -u # รวม untracked file
git stash -a # รวม ignored file ด้วย
git stash push -p # interactive: เลือก hunk
# ดู stash ทั้งหมด
git stash list
# stash@{0}: WIP on main: abc1234 last commit
# stash@{1}: On feature: ...
# ดูเนื้อหา stash
git stash show
git stash show -p stash@{1} # full diff
# กลับมาทำงานต่อ
git stash pop # apply + ลบ stash ล่าสุด
git stash apply # apply แต่เก็บ stash ไว้
git stash apply stash@{2}
# ลบ stash
git stash drop stash@{1}
git stash clear # ลบทั้งหมด
# สร้าง branch จาก stash
git stash branch new-feature stash@{0}
Tag คือ pointer ไปยัง commit เฉพาะ ที่ไม่เคลื่อนที่ ใช้ทำเครื่องหมาย version release
| คุณสมบัติ | Lightweight Tag | Annotated Tag |
|---|---|---|
| เก็บผู้สร้าง/วันที่ | ✗ | ✓ |
| เก็บ message | ✗ | ✓ |
| GPG sign ได้ | ✗ | ✓ |
| ใช้สำหรับ | bookmark ส่วนตัว | release ทางการ |
# Lightweight Tag - แค่ pointer
git tag v1.0-light
# Annotated Tag - object เต็มรูปแบบ (แนะนำสำหรับ release)
git tag -a v1.0 -m "Release version 1.0 - initial public release"
# Tag commit เก่า (ไม่ใช่ HEAD)
git tag -a v0.9 abc1234 -m "Beta release"
# GPG signed tag
git tag -s v1.0 -m "Signed release"
# ดู tag ทั้งหมด
git tag
git tag -l "v1.*" # filter pattern
git tag -n # พร้อม annotation
# ดูรายละเอียด tag
git show v1.0
# ลบ tag ใน local
git tag -d v1.0
# ลบ tag บน remote
git push origin --delete v1.0
# Push tag ไป remote
git push origin v1.0 # tag เดียว
git push origin --tags # ทุก tag (เฉพาะที่ไม่ลึก)
git push origin --follow-tags # ทุก annotated tag ที่เกี่ยวข้องกับ commit ที่ push
# Checkout ที่ tag (เข้า detached HEAD)
git checkout v1.0
git switch --detach v1.0
มาตรฐาน MAJOR.MINOR.PATCH ตามเอกสาร https://semver.org
โดยที่ MAJOR เพิ่มเมื่อมี breaking change (API ไม่เข้ากันย้อนหลัง), MINOR เพิ่มเมื่อมี feature ใหม่ที่ backward compatible, PATCH เพิ่มเมื่อแก้บั๊กเล็กน้อย, PRERELEASE เช่น -alpha.1, -rc.2 และ BUILD เช่น +20240125
| ตัวอย่าง | ความหมาย |
|---|---|
v1.0.0 |
Stable release แรก |
v1.0.1 |
Patch แก้บั๊ก |
v1.1.0 |
Minor – feature ใหม่ |
v2.0.0 |
Major – breaking change |
v2.0.0-rc.1 |
Release candidate |
v2.0.0-beta.3+a1b2c3d |
Beta พร้อม build metadata |
Release บน Git host = Tag + เอกสาร changelog + binary artifact
# วิธี CLI ผ่าน GitHub CLI (gh)
gh release create v1.0.0 \
--title "v1.0.0 - Initial Release" \
--notes "Initial public release with login feature" \
./dist/myapp-linux-amd64.tar.gz \
./dist/myapp-windows-amd64.zip
# Generate release notes อัตโนมัติจาก commit/PR
gh release create v1.1.0 --generate-notes
# วิธี CLI ผ่าน GitLab (glab)
glab release create v1.0.0 \
--name "Version 1.0.0" \
--notes "Initial release" \
--assets-link '{"name":"binary","url":"https://..."}'
Conventional Changelog แนะนำให้ใช้ tool เช่น git-cliff, standard-version, release-please สร้าง CHANGELOG.md อัตโนมัติจาก commit
# git-cliff (เขียนด้วย Rust)
cargo install git-cliff
git cliff --output CHANGELOG.md
git cliff --tag v1.1.0 --output CHANGELOG.md
| Hosting | จุดเด่น | ฟรี Private Repo | CI/CD ในตัว |
|---|---|---|---|
| GitHub | community ใหญ่ที่สุด, GitHub Actions, Copilot | ✓ ไม่จำกัด | ✓ Actions |
| GitLab | DevOps ครบวงจรที่สุด, Self-host ได้ | ✓ ไม่จำกัด | ✓ GitLab CI |
| Bitbucket | บูรณาการกับ Jira/Confluence | ✓ (≤5 user) | ✓ Pipelines |
สำหรับองค์กรที่ต้องการ host เอง
# ตัวอย่าง docker-compose.yml สำหรับ Gitea
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
restart: always
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"
Radicle เป็น P2P git hosting ที่ไม่ต้องพึ่ง server กลาง ใช้ public-key cryptography ในการระบุตัวตนและ replicate ระหว่าง peer
# ติดตั้ง Lazygit
sudo pacman -S lazygit # Arch
brew install lazygit # macOS
sudo dnf copr enable atim/lazygit && sudo dnf install lazygit # Fedora
# ใช้งาน
cd repo && lazygit
Hook = script ที่ Git รันอัตโนมัติเมื่อเกิด event ใน repo (เก็บใน .git/hooks/)
# Hook ที่ใช้บ่อย
ls .git/hooks/
# applypatch-msg.sample, commit-msg.sample, post-update.sample,
# pre-applypatch.sample, pre-commit.sample, pre-push.sample,
# pre-rebase.sample, prepare-commit-msg.sample, update.sample
ตัวอย่าง pre-commit hook ที่บล็อกการ commit ถ้า lint ไม่ผ่าน:
#!/usr/bin/env bash
# .git/hooks/pre-commit
# ตรวจ Python lint ด้วย ruff ก่อน commit
set -e
# หาไฟล์ Python ที่จะ commit
files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.py$' || true)
if [ -z "$files" ]; then
exit 0
fi
echo "Running ruff on staged Python files..."
echo "$files" | xargs ruff check
if [ $? -ne 0 ]; then
echo "❌ Lint failed. Commit aborted."
echo "💡 Fix errors or run: git commit --no-verify (ไม่แนะนำ)"
exit 1
fi
echo "✅ Lint passed"
# จากนั้น
chmod +x .git/hooks/pre-commit
pre-commit (https://pre-commit.com) คือ framework ของ Python ที่บริหาร hook ผ่าน YAML config – แชร์ config กับทีมง่ายกว่า hook ใน .git/hooks/
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=500']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
# ติดตั้งและใช้งาน
pip install pre-commit
pre-commit install # ติดตั้ง hook
pre-commit run --all-files # รันทันทีทุกไฟล์
pre-commit autoupdate # อัพเดต rev ของ repo
ทั้งสองใช้ฝัง repo อีกตัวภายใน repo หลัก แต่กลไกต่างกัน
# เพิ่ม submodule
git submodule add https://github.com/lib/awesome.git external/awesome
git commit -m "chore: add awesome lib as submodule"
# Clone repo ที่มี submodule
git clone --recurse-submodules https://github.com/me/myapp.git
# หรือถ้า clone แล้วเพิ่งรู้
git submodule update --init --recursive
# อัปเดต submodule ทั้งหมด
git submodule update --remote --merge
# ลบ submodule
git submodule deinit -f external/awesome
git rm -f external/awesome
rm -rf .git/modules/external/awesome
ข้อเสีย: complex, ต้องจำคำสั่ง --recurse-submodules ตลอด
# เพิ่ม subtree (เก็บ history ภายในเดียวกัน)
git subtree add --prefix=external/awesome \
https://github.com/lib/awesome.git main --squash
# Pull update
git subtree pull --prefix=external/awesome \
https://github.com/lib/awesome.git main --squash
# Push back
git subtree push --prefix=external/awesome \
https://github.com/lib/awesome.git main
ข้อดี: ผู้ใช้ที่ clone repo ไม่ต้องรู้ว่ามี subtree, history รวมในไฟล์เดียวกัน
| คุณสมบัติ | Submodule | Subtree |
|---|---|---|
| ขนาด repo | เล็ก (pointer) | ใหญ่ (รวมไฟล์) |
| Clone | ต้อง --recurse | ไม่ต้องคิด |
| Update | ใช้ submodule update | ใช้ subtree pull |
| Push กลับ upstream | ตรงไปตรงมา | ต้องระบุ prefix |
| Learning curve | สูง | ปานกลาง |
สำหรับไฟล์ binary ขนาดใหญ่ (รูป, video, model AI) ที่ Git ปกติจัดการไม่เก่ง
# ติดตั้ง git-lfs
sudo apt install git-lfs # Debian/Ubuntu
brew install git-lfs # macOS
sudo pacman -S git-lfs # Arch
# Initialize ใน repo
git lfs install
# Track ไฟล์ที่ใหญ่
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "models/*.pt"
# .gitattributes ถูกอัปเดตอัตโนมัติ
git add .gitattributes
git add big-file.psd
git commit -m "feat: add design source"
# ดูไฟล์ LFS
git lfs ls-files
git lfs status
ทำงานหลาย branch พร้อมกันโดยไม่ต้อง stash/switch ไปมา – แต่ละ branch มี directory แยกแต่ใช้ .git/ ร่วมกัน
# สร้าง worktree ของ branch hotfix ที่โฟลเดอร์ ../hotfix
git worktree add ../hotfix hotfix/v1.2
# สร้างพร้อม branch ใหม่
git worktree add -b feature/x ../feature-x main
# ดู worktree ทั้งหมด
git worktree list
# ลบ worktree
git worktree remove ../hotfix
# ทำความสะอาด worktree ที่ไม่อยู่แล้ว
git worktree prune
Use case: กำลังทำ feature ใหญ่บน branch หนึ่ง ขณะ production มีบั๊กด่วน → สร้าง worktree ของ hotfix branch ในอีกโฟลเดอร์ เปิด terminal/editor 2 หน้าต่าง
หา commit ที่ทำให้บั๊กเกิดด้วย binary search (เร็วระดับ log₂)
โดยที่ n = จำนวน commit ระหว่าง good และ bad ตัวอย่าง: 1024 commit ใช้แค่ 10 ครั้งทดสอบ
# เริ่ม bisect
git bisect start
# บอกว่าตอนนี้ (HEAD) บั๊ก
git bisect bad
# บอก commit ที่รู้ว่ายังดี
git bisect good v1.0
# Git จะ checkout ที่กลางทาง ให้เราทดสอบ
# ทดสอบแล้วถ้าดี
git bisect good
# ถ้ายังบั๊ก
git bisect bad
# ถ้า build ไม่ผ่าน ข้าม commit นี้
git bisect skip
# จบแล้ว Git จะบอก commit ที่ต้นเหตุ
# 7c3a9b8 is the first bad commit
# ออกจาก bisect
git bisect reset
# Automate ด้วย script (return 0=good, 1=bad, 125=skip)
git bisect start HEAD v1.0
git bisect run ./test.sh
ลงนาม commit ด้วย key เพื่อยืนยันตัวตน ป้องกันการปลอมแปลง
# === GPG Signing ===
# สร้าง GPG key
gpg --full-generate-key
gpg --list-secret-keys --keyid-format=long
# ตั้งค่า Git
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true
git config --global tag.gpgsign true
# Sign commit
git commit -S -m "signed commit"
git tag -s v1.0 -m "signed tag"
# === SSH Signing (Git 2.34+) - ง่ายกว่า ===
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
# ตรวจสอบ signature
git log --show-signature
git verify-commit HEAD
อัพโหลด public key ไป GitHub/GitLab เพื่อให้ขึ้น "Verified" badge
หาว่าใครแก้บรรทัดไหน เมื่อไร
# blame ทั้งไฟล์
git blame src/app.py
# blame เฉพาะบรรทัด 10-20
git blame -L 10,20 src/app.py
# blame หา function
git blame -L :function_name src/app.py
# ละเว้น whitespace change
git blame -w src/app.py
# ติดตามผ่าน rename
git blame -C -C src/app.py
# เห็น original commit แม้ผ่าน reformat
git config --global blame.ignoreRevsFile .git-blame-ignore-revs
echo "abc1234567890" > .git-blame-ignore-revs # commit reformat
# === git clean - ลบไฟล์ untracked ===
# Dry run (แค่แสดง ไม่ลบ)
git clean -n
# ลบไฟล์ untracked
git clean -f
# ลบทั้งไฟล์ + folder untracked
git clean -fd
# ลบรวมที่อยู่ใน .gitignore
git clean -fdx
# Interactive
git clean -i
# === git gc - garbage collection ===
# รวม loose objects เป็น pack file ลดพื้นที่
git gc
# Aggressive - ใช้เวลานาน แต่บีบอัดดีกว่า
git gc --aggressive --prune=now
# ดูขนาด repo
git count-objects -vH
# Verify integrity
git fsck --full
#!/usr/bin/env bash
# ตัวอย่าง workflow จริง: feature → PR → merge → release
# 1. เริ่ม feature ใหม่
git switch main
git pull --rebase origin main
git switch -c feature/user-profile
# 2. ทำงาน + commit เล็ก ๆ บ่อย ๆ
echo "user code" > user.py
git add user.py
git commit -m "feat(user): add user model"
echo "more code" >> user.py
git add user.py
git commit -m "feat(user): add validation"
# 3. ก่อน push ให้จัดระเบียบ commit (squash)
git rebase -i HEAD~2
# เลือก fixup รวม commit ที่สอง
# 4. Sync กับ main แล้ว push
git fetch origin
git rebase origin/main
git push -u origin feature/user-profile
# 5. เปิด PR (ผ่าน gh CLI)
gh pr create \
--title "feat: add user profile" \
--body "Closes #42" \
--reviewer @teammate
# 6. หลัง review + CI ผ่าน → merge แบบ squash
gh pr merge --squash --delete-branch
# 7. กลับมา main + tag release
git switch main
git pull --rebase origin main
git tag -a v1.2.0 -m "Release: User Profile feature"
git push origin v1.2.0
# 8. สร้าง release บน GitHub
gh release create v1.2.0 --generate-notes
บทนี้ได้ครอบคลุม Git อย่างเป็นระบบ ตั้งแต่ปรัชญาของ Version Control System ไปจนถึงการใช้งานขั้นสูงในระดับองค์กร โดยสามารถสรุปสาระสำคัญได้ดังนี้
git add -preset, revert, reflog, interactive rebase, stash) คือเครื่องมือกู้สถานการณ์ที่ผู้พัฒนาควรฝึกใช้ก่อนเกิดเหตุ
mindmap
root((Git))
Local Operations
Working Directory
Staging Area
Repository
Stash
History
Commit Graph DAG
Branches as Pointers
Tags & Releases
Reflog Safety Net
Collaboration
Remote/Origin/Upstream
Pull Request
Code Review
Fork Workflow
Workflow
GitHub Flow
Git Flow
Trunk-based
Advanced
Submodule/Subtree
Worktree
Bisect
Signed Commit
LFS
<type>(<scope>): <subject>pull.rebase=true) เพื่อ history สะอาด.gitignore, .env, secret manager และ scan ด้วย tool เช่น gitleaks, trufflehog--force-with-lease แทน --force| หมวดหมู่ (Category) | คำสั่งที่ใช้บ่อย |
|---|---|
| Setup | git init, git clone, git config |
| Snapshot | git status, git add, git commit, git restore |
| History | git log, git diff, git blame, git reflog |
| Branch | git branch, git switch, git checkout |
| Merge | git merge, git rebase, git cherry-pick |
| Remote | git remote, git fetch, git pull, git push |
| Undo | git reset, git revert, git stash |
| Tag | git tag, git describe |
| Advanced | git worktree, git bisect, git submodule, git lfs |
ผู้เรียนที่เข้าใจเนื้อหาในบทนี้แล้ว ควรเรียนรู้เพิ่มเติมในด้านต่อไปนี้
git cat-file, git hash-object, git update-refคำสุดท้าย: Git เป็นเครื่องมือที่เรียนรู้ได้ตลอดชีวิต — เริ่มจาก 10 คำสั่งพื้นฐาน (init, clone, status, add, commit, push, pull, branch, switch, log) แล้วค่อย ๆ ขยับไปสู่ workflow ที่ซับซ้อนขึ้นเมื่อทีมเติบโต ผู้พัฒนาที่ใช้ Git อย่างเชี่ยวชาญจะมี ความมั่นใจในการทดลอง เพราะรู้ว่าทุกการเปลี่ยนแปลงสามารถย้อนกลับได้เสมอ