کتاب جامع Git و GitHub
از مبانی تا موضوعات پیشرفته — یک مرجع کامل برای توسعهدهندگان
تاریخچه Git #
داستان Git با هستهی لینوکس شروع شد. لینوس توروالدز در سال ۲۰۰۲ تصمیم گرفت پروژهی لینوکس را به BitKeeper بسپارد — یک سیستم کنترل نسخهی تجاری که مالک آن، Larry McVoy، اجازه استفاده رایگان را به پروژههای متنباز داده بود. این تصمیم در ابتدا کارآمد بود، اما در سال ۲۰۰۵ پس از اینکه برخی توسعهدهندگان لینوکس مهندسی معکوسِ پروتکل BitKeeper را انجام دادند، McVoy لیسانس رایگان را لغو کرد. این اتفاق، جامعهی لینوکس را در موقعیتی سخت قرار داد.
لینوس توروالدس در آوریل ۲۰۰۵ شروع به نوشتن سیستمی جدید کرد. هدف او سه چیز بود: سرعت بالا، طراحی توزیعشده، و پشتیبانی قوی از شاخهها (branches). پس از تنها ده روز، نسخه اولیه آماده شد. در ژوئیه ۲۰۰۵، مدیریت پروژه به Junio Hamano واگذار شد که تا امروز maintainer اصلی Git است. نام «Git» در زبان بریتانیایی عامیانه به معنی «آدم خنگ» است — انتخابی طنزآمیز از سوی توروالدز.
چرا Git برنده شد؟
Git برنده شد زیرا سه مشکل اصلی سیستمهای قبلی را حل کرد: سرعت کار با پروژههای بزرگ (هسته لینوکس دهها هزار فایل دارد)، توزیعشده بودن (هر کلون یک نسخه کامل از تاریخچه است) و شاخههای ارزان (ساختن شاخه در Git تقریباً هیچ هزینهای ندارد). این ویژگیها به همراه ظهور GitHub در سال ۲۰۰۸، Git را به استاندارد جهانی کنترل نسخه تبدیل کرد.
Git طوری طراحی شده که «آرزوی واقعیترین دادهها در تاریخچه است» — یعنی هیچ commit ای واقعاً از بین نمیرود تا زمانی که reflog آن را نگه دارد (پیشفرض: ۹۰ روز). این موضوع، امنیت بالایی به توسعهدهندگان میدهد.
مقایسه با VCSهای دیگر #
سیستمهای کنترل نسخه به دو دسته اصلی تقسیم میشوند: متمرکز (Centralized) و توزیعشده (Distributed). SVN، CVS و Perforce نمونههای متمرکز هستند که در آنها یک سرور مرکزی همه چیز را نگه میدارد و کلاینتها فقط نسخههای اخیر را دارند. در مقابل، Git، Mercurial و Bazaar توزیعشدهاند: هر کلون، تاریخچهی کامل پروژه را در خود دارد.
(Full History)"] C1["Client A
(Working Copy)"] C2["Client B
(Working Copy)"] C3["Client C
(Working Copy)"] S1 <--> C1 S1 <--> C2 S1 <--> C3 end
(GitHub/GitLab)"] L1["Local Repo A
(Full History)"] L2["Local Repo B
(Full History)"] L3["Local Repo C
(Full History)"] L1 <-->|push/pull| R1 L2 <-->|push/pull| R1 L3 <-->|push/pull| R1 L1 <-.->|direct peer| L2 end
جدول مقایسه
| ویژگی | Git | SVN | Mercurial | Perforce |
|---|---|---|---|---|
| مدل | Distributed | Centralized | Distributed | Centralized |
| آفلاین کار میکند | بله | خیر | بله | خیر |
| سرعت branch | آنی | کند | آنی | کند |
| ذخیرهسازی | Snapshot | Delta | Snapshot | Delta |
| اندازه repo | کوچک | متوسط | کوچک | متوسط |
| یادگیری | سخت | آسان | متوسط | سخت |
| بازار فعلی | ~۹۰٪ | کم | کم | نیچ |
اگرچه Git توزیعشده است، اما در عمل معمولاً یک «مبدأ حقیقت» (origin remote) روی GitHub/GitLab داریم. توزیعشده بودن به معنای استقلال در کار آفلاین و نداشتن نقطه شکست واحد (single point of failure) است.
مبانی Git #
برای درک Git، باید سه منطقهی اصلی آن را بشناسید: Working Directory (دایرکتوری کاری)، Staging Area (Index) و Repository. هر فایل در هر لحظه یکی از این چهار وضعیت را دارد: Untracked، Modified، Staged یا Committed. درک این چرخه، پایهی همه کارهای بعدی است.
for commit note right of Committed : Saved in
local history
سه منطقه اصلی
(Files on disk)"] -->|git add| SA["Staging Area
(Index)"] SA -->|git commit| LR["Local Repository
(.git folder)"] LR -->|git push| RR["Remote Repository
(GitHub)"] RR -->|git fetch/pull| LR LR -->|git checkout| WD style WD fill:#f38ba8,stroke:#1e1e2e,color:#1e1e2e style SA fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style LR fill:#a6e3a1,stroke:#1e1e2e,color:#1e1e2e style RR fill:#89b4fa,stroke:#1e1e2e,color:#1e1e2e
وضعیتهای فایل
- Untracked — فایل جدیدی که Git هنوز آن را track نمیکند.
- Modified — فایل tracked که تغییر کرده اما هنوز stage نشده.
- Staged — فایل تغییر کرده و برای commit بعدی آماده است.
- Committed — تغییرات به صورت دائمی در تاریخچه ذخیره شده.
برخلاف SVN که تفاوتها (delta) را ذخیره میکند، Git snapshot میگیرد. یعنی هر commit، عکس کامل از وضعیت همه فایلها در آن لحظه است. برای فایلهای تغییر نکرده، فقط یک لینک به snapshot قبلی ذخیره میشود. این مدل، عملیات branch و merge را بسیار سریع میکند.
نصب و تنظیم اولیه #
نصب Git روی هر سه سیستمعامل اصلی ساده است. پس از نصب، مهمترین قدم، تنظیم نام و ایمیل است؛ زیرا این مقادیر در هر commit ذخیره میشوند و تاریخچهی شما را در پروژههای متنباز شناسایی میکنند. در این فصل، نصب روی هر سه پلتفرم، تنظیمات سراسری، و راهاندازی SSH برای اتصال امن به GitHub را پوشش میدهیم.
نصب روی Windows
سادهترین روش، دانلود «Git for Windows» از سایت رسمی است که شامل Git Bash، یک شبیهساز ترمینال لینوکس، و Git GUI میشود. هنگام نصب، گزینه «Use Git from the Windows Command Prompt» را انتخاب کنید تا دستورات Git در PowerShell و CMD هم در دسترس باشند.
# Download via winget (Windows Package Manager)
winget install --id Git.Git -e --source winget
# Verify installation
git --version
# Output: git version 2.45.0.windows.1
# Optional: install GitHub CLI
winget install --id GitHub.cli
gh auth login
نصب روی macOS
روی مک، سادهترین روش استفاده از Homebrew است. اگر Homebrew نصب نیست، ابتدا آن را از brew.sh نصب کنید. جایگزین، نصب Xcode Command Line Tools است که Git را به همراه میآورد.
# Option A: via Homebrew (recommended)
brew install git
# Option B: via Xcode Command Line Tools
xcode-select --install
# Verify
git --version
# Output: git version 2.45.0
نصب روی Linux
# Debian / Ubuntu
sudo apt update && sudo apt install git -y
# Fedora / RHEL
sudo dnf install git -y
# Arch Linux
sudo pacman -S git
# Verify
git --version
تنظیمات اولیه (مهم!)
پس از نصب، باید نام و ایمیل خود را تنظیم کنید. این اطلاعات در metadata هر commit ذخیره میشوند و در GitHub برای شناسایی مشارکتهای شما استفاده میشوند. مطمئن شوید ایمیل، همان ایمیل اکانت GitHub شما باشد.
# Set global identity (used for every commit)
git config --global user.name "Abbas Talebifard"
git config --global user.email "abbas@example.com"
# Set default branch name (modern convention)
git config --global init.defaultBranch main
# Set preferred editor
git config --global core.editor "code --wait" # VS Code
# git config --global core.editor "vim" # Vim
# git config --global core.editor "nano" # Nano
# Enable colored output
git config --global color.ui auto
# Set default pull behavior (rebase instead of merge)
git config --global pull.rebase true
# Configure line endings (cross-platform)
# Windows:
git config --global core.autocrlf true
# macOS/Linux:
git config --global core.autocrlf input
# View all settings
git config --list
git config --global --list
# Edit global config file directly
git config --global --edit
راهاندازی SSH برای GitHub
SSH Keys اجازه میدهند بدون وارد کردن پسورد در هر push/pull، با GitHub ارتباط امن برقرار کنید. این روش ایمنتر از HTTPS با Personal Access Token است و برای کار روزمره توصیه میشود.
# Step 1: Generate a new SSH key (Ed25519 - modern & secure)
ssh-keygen -t ed25519 -C "abbas@example.com"
# Press Enter to accept default path (~/.ssh/id_ed25519)
# Set a passphrase (optional but recommended)
# Step 2: Start ssh-agent in background
eval "$(ssh-agent -s)"
# Step 3: Add private key to ssh-agent
ssh-add ~/.ssh/id_ed25519
# Step 4: Copy public key to clipboard
cat ~/.ssh/id_ed25519.pub
# macOS: pbcopy < ~/.ssh/id_ed25519.pub
# Windows: clip < ~/.ssh/id_ed25519.pub
# Linux: xclip -sel clip < ~/.ssh/id_ed25519.pub
# Step 5: Add key to GitHub → Settings → SSH and GPG keys → New SSH key
# Step 6: Test connection
ssh -T git@github.com
# Output: Hi A-talebifard! You've successfully authenticated...
هرگز کلید خصوصی (id_ed25519 بدون .pub) را با کسی به اشتراک نگذارید یا در repo قرار ندهید. فقط کلید عمومی (.pub) به GitHub اضافه میشود.
ساخت اولین Repository
# Create a new directory and initialize git
mkdir my-first-repo
cd my-first-repo
git init
# Create a README file
echo "# My First Repo" > README.md
# Check status (file is untracked)
git status
# Stage the file
git add README.md
# Commit with a message
git commit -m "Initial commit: add README"
# View commit history
git log --oneline
# Connect to GitHub (create empty repo on GitHub first)
git remote add origin git@github.com:A-talebifard/my-first-repo.git
git branch -M main
git push -u origin main
دستورات پایه Git #
این فصل، دستورات روزمرهی Git را پوشش میدهد؛ دستوراتی که در هر ساعت کار با Git از آنها استفاده میکنید. درک عمیق این دستورات، پایهی همه کارهای پیچیدهتر است. هر دستور با مثال عملی و توضیح خروجی نشان داده شده است.
git init — ساخت repository جدید
این دستور یک پوشهی مخفی .git در دایرکتوری فعلی میسازد که تمام تاریخچه، تنظیمات و اشیای Git در آن ذخیره میشوند. دو حالت دارد: ساخت repo جدید در دایرکتوری موجود، یا clone از remote.
# Initialize a new git repo in current directory
mkdir my-project && cd my-project
git init
# Output: Initialized empty Git repository in /path/.git/
# Initialize with a specific default branch
git init -b main
# Initialize a bare repo (for servers, no working dir)
git init --bare my-project.git
# Clone an existing remote repository
git clone https://github.com/A-talebifard/my-project.git
git clone git@github.com:A-talebifard/my-project.git # via SSH
# Clone into a specific directory
git clone git@github.com:A-talebifard/my-project.git my-folder
# Clone only the latest commit (shallow, saves bandwidth)
git clone --depth 1 git@github.com:A-talebifard/my-project.git
git add — staging تغییرات
دستور add تغییرات را از Working Directory به Staging Area منتقل میکند. این یک «دو مرحلهای» بودن Git است که به شما اجازه میدهد قبل از commit، دقیقاً انتخاب کنید چه چیزی در commit قرار گیرد.
# Stage a single file
git add README.md
# Stage multiple files
git add file1.txt file2.txt file3.txt
# Stage all changes in current directory (recursive)
git add .
git add --all # stages deletions too
git add -A # same as --all
# Stage all .js files
git add "*.js"
# Stage interactively (patch mode - choose hunks)
git add -p
# For each changed hunk, choose: y (yes), n (no), s (split), q (quit)
# Stage a specific directory
git add src/components/
# Stage based on modification type
git add -u # only modified/deleted, not new files
git commit — ثبت تغییرات
هر commit یک snapshot از وضعیت staged است. پیام commit اهمیت زیادی دارد: باید کوتاه، توصیفی و در زمان حال باشد. استاندارد Conventional Commits برای پروژههای حرفهای توصیه میشود.
# Basic commit with message
git commit -m "Add user authentication"
# Multi-line commit message (title + body)
git commit -m "Add user authentication
- Implement JWT-based login flow
- Add /api/auth/login and /api/auth/register
- Add password hashing with bcrypt
- Add tests for auth middleware"
# Stage all tracked modifications and commit in one step
git commit -am "Update README"
# Amend the last commit (add changes or fix message)
git commit --amend -m "New message"
git commit --amend --no-edit # keep message, add changes
# Empty commit (useful for CI triggers)
git commit --allow-empty -m "Trigger CI"
# Sign commit with GPG key
git commit -S -m "Signed commit"
# Conventional Commits format
# <type>(<scope>): <description>
git commit -m "feat(auth): add OAuth2 login support"
git commit -m "fix(api): handle null user in /profile"
git commit -m "docs: update installation guide"
git commit -m "refactor: simplify token validation"
git commit -m "test: add unit tests for user model"
git commit -m "chore: bump dependencies"
پنج نوع اصلی: feat (قابلیت جدید)، fix (رفع باگ)، docs (مستندات)، refactor (بازنویسی بدون تغییر رفتار)، test (تست)، chore (کارهای نگهداری). این استاندارد، تولید CHANGELOG خودکار و semantic versioning را ممکن میکند.
git status — مشاهده وضعیت
# Show working tree status
git status
# Short format (compact)
git status -s
# Output:
# M modified-file.txt (modified, not staged)
# M staged-file.txt (staged)
# A new-staged-file.txt (added & staged)
# ?? untracked-file.txt (untracked)
# Show branch info
git status -b
# Show ignored files too
git status --ignored
git log — تاریخچه commits
# Show commit history
git log
# One-line compact format
git log --oneline
# Output:
# a1b2c3d (HEAD -> main, origin/main) Add user auth
# d4e5f6g Update README
# 7h8i9j0 Initial commit
# Graph view with branches
git log --oneline --graph --all --decorate
# Show last N commits
git log -5
# Show commits by a specific author
git log --author="Abbas"
# Show commits since a date
git log --since="2 weeks ago"
git log --since="2024-01-01"
# Show commits for a specific file
git log -- path/to/file.txt
# Show diff for each commit
git log -p
# Show stats per commit
git log --stat
# Show commits in a date range
git log --after="2024-01-01" --before="2024-06-01"
# Search commits by message (regex)
git log --grep="auth"
# Custom format
git log --pretty=format:"%h - %an, %ar : %s"
git diff — مشاهده تفاوتها
# Diff working dir vs staging area (unstaged changes)
git diff
# Diff staging area vs last commit (staged changes)
git diff --staged
git diff --cached # same as --staged
# Diff working dir vs last commit (all changes)
git diff HEAD
# Diff between two commits
git diff a1b2c3d d4e5f6g
# Diff between two branches
git diff main feature/login
# Diff for a specific file
git diff path/to/file.txt
# Show only file names that changed
git diff --name-only
# Show stats (insertions/deletions)
git diff --stat
# Word-level diff (better for prose)
git diff --word-diff
git remote — مدیریت remoteها
# List remote repositories
git remote -v
# origin git@github.com:A-talebifard/repo.git (fetch)
# origin git@github.com:A-talebifard/repo.git (push)
# Add a remote
git remote add origin git@github.com:A-talebifard/repo.git
# Add upstream (for forks)
git remote add upstream git@github.com:original-owner/repo.git
# Change remote URL (e.g., from HTTPS to SSH)
git remote set-url origin git@github.com:A-talebifard/repo.git
# Rename a remote
git remote rename origin github
# Remove a remote
git remote remove origin
# Show details of a remote
git remote show origin
git push و git pull — همگامسازی با remote
# Push current branch to origin, set upstream tracking
git push -u origin main
# -u is shorthand for --set-upstream
# After first push, just:
git push
git push origin main
# Force push (DANGEROUS - rewrites remote history)
git push --force-with-lease # safer than --force
git push -f # use only on personal branches!
# Push all branches
git push --all
# Push tags
git push --tags
git push origin v1.0.0
# Delete a remote branch
git push origin --delete feature/old-branch
# Pull = fetch + merge
git pull
git pull origin main
# Pull with rebase (cleaner history)
git pull --rebase origin main
# Fetch without merging (safer)
git fetch origin
git fetch --all --prune
هرگز git push --force روی شاخههای اشتراکی (main، develop) انجام ندهید. این کار تاریخچهی remote را بازنویسی میکند و commitهای همکاران را از بین میبرد. همیشه از --force-with-lease استفاده کنید که در صورت وجود commitهای جدید روی remote، عملیات را متوقف میکند.
مفاهیم داخلی: Objects و Hashes #
برای تسلط واقعی بر Git، باید بدانید درون پوشهی .git چه میگذرد. Git یک پایگاه داده کلید-مقدار است که اشیای مختلفی را با هش SHA-1 (یا SHA-256 در نسخههای جدید) ذخیره میکند. درک این مفاهیم، حل مشکلات پیچیده و استفاده از ابزارهای پیشرفته را آسان میکند.
چهار نوع object در Git
| نوع | توضیح | محتوای ذخیره شده |
|---|---|---|
blob | محتوای یک فایل | محتوای باینری فایل (بدون نام) |
tree | یک دایرکتوری | لیست blobs و trees با نام و حالت |
commit | یک snapshot | tree، parent(s)، author، message |
tag | تگ annotated | commit، tagger، message |
SHA: a1b2c3d"] T["Tree Object
(root)
SHA: e5f6g7h"] B1["Blob: README.md
SHA: i9j0k1l"] B2["Blob: package.json
SHA: m3n4o5p"] T2["Tree: src/
SHA: q7r8s9t"] B3["Blob: src/index.js
SHA: u1v2w3x"] C -->|points to| T C -->|parent| C2["Parent Commit
SHA: y4z5a6b"] T -->|entry| B1 T -->|entry| B2 T -->|entry| T2 T2 -->|entry| B3 style C fill:#c084fc,stroke:#1e1e2e,color:#1e1e2e style T fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e style T2 fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e style B1 fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style B2 fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style B3 fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style C2 fill:#c084fc,stroke:#1e1e2e,color:#1e1e2e
کاوش اشیای داخلی
# Look inside the .git directory
ls -la .git/
# HEAD config description hooks/ info/ objects/ refs/
# View the HEAD pointer
cat .git/HEAD
# Output: ref: refs/heads/main
# View the type of any object by SHA
git cat-file -t a1b2c3d
# Output: commit
# View the content of an object (raw)
git cat-file -p a1b2c3d
# Output:
# tree e5f6g7h...
# parent y4z5a6b...
# author Abbas <abbas@example.com> 1700000000 +0330
# committer Abbas <abbas@example.com> 1700000000 +0330
#
# Add user authentication
# Hash a file without storing it (see what hash it WOULD get)
git hash-object README.md
# Output: i9j0k1l...
# Write a file to git object database
git hash-object -w README.md
# List all objects in the database
git rev-list --all --objects | head -20
# View tree contents
git ls-tree HEAD
git ls-tree HEAD src/
# Show commit details
git show a1b2c3d
Refs — اشارهگرها به commits
Refs نامهای قابل خواندن برای انسان هستند که به SHA-1 commits اشاره میکنند. اینها در پوشهی .git/refs ذخیره میشوند.
# View all refs
git show-ref
# Local branches
ls .git/refs/heads/
cat .git/refs/heads/main
# Output: a1b2c3d... (SHA of latest commit on main)
# Remote branches
ls .git/refs/remotes/origin/
# Tags
ls .git/refs/tags/
# HEAD points to current branch
cat .git/HEAD
# ref: refs/heads/main
# Special refs
# ORIG_HEAD - previous HEAD (after reset/merge)
# MERGE_HEAD - commit being merged in
# FETCH_HEAD - result of last fetch
# Symbolic ref (HEAD points to a branch)
git symbolic-ref HEAD
# refs/heads/main
# Detached HEAD (HEAD points to a commit directly)
git checkout a1b2c3d
git symbolic-ref HEAD
# fatal: ref HEAD is not a symbolic ref
git reflog — تاریخچه پنهان
Reflog یکی از قدرتمندترین ویژگیهای Git است. هر بار که HEAD حرکت میکند (commit، checkout، reset، rebase)، یک ورودی در reflog ذخیره میشود. این تاریخچه محلی است و در repo به اشتراک گذاشته نمیشود، اما برای بازیابی commitهای «گمشده» بینظیر است.
# View reflog for HEAD
git reflog
# Output:
# a1b2c3d HEAD@{0}: commit: Add feature X
# y4z5a6b HEAD@{1}: checkout: moving from feature to main
# 7h8i9j0 HEAD@{2}: reset: moving to HEAD~1
# k1l2m3n HEAD@{3}: commit: Important work (was "lost")
# View reflog for a specific branch
git reflog show feature/login
# Recover a "lost" commit
git reset --hard k1l2m3n # go back to that commit
# Expire old reflog entries (cleanup)
git reflog expire --expire=90.days --all
# Garbage collect (permanently remove unreachable objects)
git gc --prune=now
اگر تا حالا فکر کردهاید «یک commit را گم کردم»، اصلاً نگران نباشید. تا ۹۰ روز (پیشفرض)، آن commit در reflog وجود دارد و با git reflog و سپس git reset --hard <sha> قابل بازیابی است. این یکی از بزرگترین مزایای Git نسبت به VCSهای دیگر است.
سه حالهی فایلها در .git
- Loose objects — هر object به صورت فشردهشده در یک فایل جدا در
.git/objects/ab/cdef...ذخیره میشود. - Packed objects — هنگام
git gc، اشیای پراکنده در یک فایل.packفشرده میشوند تا فضای کمتری بگیرند. - Deltas — اشیای مشابه به صورت تفاوت با یک base object ذخیره میشوند (بدون تغییر در منطق کاربر).
Branching (شاخهبندی) #
Branching یکی از قدرتمندترین ویژگیهای Git است. در Git، یک branch فقط یک pointer سبک به یک commit است؛ برخلاف SVN که کل کپی فایلها را کپی میکرد. این یعنی ساختن، جابهجایی و حذف branch در Git آنی و تقریباً بدون هزینه است. این موضوع، مدل توسعهای که در آن هر قابلیت جدید روی یک branch جدا توسعه مییابد، را عملی میکند.
دستورات پایهی Branch
# List all local branches
git branch
# * main
# feature/login
# feature/signup
# List all branches (local + remote)
git branch -a
# List branches with last commit info
git branch -v
# List merged branches (into current)
git branch --merged
# List unmerged branches
git branch --no-merged
# Create a new branch (stay on current)
git branch feature/login
# Create and switch in one step
git switch -c feature/login
git checkout -b feature/login # older syntax
# Switch to an existing branch
git switch main
git checkout main # older syntax
# Switch to previous branch
git switch -
git checkout - # older syntax
# Rename current branch
git branch -m new-name
# Rename another branch
git branch -m old-name new-name
# Delete a fully-merged branch (safe)
git branch -d feature/login
# Force delete a branch (even if not merged)
git branch -D feature/login
# Delete a remote branch
git push origin --delete feature/login
# Track a remote branch
git switch -c feature/login --track origin/feature/login
git checkout -b feature/login origin/feature/login
# Push a new branch to remote and set upstream
git push -u origin feature/login
Merge — ادغام شاخهها
Merge، تغییرات یک branch را به branch فعلی وارد میکند. دو نوع اصلی دارد: Fast-forward و Three-way merge.
(main)"] A3 -.->|fast-forward| A4["C4
(feature)"] end subgraph TW["Three-way Merge"] B1["C1"] --> B2["C2 (main)"] B1 --> B3["C3 (feature)"] B2 --> B4["C4 (main, new)"] B3 --> B4 B4 -.->|merge commit| B5["M1"] end style A4 fill:#a6e3a1,stroke:#1e1e2e,color:#1e1e2e style B5 fill:#c084fc,stroke:#1e1e2e,color:#1e1e2e
# Switch to target branch (e.g., main)
git switch main
# Merge feature branch into main
git merge feature/login
# Fast-forward merge (default when possible)
# If main hasn't changed since branch was created,
# Git just moves the main pointer forward
# Three-way merge (when both branches have new commits)
# Git creates a merge commit with two parents
# Disable fast-forward (always create merge commit)
git merge --no-ff feature/login
# Force fast-forward (fail if not possible)
git merge --ff-only feature/login
# Merge with a message
git merge -m "Merge feature/login into main" feature/login
# Squash merge (combine all commits into one)
git merge --squash feature/login
git commit -m "Add login feature (squashed)"
# Abort a merge in progress
git merge --abort
# Check if a branch is merged
git branch --merged | grep feature/login
Fast-forward: زمانی که main ثابت بوده — تاریخچه خطی تمیز میسازد. --no-ff: زمانی که میخواهید نشان دهید یک feature در یک بازه زمانی توسعه یافته — مرز واضح بین featureها. Squash: وقتی feature branch دارای commitهای «WIP» یا نامرتب است و میخواهید تاریخچهی main تمیز بماند.
عملیاتهای پیشرفته روی Branch
# Show branches that contain a specific commit
git branch --contains a1b2c3d
# Show remote branches that contain a commit
git branch -r --contains a1b2c3d
# Compare branches
git log main..feature/login # commits in feature, not in main
git log feature/login..main # commits in main, not in feature
git log --left-right main...feature/login # both, with markers
# Show diff between branch tips
git diff main feature/login
# Cherry-pick a single commit from another branch
git cherry-pick a1b2c3d
# Move a branch to a different commit
git branch -f feature/login a1b2c3d
# Create a branch from a specific commit
git branch hotfix a1b2c3d
# List branches sorted by last commit date
git for-each-ref --sort=-committerdate refs/heads/ \
--format='%(committerdate:short) %(refname:short)'
Rebase در برابر Merge #
یکی از مهمترین تصمیمات در کار با Git، انتخاب بین merge و rebase برای یکپارچهسازی تغییرات است. هر دو به یک نتیجهی نهایی مشابه میرسند (تغییرات دو branch با هم ترکیب میشوند)، اما تاریخچهی متفاوتی تولید میکنند. درک این تفاوت، کلید داشتن تاریخچهی تمیز و قابل فهم است.
git merge — حفظ تاریخچه
Merge یک commit جدید (merge commit) میسازد که دو والد دارد. تاریخچه واقعی preserved میشود: میتوانید ببینید چه زمانی یک branch شروع شد، چه زمانی به هم پیوست و چه کسی این کار را کرد. این روش برای branchهای اشتراکی امنتر است زیرا تاریخچه را بازنویسی نمیکند.
git rebase — بازنویسی تاریخچه
Rebase commitهای یک branch را برمیدارد و آنها را دوباره روی base جدید «دوباره play میکند». نتیجه: یک تاریخچه خطی و تمیز. اما این یعنی commitهای جدیدی با SHA جدید ساخته میشوند — یعنی تاریخچه بازنویسی شده. این موضوع اگر روی branchهای اشتراکی انجام شود، فاجعهبار است.
# Basic rebase: replay feature commits on top of main
git switch feature/login
git rebase main
# Rebase and then fast-forward main (clean alternative to merge)
git switch feature/login
git rebase main
git switch main
git merge --ff-only feature/login # safe now
# Interactive rebase (most powerful tool!)
git rebase -i HEAD~3
# Opens editor with last 3 commits:
# pick a1b2c3d Commit 1
# pick d4e5f6g Commit 2
# pick 7h8i9j0 Commit 3
#
# Commands you can use:
# pick = use commit as-is
# reword = use commit, but edit message
# edit = use commit, but pause to amend
# squash = combine with previous commit
# fixup = like squash, but discard message
# drop = remove commit entirely
# reorder = rearrange lines to reorder commits
# Abort a rebase
git rebase --abort
# Continue after fixing conflicts
git rebase --continue
# Skip a commit (if it's empty after rebase)
git rebase --skip
# Rebase onto a different base (advanced)
git rebase --onto main old-base feature/login
مثال: Squash با Interactive Rebase
# Before: 5 messy commits on feature branch
git log --oneline
# f5g6h7i WIP: login form
# j8k9l0m fix typo
# n1p2q3r WIP: validation
# s4t5u6v cleanup
# w7x8y9z Add login feature
# Start interactive rebase for last 5 commits
git rebase -i HEAD~5
# In editor, change to:
# pick w7x8y9z Add login feature
# squash s4t5u6v cleanup
# squash n1p2q3r WIP: validation
# squash j8k9l0m fix typo
# squash f5g6h7i WIP: login form
# After saving, edit combined message:
# Add login feature
#
# - Form with email/password
# - Client-side validation
# - API integration
# Result: 1 clean commit instead of 5
git log --oneline
# a1b2c3d Add login feature
هرگز روی commitهای اشتراکی rebase نکنید! یعنی اگر branchی به remote push شده و همکاران از آن استفاده میکنند، rebase نکنید. چون rebase، SHAها را عوض میکند، برای همکاران شما به نظر میرسد تاریخچه از بین رفته. قانون: rebase برای branchهای محلی و شخصی، merge برای branchهای اشتراکی.
جدول مقایسه
| معیار | Merge | Rebase |
|---|---|---|
| تاریخچه | واقعی، با merge commits | خطی، تمیز |
| SHA commits | حفظ میشوند | عوض میشوند |
| روی branch اشتراکی | ✅ امن | ❌ خطرناک |
| پیچیدگی | ساده | نیاز به درک عمیق |
| Conflict | یکبار، در merge | ممکن است در هر commit |
| خوانایی log | گراف پیچیده | خطی و واضح |
| پیشفرض در | بیشتر پروژهها | پروژههای large-scale |
حل Conflict (تعارض) #
Conflict زمانی رخ میدهد که دو نفر همان خط از همان فایل را به روشهای متفاوت تغییر دادهاند و Git نمیتواند به طور خودکار تصمیم بگیرد کدام را نگه دارد. Conflict طبیعی است و نشانهی بد بودن کد نیست؛ بلکه نشانهی کار همزمان است. در این فصل، نشانهها، روش حل دستی و استفاده از ابزارها را میآموزید.
Conflict چه شکلی است؟
// Original file (on main):
function greet(name) {
return "Hello, " + name;
}
// During merge, Git shows conflict like this:
<<<<<<< HEAD
function greet(name) {
return "Hi, " + name + "!";
}
=======
function greet(name) {
return `Hello, ${name}!`;
}
>>>>>>> feature/login
// <<<<<<< HEAD : your current branch (HEAD)
// ======= : separator
// >>>>>>> feature/login : incoming branch
مراحل حل Conflict
# Step 1: Identify conflicted files
git status
# Output:
# Unmerged paths:
# both modified: src/auth.js
# both modified: README.md
# Step 2: Open each conflicted file in your editor
# Look for <<<<<<<, =======, >>>>>>> markers
# Edit to keep the final desired code (remove all markers)
# Step 3: Stage the resolved files
git add src/auth.js README.md
# Step 4: Complete the merge
git commit
# (For rebase: git rebase --continue)
# ===== If you want to abort the merge entirely =====
git merge --abort
# (For rebase: git rebase --abort)
# ===== View conflicts in a visual mergetool =====
git mergetool
# Opens configured merge tool (VS Code, Meld, KDiff3, Beyond Compare)
# Configure default mergetool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
استراتژیهای merge خودکار
Git برای فایلهای متفاوت یا خطوط متفاوت، خودکار conflict را حل میکند. اما میتوانید استراتژی پیشفرض را تغییر دهید.
# Default strategy: recursive (now called "ort" in modern Git)
git merge feature/login
# Prefer "ours" — keep our version on conflict
git merge -X ours feature/login
# Prefer "theirs" — keep their version on conflict
git merge -X theirs feature/login
# Show common ancestor in conflict markers (helpful!)
git merge -X diff-algorithm=histogram feature/login
# Use a specific strategy
git merge -s recursive feature/login
git merge -s octopus branch1 branch2 branch3 # for 3+ branches
git merge -s ours redundant-branch # discard their changes, just record merge
# ===== Pull strategies for conflicts =====
# When pulling, prefer rebase to avoid extra merge commits
git config --global pull.rebase true
# Or set per-pull
git pull --rebase origin main
۱) ارتباط مداوم: قبل از شروع کار، pull کنید. ۲) branchهای کوتاهمدت: هرچه branch طولانیتر باشد، احتمال conflict بیشتر. ۳) فایلهای کوچک و ماژولار: اگر همه روی یک فایل بزرگ کار کنند، conflict قطعی است. ۴) قراردادهای کدنویسی: فرمت یکسان (Prettier، ESLint) conflictهای مصنوعی را کم میکند.
Conflict در عمل: مثال واقعی
# Scenario: You're on feature/login, your colleague pushed to main
# 1. Try to merge latest main into your feature
git switch feature/login
git merge main
# CONFLICT (content): Merge conflict in src/auth.js
# 2. View the conflict
git diff
# Shows the <<<<<<< ======= >>>>>>> markers
# 3. Open in editor, manually resolve
# Before:
<<<<<<< HEAD
const API_URL = "https://api.staging.example.com";
=======
const API_URL = "https://api.example.com";
>>>>>>> main
# After resolution (you decide which, or combine):
const API_URL = process.env.API_URL || "https://api.example.com";
# 4. Stage and commit
git add src/auth.js
git commit -m "Merge main into feature/login, resolve API_URL conflict"
استراتژیهای Branching #
استراتژی branching، قواعد نامگذاری و workflow شاخهها در یک تیم است. انتخاب استراتژی مناسب، بسته به اندازه تیم، نوع پروژه و فرکانس release متفاوت است. در این فصل، سه استراتژی اصلی را بررسی میکنیم: Git Flow (جامع برای نسخهگذاری)، GitHub Flow (ساده برای web apps) و Trunk-Based (سریع برای CI/CD).
۱. Git Flow
گستردهترین استراتژی برای پروژههای نسخهگذاری شده (مثل نرمافزارهای دسکتاپ، کتابخانهها). توسط Vincent Driessen در ۲۰۱۰ معرفی شد. شش نوع branch دارد و برای releaseهای برنامهریزیشده مناسب است.
| Branch | منبع | مقصد merge | هدف |
|---|---|---|---|
main | — | — | کد production |
develop | main | main (via release) | تلفیق featureها |
feature/* | develop | develop | قابلیتهای جدید |
release/* | develop | main + develop | آمادهسازی release |
hotfix/* | main | main + develop | رفع باگ production |
۲. GitHub Flow
استراتژی ساده و سبک که GitHub پیشنهاد میدهد. فقط دو نوع branch: main و feature. مناسب پروژههایی که به طور مداوم deploy میشوند (SaaS، web apps). هیچ release branch یا develop branch ندارد.
۳. Trunk-Based Development
همه روی یک branch اصلی (trunk) کار میکنند. feature branches کوتاهمدت (یک تا دو روز) هستند. مناسب تیمهای با CI/CD قوی که میخواهند سرعت deploy بالا باشد. گوگل، فیسبوک و Netflix از این مدل استفاده میکنند.
مقایسه استراتژیها
| معیار | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| پیچیدگی | بالا | پایین | پایین |
| تعداد branchها | ۶ | ۲ | ۱-۲ |
| فرکانس release | هفتهای/ماهانه | روزانه | چند بار در روز |
| نیاز به CI/CD | متوسط | بالا | بسیار بالا |
| مناسب برای | نسخهگذاری شده | Web apps | SaaS بزرگ |
| هزینه آموزش | زیاد | کم | متوسط |
برای اکثر پروژههای کوچک و متوسط، GitHub Flow بهترین انتخاب است. ساده، قابل فهم و برای عملیات روزانه کافی است. Git Flow را فقط برای پروژههایی با releaseهای رسمی (مثل کتابخانههای open source) انتخاب کنید. Trunk-Based را فقط اگر CI/CD قوی دارید و تیم به اندازه کافی بزرگ است، در نظر بگیرید.
GitHub — معرفی و راهاندازی #
GitHub سرویس میزبانی Git مبتنی بر وب است که در سال ۲۰۰۸ تأسیس شد و در ۲۰۱۸ توسط مایکروسافت خریداری شد. با بیش از ۱۰۰ میلیون توسعهدهنده، بزرگترین پلتفرم collaboration برای کد در جهان است. GitHub روی Git ساخته شده و قابلیتهایی مثل Pull Request، Issues، Actions و Projects را اضافه میکند.
Fork در برابر Clone
(github.com/upstream/repo)"] F["Your Fork
(github.com/A-talebifard/repo)"] L["Local Clone
(your machine)"] O -->|fork| F F -->|clone| L L -->|push| F F -->|PR| O L -.->|fetch upstream| O style O fill:#f38ba8,stroke:#1e1e2e,color:#1e1e2e style F fill:#c084fc,stroke:#1e1e2e,color:#1e1e2e style L fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e
# ===== Scenario: Contributing to someone else's project =====
# Step 1: Fork on GitHub (via web UI: click "Fork" button)
# Now you have: github.com/A-talebifard/their-project
# Step 2: Clone YOUR fork (not the original)
git clone git@github.com:A-talebifard/their-project.git
cd their-project
# Step 3: Add upstream remote (original repo)
git remote add upstream git@github.com:original-owner/their-project.git
# Step 4: Create a feature branch
git switch -c feature/my-contribution
# Step 5: Make changes, commit, push to your fork
git push -u origin feature/my-contribution
# Step 6: Open Pull Request on GitHub (your-fork → upstream)
# Step 7: Keep your fork in sync with upstream
git fetch upstream
git switch main
git merge upstream/main
git push origin main
# ===== Using GitHub CLI (gh) =====
# Install: https://cli.github.com/
gh auth login # one-time setup
gh repo clone A-talebifard/their-project
gh repo fork original-owner/repo --clone
gh pr create --title "Add dark mode" --body "Resolves #42"
gh pr status
gh pr checkout 123 # checkout PR #123 locally
gh pr merge 123 --squash --delete-branch
Personal Access Tokens (PAT)
از آگوست ۲۰۲۱، GitHub دیگر اجازه نمیدهد با پسورد اکانت از طریق HTTPS به repository ها push کنید. به جای آن، باید از Personal Access Token استفاده کنید. دو نوع PAT وجود دارد: Classic (با scopeهای دستی) و Fine-grained (با دسترسی دقیق به هر repo).
# Create PAT:
# GitHub → Settings → Developer settings → Personal access tokens → Generate new token
# Classic PAT scopes (for git operations):
# - repo (full control of private repos)
# - workflow (update GitHub Actions workflows)
# - read:org (read org and team membership)
# Use PAT in place of password (HTTPS)
git clone https://github.com/A-talebifard/repo.git
# Username: A-talebifard
# Password: <your-PAT>
# Cache credentials (avoid entering every time)
git config --global credential.helper store # plain text (not secure)
git config --global credential.helper cache # in-memory for 15 min
git config --global credential.helper 'cache --timeout=3600' # 1 hour
# macOS: use Keychain
git config --global credential.helper osxkeychain
# Windows: use Git Credential Manager (default with Git for Windows)
git config --global credential.helper manager
# Embed PAT in URL (NOT recommended, but possible)
git remote set-url origin https://A-talebifard:<PAT>@github.com/A-talebifard/repo.git
README.md — ویترین پروژه
# Project Name
> Short one-line description of what this project does.



## Features
- ✨ Feature one
- 🚀 Feature two
- 🔒 Feature three
## Installation
```bash
npm install project-name
```
## Quick Start
```javascript
import { doSomething } from 'project-name';
doSomething({ option: true });
```
## Documentation
Full docs at [https://docs.example.com](https://docs.example.com)
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
## License
MIT © [Abbas Talebifard](https://github.com/A-talebifard)
۱) یک تگline description در ابتدا. ۲) Badgeهای وضعیت (build، license، version). ۳) بخش Installation واضح. ۴) مثال Quick Start قابل کپی. ۵) لینک به مستندات کامل. ۶) بخش Contributing. ۷) License. README اولین چیزی است که کاربر میبیند؛ تا ۳۰ ثانیه اول قانعکننده نباشد، کاربر میرود.
Pull Request — گردش کار کامل #
Pull Request (PR) قلب همکاری در GitHub است. PR یک درخواست رسمی برای merge کردن تغییرات شما به یک branch دیگر (معمولاً main) است. این فرآیند، فرصتی برای code review، بحث دربارهی تغییرات، اجرای CI و در نهایت ادغام است. در این فصل، چرخهی کامل PR را از ساخت تا merge میبینیم.
git switch -c feature/X"] B["2. Make changes & commit"] C["3. Push to your fork/origin
git push -u origin feature/X"] D["4. Open Pull Request
on GitHub"] E["5. CI checks run
Tests, lint, build"] F["6. Code review
Comments, suggestions"] G{"Review approved?"} H["7. Address feedback
push more commits"] I["8. Merge to main
squash/merge/rebase"] J["9. Delete feature branch"] K["10. Deploy to production"] A --> B --> C --> D --> E --> F --> G G -->|No| H --> C G -->|Yes| I --> J --> K style G fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style I fill:#a6e3a1,stroke:#1e1e2e,color:#1e1e2e style K fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e
گردش کار عملی
# ===== Step 1: Sync with main before starting =====
git switch main
git pull origin main
# ===== Step 2: Create a feature branch with descriptive name =====
git switch -c feature/add-jwt-auth
# Naming conventions:
# feature/xxx - new features
# fix/xxx - bug fixes
# docs/xxx - documentation
# refactor/xxx - code refactoring
# chore/xxx - maintenance tasks
# ===== Step 3: Make changes, commit in logical chunks =====
git add src/auth.js
git commit -m "feat(auth): implement JWT generation"
git add src/middleware.js
git commit -m "feat(auth): add auth middleware"
git add tests/auth.test.js
git commit -m "test(auth): cover token validation"
# ===== Step 4: Push to remote =====
git push -u origin feature/add-jwt-auth
# ===== Step 5: Open PR via GitHub CLI =====
gh pr create \
--title "feat(auth): Add JWT authentication" \
--body "## Description
Implements JWT-based authentication for the API.
## Type of Change
- [x] New feature
- [ ] Bug fix
- [ ] Breaking change
## Testing
- [x] Unit tests pass
- [x] Manual testing on staging
## Related Issue
Closes #42
## Checklist
- [x] Code follows style guide
- [x] Self-review completed
- [x] Documentation updated
- [x] No new warnings" \
--base main \
--head feature/add-jwt-auth
# ===== Step 6: After review feedback, make more commits =====
git add src/auth.js
git commit -m "fix(auth): handle expired tokens"
git push # automatically updates the PR
# ===== Step 7: View PR status =====
gh pr view --web
gh pr checks
# ===== Step 8: After approval, merge (choose strategy) =====
gh pr merge 123 --squash --delete-branch # recommended
gh pr merge 123 --merge --delete-branch # preserves all commits
gh pr merge 123 --rebase --delete-branch # linear history
# ===== Step 9: Sync local main =====
git switch main
git pull origin main
git branch -d feature/add-jwt-auth # cleanup
سه استراتژی Merge در GitHub
| استراتژی | توضیح | تاریخچه نهایی |
|---|---|---|
| Merge commit | یک merge commit با همه commitهای branch | کامل، با گراف شاخهها |
| Squash | همه commitها را یکی میکند | تمیز، یک commit per PR |
| Rebase | commitها را روی main rebase میکند | خطی، همه commitها preserved |
PR Template
برای استاندارد کردن PRها، یک فایل در .github/PULL_REQUEST_TEMPLATE.md بسازید تا برای هر PR خودکار ظاهر شود.
## Description
<!-- Brief description of what this PR does -->
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Refactor (no functional changes)
## Related Issue
Closes #(issue-number)
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing completed
## Screenshots (if applicable)
<!-- Drag and drop screenshots here -->
## Checklist
- [ ] My code follows the project's style guide
- [ ] I have performed a self-review
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have updated documentation accordingly
- [ ] My changes generate no new warnings
۱) به کد نقد کنید، نه به نفر. ۲) فرق بین «must fix» و «nit» را مشخص کنید. ۳) پیشنهاد به جای دستور: «چه میشود اگر...» به جای «این را عوض کن». ۴) خوبیها را هم بگویید، نه فقط مشکلات. ۵) اگر بحث طولانی شد، به جای کامنت، تماس تصویری بگیرید. ۶) برای کامنتهای خود، یک reaction (emoji) بگذارید تا مشخص باشد addressed شده.
Issues و Project Management #
GitHub Issues سیستم ردیابی باگها، taskها و inquests است که مستقیماً در repo ادغام شده. بههمراه Projects v2 و Discussions، یک پلتفرم کامل مدیریت پروژه را فراهم میکند. در این فصل، بهترین روشهای استفاده از Issues، Labels، Milestones و Projects را میبینیم.
Issues — ساخت و مدیریت
# Create issue via CLI
gh issue create \
--title "Bug: Login button not working on Safari" \
--body "## Description
The login button does not respond on Safari 17.
## Steps to Reproduce
1. Go to /login
2. Enter credentials
3. Click 'Sign In'
## Expected
Redirect to dashboard
## Actual
Nothing happens, console shows TypeError
## Environment
- Safari 17.0
- macOS 14.0
- Production: https://app.example.com
## Screenshots
<!-- drag screenshot here -->
## Labels
bug, frontend, safari, P1" \
--label "bug,P1" \
--assignee "@me" \
--project "Q4 Sprint"
# List open issues
gh issue list
gh issue list --state closed --limit 20
gh issue list --assignee "@me" --label "bug"
# View an issue
gh issue view 42
gh issue view 42 --web
# Close/reopen
gh issue close 42
gh issue close 42 --reason "completed" # or "not planned"
gh issue reopen 42
# Comment on an issue
gh issue comment 42 --body "Fixed in PR #43"
# Add labels, assignees
gh issue edit 42 --add-label "high-priority"
gh issue edit 42 --add-assignee "A-talebifard"
Issue Template
برای استاندارد کردن issueها، تمپلیتها در .github/ISSUE_TEMPLATE/ قرار دهید. این کار، اطلاعات لازم را از کاربر میگیرد و کیفیت گزارشها را بالا میبرد.
# .github/ISSUE_TEMPLATE/bug_report.yml
name: Bug Report
description: Report a bug to help us improve
labels: ["bug", "triage"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Description
description: A clear description of the bug
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
placeholder: |
1. Go to '...'
2. Click on '....'
3. See error
validations:
required: true
- type: dropdown
id: browser
attributes:
label: Browser
options:
- Chrome
- Firefox
- Safari
- Edge
- Other
- type: input
id: version
attributes:
label: App Version
placeholder: "v1.2.3"
Labels — دستهبندی
| دسته | Labels پیشفرض | هدف |
|---|---|---|
| نوع | bug, enhancement, feature | چه چیزی |
| وضعیت | triage, in-progress, blocked | کجای workflow |
| اولویت | P0, P1, P2, P3 | چقدر فوری |
| محدوده | frontend, backend, docs, ci | کدام بخش |
| سختی | good-first-issue, help-wanted | برای newcomers |
Milestones — نقاط عطف
Milestone گروهی از issueها و PRها را برای یک هدف مشخص (مثل release v2.0) گرد هم میآورد. به شما اجازه میدهد پیشرفت به سمت یک هدف را ببینید.
GitHub Projects v2
Projects v2 یک ابزار مدیریت پروژه انعطافپذیر است که Kanban، Table، Roadmap و Timeline viewها را پشتیبانی میکند. برخلاف v1، میتواند cross-repo باشد و از Issues، PRs و Draft Issues استفاده کند.
(Drafts)"] U["In Progress
(Issues + PRs)"] R["In Review
(PRs)"] D["Done
(Closed)"] T --> U --> R --> D end Repo1["Repo A Issues"] Repo2["Repo B Issues"] Repo3["Repo C PRs"] Repo1 -.-> T Repo2 -.-> T Repo3 -.-> R style T fill:#45475a,stroke:#c084fc,color:#cdd6f4 style U fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style R fill:#89b4fa,stroke:#1e1e2e,color:#1e1e2e style D fill:#a6e3a1,stroke:#1e1e2e,color:#1e1e2e
Discussions — گفتگوهای طولانی
برای پرسشها، ایدهها و گفتگوهای طولانی که issue نیستند، GitHub Discussions را فعال کنید (Settings → Features → Discussions). مناسب FAQ، Announcements و Community است.
GitHub Actions — CI/CD #
GitHub Actions پلتفرم CI/CD داخلی GitHub است که در ۲۰۱۹ معرفی شد. به شما اجازه میدهد workflowهای خودکاری بسازید که در رویدادهای مختلف (push، PR، release، schedule) اجرا شوند. با هزاران action آماده در Marketplace، تقریباً هر کاری میتوان خودکار کرد.
ساختار یک Workflow
# .github/workflows/ci.yml
name: CI
# When should this workflow run?
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# Schedule: every day at 3 AM UTC
schedule:
- cron: "0 3 * * *"
# Manual trigger
workflow_dispatch:
inputs:
debug:
description: 'Enable debug mode'
type: boolean
default: false
# Environment variables available to all jobs
env:
NODE_VERSION: '20'
CI: true
jobs:
# ===== Job 1: Lint =====
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci # clean install (faster than npm install)
- name: Run ESLint
run: npm run lint
# ===== Job 2: Test =====
test:
runs-on: ubuntu-latest
needs: lint # wait for lint job
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
env:
COVERAGE: true
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-${{ matrix.node-version }}
path: coverage/
# ===== Job 3: Build & Deploy =====
build:
runs-on: ubuntu-latest
needs: [lint, test]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
env:
API_KEY: ${{ secrets.API_KEY }}
- name: Deploy to production
run: |
echo "Deploying..."
./scripts/deploy.sh
Secrets و Variables
# Steps to add a secret:
# Repo → Settings → Secrets and variables → Actions → New repository secret
# Use in workflow:
steps:
- name: Use secret
run: |
curl -X POST https://api.example.com \
-H "Authorization: Bearer ${{ secrets.API_TOKEN }}"
- name: Use variable (non-sensitive)
run: echo "Environment is ${{ vars.ENVIRONMENT }}"
# Organization-level secrets (shared across repos):
# Org → Settings → Secrets → Actions
# Environment secrets (per-deployment):
# Repo → Settings → Environments → production → Add secret
jobs:
deploy:
environment: production # requires approval
steps:
- run: ./deploy.sh
env:
PROD_KEY: ${{ secrets.PROD_KEY }}
Workflow پیشرفته: Release خودکار
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*' # triggers on tags like v1.0.0, v2.1.3
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write # required to create release
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for changelog
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install & build
run: |
npm ci
npm run build
- name: Generate changelog
id: changelog
uses: mikepenz/release-changelog-builder-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.changelog.outputs.changelog }}
files: |
dist/*.zip
dist/*.tar.gz
CHANGELOG.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
۱) همیشه actions/checkout@v4 را pin کنید (نه @main). ۲) از npm ci به جای npm install استفاده کنید (سریعتر و قابل پیشبینی). ۳) Caching را فعال کنید (میتواند CI را ۵۰٪ سریعتر کند). ۴) Secrets را هرگز در log چاپ نکنید. ۵) Job parallelism را با strategy.matrix افزایش دهید. ۶) از Environment protection rules برای production استفاده کنید (approval لازم).
GitHub Pages — هاست رایگان #
GitHub Pages سرویس هاستینگ استاتیک رایگان GitHub است که اجازه میدهد سایت خود را مستقیماً از repo deploy کنید. مناسب برای portfolio، blog، مستندات پروژه و landing page است. هر اکانت GitHub یک سایت کاربر (username.github.io) و unlimited سایت پروژه (username.github.io/repo) رایگان دارد.
ساخت User Page
# Step 1: Create a special repo named exactly: username.github.io
# For example: A-talebifard.github.io
# Step 2: Clone and add content
git clone git@github.com:A-talebifard/A-talebifard.github.io.git
cd A-talebifard.github.io
echo "<h1>Hello World</h1>" > index.html
git add .
git commit -m "Initial site"
git push
# Step 3: Visit https://A-talebifard.github.io (live in ~1 min)
ساخت Project Page
# Any repo can have a Pages site
# Option A: From /docs folder on main branch
mkdir docs
echo "<h1>My Project</h1>" > docs/index.html
git add docs/
git commit -m "Add docs site"
git push
# Then: Repo → Settings → Pages → Source: main /docs folder
# Option B: From gh-pages branch
git switch --orphan gh-pages
git rm -rf .
echo "<h1>My Project</h1>" > index.html
git add .
git commit -m "Initial gh-pages"
git push origin gh-pages
# Site URL: https://username.github.io/repo-name/
Deploy با GitHub Actions
# .github/workflows/deploy-pages.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
Custom Domain
# Step 1: Add CNAME file in repo root or /docs
echo "www.example.com" > CNAME
git add CNAME
git commit -m "Add custom domain"
git push
# Step 2: Configure DNS
# For subdomain (www.example.com):
# CNAME www -> username.github.io
# For apex (example.com):
# A @ -> 185.199.108.153
# A @ -> 185.199.109.153
# A @ -> 185.199.110.153
# A @ -> 185.199.111.153
# Step 3: Enable HTTPS
# Repo → Settings → Pages → Custom domain → Enforce HTTPS ✓
# Step 4: Verify in 24-48 hours
curl -I https://www.example.com
GitHub Pages به طور پیشفرض از Jekyll (Ruby) پشتیبانی میکند. کافیست فایلهای Markdown بسازید و Jekyll خودکار آنها را به HTML تبدیل میکند. برای پروژههای مدرن، میتوانید از Next.js، Astro، Hugo یا VitePress هم استفاده کنید و خروجی build را deploy کنید.
Undo و Reset #
یکی از پرکاربردترین موضوعات Git، «برگرداندن» تغییرات است. اما کلمهی «undo» در Git چند معنی دارد: میتوانید یک commit را برگردانید، فایل را به حالت قبلی برگردانید، یا تاریخچه را بازنویس کنید. در این فصل، تفاوتهای ظریف بین reset، revert و checkout را میآموزید.
سه حالتی git reset
(still staged)"] end subgraph Mixed["reset --mixed HEAD~1 (default)"] M1["HEAD"] --> M2["Commit B"] M2 --> M3["Commit A"] M4["Working dir: changes of C
(unstaged)"] end subgraph Hard["reset --hard HEAD~1"] H1["HEAD"] --> H2["Commit B"] H2 --> H3["Commit A"] H4["Changes of C: GONE"] end style S4 fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style M4 fill:#f9e2af,stroke:#1e1e2e,color:#1e1e2e style H4 fill:#f38ba8,stroke:#1e1e2e,color:#1e1e2e
# ===== git reset --soft =====
# Moves HEAD only; staging area and working dir unchanged
git reset --soft HEAD~1
# Use case: "I want to re-commit my last commit with a different message
# or split it into multiple commits"
# ===== git reset --mixed (default) =====
# Moves HEAD + unstages changes (but keeps them in working dir)
git reset HEAD~1
git reset --mixed HEAD~1
# Use case: "I committed too early, want to re-stage selectively"
# ===== git reset --hard =====
# Moves HEAD + discards all changes (DANGEROUS!)
git reset --hard HEAD~1
# Use case: "I want to completely discard my last commit and changes"
# ===== Reset to a specific commit =====
git reset --hard a1b2c3d
# ===== Reset a single file to HEAD =====
git checkout HEAD -- path/to/file.txt
git restore path/to/file.txt # modern syntax (Git 2.23+)
# ===== Reset staging area (unstage everything) =====
git reset
# ===== Unstage a specific file =====
git reset path/to/file.txt
git restore --staged path/to/file.txt # modern
git revert — undo ایمن
برخلاف reset که تاریخچه را بازنویسی میکند، revert یک commit جدید میسازد که تغییرات commit قبلی را معکوس میکند. این روش برای branchهای اشتراکی ایمن است زیرا تاریخچه را تغییر نمیدهد.
# Revert a single commit (creates a new commit)
git revert a1b2c3d
# Opens editor with default message: "Revert: <original message>"
# Revert without auto-commit (combine multiple reverts)
git revert --no-commit a1b2c3d
git revert --no-commit d4e5f6g
git commit -m "Revert: remove broken auth changes"
# Revert a range of commits
git revert --no-commit HEAD~3..HEAD
# Revert a merge commit
git revert -m 1 a1b2c3d
# -m 1 means: keep parent 1 (the branch you merged INTO)
# Revert last commit but keep changes staged
git revert -n HEAD
# ===== Difference: reset vs revert =====
# reset: rewrites history (use on local/personal branches)
# revert: adds new commit that undoes (use on shared branches)
# ===== Scenario: Bad commit already pushed to main =====
# DON'T: git reset --hard HEAD~1 && git push -f
# DO:
git revert HEAD
git push
git reset --hard تغییرات uncommitted را برای همیشه پاک میکند (شامل staging area و working dir). فقط commitها از طریق reflog قابل بازیابی هستند. هرگز این دستور را وقتی تغییرات مهم uncommitted دارید، اجرا نکنید. اگر فقط میخواهید فایلها را برگردانید، از git restore استفاده کنید.
git checkout و git restore — بازیابی فایلها
# Restore a file from latest commit (discard local changes)
git checkout -- path/to/file.txt
git restore path/to/file.txt # modern (Git 2.23+)
# Restore a file from a specific commit
git checkout a1b2c3d -- path/to/file.txt
git restore --source=a1b2c3d path/to/file.txt
# Restore multiple files
git restore file1.txt file2.txt
# Restore all files in current directory
git restore .
# Restore staged changes (unstage)
git restore --staged path/to/file.txt
git restore --staged .
# Restore a deleted file (not yet committed deletion)
git restore path/to/deleted.txt
# Restore a deleted file (committed deletion)
git checkout HEAD~1 -- path/to/deleted.txt
# ===== View a file from a specific commit without restoring =====
git show a1b2c3d:path/to/file.txt
git clean — حذف فایلهای untracked
# Show what would be deleted (dry run)
git clean -n
git clean --dry-run
# Delete untracked files
git clean -f
# Delete untracked files and directories
git clean -fd
# Delete ignored files too (be careful!)
git clean -fdx
# Interactive clean
git clean -i
Stash و Cherry-pick #
دو دستور پرکاربرد اما کمتر شناختهشده: stash برای موقتاً کنار گذاشتن تغییرات، و cherry-pick برای انتخاب یک commit از branch دیگر. هر دو در کار روزمره به شدت مفیدند.
git stash — ذخیره موقت تغییرات
وقتی روی چیزی کار میکنید و باید به یک branch دیگر switch کنید اما نمیخواهید commit ناقص بسازید، stash به کمک میآید. تغییرات شما در یک stack ذخیره میشود و بعداً میتوانید برگردانید.
# Stash all tracked changes (modified + staged)
git stash
git stash push -m "WIP: working on login form"
# Stash including untracked files
git stash -u
git stash --include-untracked
# Stash only staged changes (keep unstaged in working dir)
git stash --keep-index
# Stash a specific file
git stash push -m "Just auth.js" -- path/to/auth.js
# List all stashes
git stash list
# Output:
# stash@{0}: On feature/login: WIP: working on login form
# stash@{1}: On main: quick fix
# stash@{2}: WIP on main: a1b2c3d Add README
# Show what's in a stash
git stash show stash@{0}
git stash show -p stash@{0} # full diff
# Apply most recent stash (keeps it in stack)
git stash apply
# Apply a specific stash
git stash apply stash@{2}
# Apply and remove from stack (most common)
git stash pop
# Drop a stash
git stash drop stash@{0}
# Clear all stashes (DANGEROUS)
git stash clear
# Create a branch from a stash
git stash branch new-branch stash@{0}
۱) روی feature کار میکنید. ۲) باگ urgent روی main میآید. ۳) git stash کنید. ۴) به main بروید، hotfix branch بسازید، درست کنید، push کنید. ۵) به feature برگردید. ۶) git stash pop کنید. این روش از ساخت commitهای ناقص («WIP»، «temporary») جلوگیری میکند.
git cherry-pick — انتقال یک commit
cherry-pick یک commit خاص را از یک branch به branch فعلی اعمال میکند. برخلاف merge که کل تاریخچه را میآورد، cherry-pick فقط یک commit را. مفید برای: انتقال hotfix بین branchها، یا جدا کردن یک قابلیت از یک branch شلوغ.
# Cherry-pick a single commit
git cherry-pick a1b2c3d
# Cherry-pick multiple commits
git cherry-pick a1b2c3d d4e5f6g 7h8i9j0
# Cherry-pick a range (exclusive of start)
git cherry-pick a1b2c3d..d4e5f6g
# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit a1b2c3d
git cherry-pick -n a1b2c3d
# Cherry-pick and edit commit message
git cherry-pick --edit a1b2c3d
# Cherry-pick but keep original message format
git cherry-pick -x a1b2c3d
# Adds: (cherry picked from commit a1b2c3d...)
# ===== Common scenario: hotfix from main to develop =====
# A bug was fixed on main, need same fix on develop
git switch develop
git cherry-pick a1b2c3d # the hotfix commit
git push
# ===== Resolve conflicts during cherry-pick =====
git cherry-pick a1b2c3d
# CONFLICT...
# Resolve, then:
git add .
git cherry-pick --continue
# Abort cherry-pick
git cherry-pick --abort
git bisect — پیدا کردن commit مخرب
وقتی یک باگ ظاهر شده و نمیدانید کدام commit آن را وارد کرده، bisect با binary search روی تاریخچه، commit مقصر را پیدا میکند.
# Start bisect session
git bisect start
# Mark current commit as bad (has the bug)
git bisect bad
# Mark a known-good commit (e.g., from a month ago)
git bisect good v1.0.0
# or: git bisect good a1b2c3d
# Git checks out a middle commit.
# Test it, then mark as good or bad:
git bisect good # or git bisect bad
# Continue until Git identifies the first bad commit
# Output: "a1b2c3d is the first bad commit"
# End bisect session (return to original branch)
git bisect reset
# ===== Automated bisect (with a test script) =====
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run npm test # auto-runs tests until failure found
git bisect reset
اگر تستی دارید که با fail شدنش، باگ را شناسایی میکند، git bisect run کل فرآیند را خودکار میکند. حتی اگر ۱۰۰۰ commit قبل از current باشد، در ~۱۰ مرحله (log2(1000)) پیدا میشود. این یکی از بهترین ویژگیهای Git برای debug است.
Submodules و Worktrees #
گاهی اوقات نیاز به ساختارهای پیشرفتهتری داریم: پروژهای که شامل پروژهی دیگر است (submodule)، یا کار همزمان روی چند branch بدون نیاز به stash (worktree). در این فصل، هر دو را میبینیم.
git submodule — repo در repo
Submodule اجازه میدهد یک Git repo را درون repo دیگر قرار دهید. مفید برای: shared libraries، monorepoهای ترکیبی، یا وقتی میخواهید یک repo را در چندین پروژه استفاده کنید بدون اینکه کد را کپی کنید.
(your-project)"] S1["Submodule: shared-lib
(separate Git repo)"] S2["Submodule: theme
(separate Git repo)"] P -->|.gitmodules| S1 P -->|.gitmodules| S2 style P fill:#c084fc,stroke:#1e1e2e,color:#1e1e2e style S1 fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e style S2 fill:#2dd4bf,stroke:#1e1e2e,color:#1e1e2e
# ===== Add a submodule =====
git submodule add git@github.com:shared/lib.git libs/shared-lib
git commit -m "Add shared-lib as submodule"
# This creates:
# 1. .gitmodules file (tracks submodule URL & path)
# 2. libs/shared-lib/ directory (the submodule content)
# 3. A gitlink entry (pointer to specific commit in submodule)
# ===== Clone a repo WITH its submodules =====
git clone --recurse-submodules git@github.com:A-talebifard/project.git
# If you forgot --recurse-submodules, initialize after clone:
git submodule update --init --recursive
# ===== Update all submodules to latest commit =====
git submodule update --remote
git add .
git commit -m "Update submodules to latest"
# ===== Update a specific submodule =====
cd libs/shared-lib
git switch main
git pull
cd ../..
git add libs/shared-lib
git commit -m "Update shared-lib to latest main"
# ===== View submodule status =====
git submodule status
# ===== Make changes INSIDE a submodule =====
cd libs/shared-lib
# Make changes, commit, push as normal
git add .
git commit -m "Fix bug in shared-lib"
git push origin main
cd ../..
git add libs/shared-lib
git commit -m "Bump shared-lib to latest with bug fix"
# ===== Remove a submodule =====
git submodule deinit -f libs/shared-lib
git rm -f libs/shared-lib
git commit -m "Remove shared-lib submodule"
Submoduleها قدرتمندند اما چالشبرانگیز: ۱) کاربران تازهکار یادشان میرود --recurse-submodules را اضافه کنند. ۲) Conflict در submodule references رایج است. ۳) CI باید submodules را init کند. ۴) اگر به submodules علاقهمندید، جایگزینهای مدرنتر مثل Git Subtree یا monorepo tools (Nx، Turborepo، Lerna) را هم بررسی کنید.
git worktree — کار همزمان روی چند branch
worktree اجازه میدهد چند working directory برای همان repo داشته باشید. یعنی میتوانید همزمان روی feature/login و main کار کنید بدون نیاز به stash یا clone جدید.
# Create a new working tree for an existing branch
git worktree add ../project-feature feature/login
# Now you have:
# /path/to/project -> main branch (original)
# /path/to/project-feature -> feature/login (new worktree)
# Create worktree with a NEW branch
git worktree add -b feature/api ../project-api
# List all worktrees
git worktree list
# Output:
# /path/to/project a1b2c3d [main]
# /path/to/project-feature d4e5f6g [feature/login]
# /path/to/project-api 7h8i9j0 [feature/api]
# Remove a worktree (just delete the directory)
git worktree remove ../project-feature
# Prune stale worktree entries
git worktree prune
# ===== Common use cases =====
# 1. Working on a feature when a hotfix is needed:
# - Open new worktree on main, fix, push, remove worktree
# 2. Running tests on one branch while coding on another
# 3. Comparing two versions side by side in two editors
Partial Clone و Sparse-Checkout
برای repoهای بزرگ، میتوانید فقط تاریخچهای که نیاز دارید دانلود کنید.
# ===== Partial clone: only download commits, fetch blobs on demand =====
git clone --filter=blob:none git@github.com:large/repo.git
# Faster clone, downloads file contents only when you read them
# Even more aggressive: no blobs, no trees
git clone --filter=tree:0 git@github.com:large/repo.git
# ===== Sparse checkout: only check out specific directories =====
git clone --no-checkout git@github.com:large/repo.git
cd repo
git sparse-checkout init --cone
git sparse-checkout set src/components src/utils
git checkout main
# Now only src/components/ and src/utils/ are on disk
# Add more directories later
git sparse-checkout add docs
# Disable sparse checkout
git sparse-checkout disable
Hooks و تنظیمات پیشرفته #
Git Hooks اسکریپتهایی هستند که بهطور خودکار قبل یا بعد از رویدادهای مهم (commit، push، receive) اجرا میشوند. با ترکیب hooks، .gitignore هوشمند و GPG signing، میتوانید کیفیت کد و امنیت پروژه را به طور چشمگیری افزایش دهید.
ساختار Hooks
Hooks در .git/hooks/ قرار دارند و به صورت پیشفرض sample هستند (با پسوند .sample). برای فعالسازی، پسوند را حذف کنید و فایل را executable کنید.
| Hook | زمان اجرا | کاربرد رایج |
|---|---|---|
pre-commit | قبل از ساخت commit | Lint، format، block sensitive data |
prepare-commit-msg | قبل از باز شدن editor پیام | افزودن issue number خودکار |
commit-msg | بعد از نوشته شدن پیام | اعتبارسنجی فرمت پیام (Conventional Commits) |
post-commit | بعد از کامل شدن commit | اعلان، backup |
pre-push | قبل از push | اجرای تست، block push به main |
pre-rebase | قبل از rebase | جلوگیری از rebase روی main |
post-merge | بعد از merge | نصب خودکار dependencies |
مثال: pre-commit hook
#!/bin/bash
# File: .git/hooks/pre-commit (make executable with: chmod +x)
# Exit on any error
set -e
echo "▶ Running pre-commit checks..."
# 1. Block commits with console.log in JS files
if git diff --cached --name-only | grep -E "\.(js|ts|jsx|tsx)$"; then
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E "\.(js|ts|jsx|tsx)$")
for FILE in $FILES; do
if grep -n "console\.log" "$FILE"; then
echo "❌ Found console.log in $FILE. Remove before committing."
exit 1
fi
done
fi
# 2. Block commits with TODO comments
if git diff --cached | grep -E "^\+.*TODO"; then
echo "⚠️ Warning: commit contains TODO comments"
# exit 1 # uncomment to make this blocking
fi
# 3. Check for secrets (basic patterns)
if git diff --cached | grep -iE "(api_key|password|secret)\s*=\s*['\"]"; then
echo "❌ Possible secret detected! Please review."
exit 1
fi
# 4. Run linter on staged files
if [ -f "package.json" ]; then
npx lint-staged
fi
echo "✅ Pre-commit checks passed"
exit 0
مثال: commit-msg hook (Conventional Commits)
#!/bin/bash
# File: .git/hooks/commit-msg
# Ensures commit messages follow Conventional Commits format
MSG_FILE="$1"
MSG=$(cat "$MSG_FILE")
# Regex: type(scope): description or type: description
PATTERN="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?!?: .{1,80}$"
# Skip merge commits
if echo "$MSG" | grep -q "^Merge "; then
exit 0
fi
# Skip revert commits
if echo "$MSG" | grep -q "^Revert "; then
exit 0
fi
# Check first line
FIRST_LINE=$(echo "$MSG" | head -1)
if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then
echo "❌ Invalid commit message format!"
echo ""
echo "Expected: type(scope): description"
echo "Examples:"
echo " feat(auth): add JWT login"
echo " fix: handle null user"
echo " docs: update README"
echo ""
echo "Got: $FIRST_LINE"
exit 1
fi
exit 0
حرفهای: Husky + lint-staged
برای پروژههای Node.js، ابزار Husky مدیریت hooks را در repo به اشتراک میگذارد (hooks پیشفرض در .git/hooks فقط محلی هستند).
# Install Husky
npm install --save-dev husky lint-staged
# Initialize Husky (creates .husky/ directory)
npx husky init
# Add pre-commit hook
echo "npx lint-staged" > .husky/pre-commit
# Configure lint-staged in package.json
# This runs specific commands on staged files only
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint --fix",
"prettier --write",
"git add"
],
"*.{css,scss}": [
"stylelint --fix",
"git add"
],
"*.{md,json,yaml}": [
"prettier --write",
"git add"
]
}
}
.gitignore — نادیده گرفتن فایلها
# ===== A comprehensive .gitignore =====
# Dependencies
node_modules/
.pnp/
.pnp.js
# Build output
dist/
build/
out/
.next/
.nuxt/
.cache/
.parcel-cache/
# Environment variables (NEVER commit!)
.env
.env.local
.env.*.local
*.env
# Secrets
secrets.json
*.pem
*.key
!public.key
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS files
.DS_Store
Thumbs.db
desktop.ini
# IDE / Editor
.vscode/
!.vscode/settings.json
!.vscode/extensions.json
.idea/
*.swp
*.swo
*~
.project
.classpath
.settings/
# Test coverage
coverage/
.nyc_output/
# Misc
*.pid
*.seed
*.pid.lock
.tmp/
tmp/
# Negation: include a file that would be ignored
!tests/fixtures/.env
برای شروع سریع، به gitignore.io بروید، زبان/فریمورک/OS خود را وارد کنید و یک .gitignore کامل دریافت کنید. GitHub هم در github/gitignore تمپلیتهای رسمی برای هر زبان دارد.
global .gitignore
# Create a global gitignore for OS/IDE files
echo "# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp" > ~/.gitignore_global
# Tell Git to use it
git config --global core.excludesfile ~/.gitignore_global
# Now you don't need to add these to every project's .gitignore
GPG Signing — امضای commits
GPG signing به commits شما امضای دیجیتال اضافه میکند تا قابل تأیید باشد که واقعاً شما آن را ساختهاید. GitHub commits امضاشده را با علامت "Verified" نشان میدهد.
# Step 1: Generate GPG key
gpg --full-generate-key
# Choose: (1) RSA and RSA
# Key size: 4096
# Validity: 1y (or 0 for no expiry)
# Real name: Abbas Talebifard
# Email: abbas@example.com (same as GitHub)
# Step 2: List GPG keys
gpg --list-secret-keys --keyid-format=long
# Output:
# sec rsa4096/ABC123DEF456 2024-01-01 [SC]
# Copy the KEY_ID (after the /): ABC123DEF456
# Step 3: Export public key
gpg --armor --export ABC123DEF456
# Copy the entire output (-----BEGIN PGP PUBLIC KEY BLOCK----- ...)
# Step 4: Add to GitHub → Settings → SSH and GPG keys → New GPG key
# Step 5: Configure Git to use GPG
git config --global user.signingkey ABC123DEF456
git config --global commit.gpgsign true # sign all commits by default
# Step 6: Sign a commit
git commit -S -m "Signed commit"
# Sign a tag
git tag -s v1.0.0 -m "Signed release v1.0.0"
# Verify a signature
git verify-commit HEAD
git tag -v v1.0.0
.gitattributes
# .gitattributes - per-file settings
# Normalize line endings (auto-convert on checkout/commit)
* text=auto
# Specific file types
*.txt text
*.jpg binary
*.png binary
*.pdf binary
# Force LF for shell scripts
*.sh text eol=lf
# Force CRLF for Windows batch files
*.bat text eol=crlf
# Mark generated files as linguist-vendored (excluded from GitHub stats)
docs/*.md linguist-vendored
minified/*.js linguist-generated=true
# Diff settings
*.md diff=markdown
*.json diff=json
Troubleshooting — حل مشکلات رایج #
حتی بهترین توسعهدهندگان هم به مشکل میخورند. این فصل، رایجترین مشکلات Git و راهحل آنها را پوشش میدهد. هر مشکل با سناریوی واقعی و دستورات بازیابی نشان داده شده است.
مشکل ۱: «یک commit را گم کردم!»
# Scenario: Did git reset --hard, lost my work!
# Step 1: Check reflog (record of every HEAD movement)
git reflog
# Output:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# d4e5f6g HEAD@{1}: commit: My important work ← this is what you want!
# Step 2: Recover by resetting to that commit
git reset --hard d4e5f6g
# Or cherry-pick just that commit
git cherry-pick d4e5f6g
مشکل ۲: «در حالت Detached HEAD هستم»
# Scenario: Checked out a commit (not a branch), now commits go nowhere
# Symptoms:
# "You are in 'detached HEAD' state..."
# Solution 1: Create a new branch from current position
git switch -c new-branch-name
# Solution 2: If you already made commits, save them first
git branch temp-branch # mark current position
git switch main
git merge temp-branch # bring commits to main
git branch -d temp-branch
# Solution 3: If you don't want those commits
git switch main # return to main, commits are "lost" but in reflog
مشکل ۳: «push رد میشود (non-fast-forward)»
# Error: "Updates were rejected because the remote contains work
# that you do not have locally"
# Cause: Someone else pushed to the same branch
# Solution 1: Pull first, then push
git pull --rebase origin main # rebase your commits on top of remote
git push
# Solution 2: If you want to merge instead of rebase
git pull origin main
# (this creates a merge commit)
git push
# Solution 3: Force push (ONLY for personal branches!)
git push --force-with-lease
مشکل ۴: «فایل در .gitignore است اما هنوز track میشود»
# Cause: .gitignore only affects untracked files.
# If a file was already tracked, adding to .gitignore doesn't remove it.
# Solution: Remove from tracking but keep on disk
git rm --cached path/to/file # for a single file
git rm -r --cached path/to/dir # for a directory
# Then commit
git commit -m "Stop tracking file (now in .gitignore)"
# ===== Remove all files that should be ignored =====
git rm -r --cached .
git add .
git commit -m "Apply .gitignore to all files"
مشکل ۵: «فایلهای حساس را commit کردم!»
# Scenario: Committed .env with API keys!
# Step 1: If recent commit (not pushed yet)
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend --no-edit
# Step 2: If already pushed (BAD! keys are exposed)
# - IMMEDIATELY rotate/revoke the exposed keys
# - Then clean history with BFG Repo-Cleaner or git-filter-repo
# Using git-filter-repo (modern, recommended):
pip install git-filter-repo
git filter-repo --path .env --invert-paths
# Force push rewritten history
git push --force --all
git push --force --tags
# Step 3: Notify collaborators - they need to re-clone
اگر secretها (API keys، passwords، private keys) را به GitHub push کردهاید، قبل از هر چیز آنها را revoke و جدید بسازید. پاک کردن history هیچ فایدهای ندارد اگر کسی قبلاً clone کرده باشد. GitHub حتی اگر history را rewrite کنید، forkها و cacheها همچنان secret را دارند.
مشکل ۶: «repo خیلی بزرگ شده»
# Step 1: Check what's taking space
git count-objects -vH
# Step 2: See largest files in history
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print substr($0,6)}' | \
sort -k2 -n -r | head -20
# Step 3: Run garbage collection
git gc --aggressive --prune=now
# Step 4: For large files in history, use BFG Repo-Cleaner
# Download from: https://rtyley.github.io/bfg-repo-cleaner/
java -jar bfg.jar --strip-blobs-bigger-than 50M my-repo.git
cd my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Step 5: Consider Git LFS for large binary files going forward
git lfs install
git lfs track "*.psd" "*.mp4" "*.zip"
git add .gitattributes
مشکل ۷: «merge را خراب کردم»
# Scenario: Started merge but conflicts are too complex
# Abort the merge (return to pre-merge state)
git merge --abort
# If you already committed the merge:
git reset --hard ORIG_HEAD
# ORIG_HEAD points to where you were before the merge
# For rebase:
git rebase --abort
مشکل ۸: «اسم/ایمیل اشتباه در commits قبلی»
# For future commits: update config
git config --global user.name "Correct Name"
git config --global user.email "correct@example.com"
# For past commits (last N commits), use interactive rebase:
git rebase -i HEAD~5
# Mark commits as "edit", then for each:
git commit --amend --author="Correct Name <correct@example.com>"
git rebase --continue
# For ALL commits in repo (DANGEROUS, rewrites entire history):
git filter-repo --commit-callback '
commit.author_email = b"correct@example.com"
commit.author_name = b"Correct Name"
commit.committer_email = b"correct@example.com"
commit.committer_name = b"Correct Name"
'
# Force push
git push --force --all
Best Practices و Markdown #
این فصل، مجموعهای از بهترین روشها برای کار با Git و GitHub را ارائه میدهد؛ از کیفیت commitها تا طراحی پروژههای متنباز. رعایت این اصول، تفاوت بین یک کاربر آماتور و یک توسعهدهندهی حرفهای است.
قوانین commit messages
- خط اول: حداکثر ۵۰ کاراکتر، در زمان حال، با حرف بزرگ.
- خط دوم: خالی.
- خط سوم به بعد: توضیحات با حداکثر ۷۲ کاراکتر در هر خط.
- از Conventional Commits استفاده کنید:
feat,fix,docs,refactor,test,chore. - به جای «what»، «why» را توضیح دهید: کد خودش میگوید چه شده، پیام باید بگوید چرا.
// ❌ Bad commit messages:
- "fix"
- "update"
- "wip"
- "fixed the bug"
- "asdf"
- "Changes from meeting"
// ✅ Good commit messages:
- "fix(auth): handle expired JWT tokens gracefully"
- "feat(api): add /users endpoint with pagination"
- "docs: add installation instructions for Windows"
- "refactor(auth): extract token validation to middleware"
// ✅ With body:
feat(auth): add OAuth2 login with Google
- Add /api/auth/google endpoint
- Implement OAuth2 callback handler
- Store refresh tokens in database
- Add middleware to validate OAuth sessions
Resolves: #42
Breaking change: removes old /api/login endpoint
قوانین branch
- نامگذاری معنادار:
feature/add-jwt-authبه جایmy-changes. - کوتاهمدت نگه دارید: هرچه طولانیتر، احتمال conflict بیشتر.
- یک feature per branch: مخلوط کردن چند قابلیت، review را سخت میکند.
- قبل از merge، rebase روی main: تاریخچهی تمیز و آخرین تغییرات.
- بعد از merge، حذف کنید: branchهای قدیمی شلوغ میکنند.
قوانین PR
- کوچک نگه دارید: حداکثر ۴۰۰ خط تغییر، review سادهتر.
- توضیح واضح: چرا، چه چیزی، چطور.
- خود-review کنید: قبل از assign به دیگران.
- آزمایشها را بهروز کنید: پوشش کد نباید افت کند.
- صبور باشید: review زمان میبرد؛ فشار نیاورید.
Markdown — راهنمای سریع
# H1 Heading
## H2 Heading
### H3 Heading
**Bold text**
*Italic text*
~~Strikethrough~~
`inline code`
[Link text](https://example.com)

- Unordered list item
- Another item
- Nested item
1. Ordered list
2. Second item
3. Third item
> Blockquote
> More quote
```javascript
// Code block with syntax highlighting
function hello() {
console.log("Hello");
}
```
| Column 1 | Column 2 |
|----------|----------|
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |
---
Horizontal rule above
- [x] Task list (completed)
- [ ] Task list (pending)
<details>
<summary>Click to expand</summary>
Hidden content here
</details>
<!-- HTML comment -->
Footnote[^1]
[^1]: Footnote definition
## GitHub Flavored Markdown (GFM)
- @mentions (notify users)
- #123 (link to issue/PR)
- emoji :smile: :rocket: :+1:
- SHA references (a1b2c3d)
کانالهای ارتباطی پروژههای Open Source
یک پروژهی متنباز خوب، این فایلها را دارد:
| فایل | هدف |
|---|---|
README.md | معرفی، نصب، استفاده |
CONTRIBUTING.md | چطور مشارکت کنیم |
CODE_OF_CONDUCT.md | قوانین رفتار جامعه |
LICENSE | لیسانس قانونی |
CHANGELOG.md | تاریخچهی تغییرات |
SECURITY.md | گزارش آسیبپذیریها |
.github/ISSUE_TEMPLATE/ | تمپلیت issue |
.github/PULL_REQUEST_TEMPLATE.md | تمپلیت PR |
.github/FUNDING.yml | دکمه Sponsor |
لیسانسهای رایج
| لیسانس | اجازه商用 | تغییر لازم | محبوبیت |
|---|---|---|---|
| MIT | ✅ | ✅ (اعتبار) | بسیار زیاد |
| Apache 2.0 | ✅ | ✅ + patent grant | زیاد |
| BSD 3-Clause | ✅ | ✅ | متوسط |
| GPL v3 | ✅ | ❌ باید GPL بماند | متوسط |
| AGPL v3 | ✅ | ❌ برای SaaS هم | کم |
| Unlicense | ✅ | ❌ | کم |
برای اکثر پروژهها MIT بهترین انتخاب است: ساده، مجاز به همهچیز، فقط اعتبار لازم. اگر نگران patent هستید، Apache 2.0. اگر میخواهید derivativeها هم open source بمانند، GPL v3. از سایت choosealicense.com کمک بگیرید.
Cheat Sheet کامل #
این فصل، یک مرجع سریع برای تمام دستورات مهم Git است. برای دسترسی سریع در حین کار طراحی شده است. هر دستور با توضیح کوتاه و مثال آمده است.
Setup & Config
| دستور | توضیح |
|---|---|
git config --global user.name "Name" | تنظیم نام |
git config --global user.email "email" | تنظیم ایمیل |
git config --global init.defaultBranch main | branch پیشفرض |
git config --global core.editor "code --wait" | ادیتور پیشفرض |
git config --global pull.rebase true | pull با rebase |
git config --list | نمایش تنظیمات |
Repository Operations
| دستور | توضیح |
|---|---|
git init | ساخت repo جدید |
git clone <url> | کپی از remote |
git clone --depth 1 <url> | shallow clone |
git clone --recurse-submodules <url> | با submodules |
git status | وضعیت فعلی |
git status -s | وضعیت کوتاه |
Staging & Committing
| دستور | توضیح |
|---|---|
git add <file> | stage یک فایل |
git add . | stage همه |
git add -p | stage تعاملی |
git add -u | stage فقط modifiedها |
git commit -m "msg" | commit |
git commit -am "msg" | stage + commit tracked |
git commit --amend | اصلاح آخرین commit |
git commit --amend --no-edit | اضافه تغییر بدون تغییر پیام |
Branching & Merging
| دستور | توضیح |
|---|---|
git branch | لیست branchها |
git branch <name> | ساخت branch |
git switch <name> | تعویض branch |
git switch -c <name> | ساخت + تعویض |
git branch -d <name> | حذف branch (safe) |
git branch -D <name> | حذف branch (force) |
git merge <branch> | merge |
git merge --no-ff <branch> | merge با merge commit |
git merge --squash <branch> | squash merge |
git merge --abort | لغو merge |
Rebasing
| دستور | توضیح |
|---|---|
git rebase <branch> | rebase روی branch |
git rebase -i HEAD~N | rebase تعاملی |
git rebase --continue | ادامه rebase |
git rebase --abort | لغو rebase |
git rebase --skip | رد کردن commit |
Remote Operations
| دستور | توضیح |
|---|---|
git remote -v | لیست remoteها |
git remote add <name> <url> | افزودن remote |
git remote remove <name> | حذف remote |
git fetch <remote> | دریافت بدون merge |
git pull | fetch + merge |
git pull --rebase | fetch + rebase |
git push | ارسال به remote |
git push -u origin <branch> | push + set upstream |
git push --force-with-lease | force push امن |
git push origin --delete <branch> | حذف remote branch |
Undo & Recovery
| دستور | توضیح |
|---|---|
git reset --soft HEAD~1 | undo commit، keep staged |
git reset --mixed HEAD~1 | undo commit + unstage (default) |
git reset --hard HEAD~1 | undo commit + discard |
git revert <commit> | ساخت commit معکوس |
git restore <file> | بازگرداندن فایل |
git restore --staged <file> | unstage |
git reflog | تاریخچهی HEAD |
git clean -fd | حذف untracked |
Stash & Cherry-pick
| دستور | توضیح |
|---|---|
git stash | ذخیره موقت |
git stash -u | شامل untracked |
git stash list | لیست stashها |
git stash pop | apply + drop |
git stash apply | apply (keep in stack) |
git cherry-pick <sha> | انتقال یک commit |
git cherry-pick --abort | لغو cherry-pick |
Inspection & Diff
| دستور | توضیح |
|---|---|
git log | تاریخچه |
git log --oneline --graph | گراف فشرده |
git log -p | log با diff |
git log --author="name" | فیلتر بر اساس نویسنده |
git diff | تفاوت unstaged |
git diff --staged | تفاوت staged |
git diff HEAD | تفاوت با آخرین commit |
git show <sha> | نمایش یک commit |
git blame <file> | آخرین تغییر هر خط |
git bisect | binary search برای باگ |
Tags & Releases
| دستور | توضیح |
|---|---|
git tag v1.0.0 | tag سبک |
git tag -a v1.0.0 -m "msg" | annotated tag |
git tag -s v1.0.0 -m "msg" | signed tag |
git tag | لیست tags |
git push origin v1.0.0 | push یک tag |
git push --tags | push همه tags |
git tag -d v1.0.0 | حذف tag محلی |
git checkout v1.0.0 | checkout یک tag |
GitHub CLI (gh)
| دستور | توضیح |
|---|---|
gh auth login | ورود به GitHub |
gh repo clone <owner>/<repo> | clone |
gh repo fork <owner>/<repo> | fork |
gh repo create <name> --public | ساخت repo |
gh pr create | ساخت PR |
gh pr list | لیست PRها |
gh pr checkout <num> | checkout PR |
gh pr merge <num> --squash | merge PR |
gh issue create | ساخت issue |
gh issue list | لیست issueها |
gh workflow run | اجرای workflow |
gh run list | لیست runها |
این کتاب از Ctrl+K برای جستجو، Esc برای بستن جستجو، و →/← برای ناوبری بین فصلها پشتیبانی میکند. برای کپی کردن کد، روی دکمهی Copy در بالای هر باکس کد کلیک کنید.
واژهنامه #
اصطلاحات تخصصی Git و GitHub با توضیح کوتاه برای مرجع سریع.
| اصطلاح | توضیح |
|---|---|
| HEAD | اشارهگر به commit فعلی روی branch جاری |
| ORIG_HEAD | موقعیت HEAD قبل از آخرین عملیات بزرگ (merge، reset) |
| MERGE_HEAD | commit ای که در حال merge شدن به HEAD است |
| FETCH_HEAD | نتیجهی آخرین fetch |
| origin | نام پیشفرض remote repository |
| upstream | remote ای که از آن fork کردهاید |
| main | نام پیشفرض branch اصلی (به جای master) |
| master | نام قدیمی branch اصلی (تا ۲۰۲۰) |
| fast-forward | merge ای که فقط pointer را جلو میبرد، بدون merge commit |
| three-way merge | merge با ساختن commit جدید با دو والد |
| detached HEAD | وقتی HEAD به commit خاصی اشاره میکند، نه به branch |
| working directory | فایلهای روی دیسک که شما ویرایش میکنید |
| staging area (index) | منطقهی بین working dir و repository برای آمادهسازی commit |
| blob | object ذخیرهی محتوای فایل در Git |
| tree | object ذخیرهی ساختار دایرکتوری |
| commit | snapshot از staged files + metadata (author، message، parent) |
| ref | نام قابل خواندن که به SHA یک commit اشاره میکند (branch، tag) |
| reflog | تاریخچهی محلی حرکات HEAD (برای recovery) |
| shallow clone | clone فقط با آخرین commit (با --depth) |
| shallow clone | clone فقط با آخرین commit (با --depth) |
| fork | کپی server-side از repo روی حساب شما |
| clone | کپی local از repo روی دیسک شما |
| Pull Request (PR) | درخواست merge از یک branch به دیگر |
| issue | گزارش باگ یا درخواست قابلیت در GitHub |
| milestone | گروهی از issues/PRs برای یک هدف |
| label | برچسب برای دستهبندی issue/PR |
| Actions | پلتفرم CI/CD داخلی GitHub |
| workflow | یک فایل YAML در .github/workflows/ |
| runner | ماشینی که workflow روی آن اجرا میشود |
| Pages | هاستینگ استاتیک رایگان GitHub |
| LFS | Large File Storage، برای ذخیرهی فایلهای بزرگ |
| submodule | repo داخل repo (reference به commit خاص) |
| worktree | working directory اضافی برای همان repo |
| squash | ترکیب چند commit در یکی |
| cherry-pick | اعمال یک commit خاص از branch دیگر |
| bisect | binary search برای پیدا کردن commit مخرب |
| blame | مشاهدهی اینکه کدام commit هر خط را تغییر داده |
| hook | اسکریپتی که قبل/بعد از یک رویداد Git اجرا میشود |
| Conventional Commits | استاندارد پیام commit: type(scope): description |
| GPG signing | امضای دیجیتال commits برای تأیید هویت |
| PAT | Personal Access Token، جایگزین پسورد HTTPS |
| SSH key | کلید عمومی/خصوصی برای auth بدون پسورد |
| Git Flow | استراتژی branching با ۶ نوع branch |
| GitHub Flow | استراتژی ساده با main + feature |
| Trunk-Based | استراتژی با branchهای کوتاهمدت |