~/icsd.ir — bash
SYSTEM_ONLINE

Rebase، Stash و تاریخچه تمیز

دو ابزار قدرتمند برای حرفه‌ای‌ها: rebase برای تاریخچه تمیز و خطی، و stash برای ذخیره موقت کارهای ناتمام. در این فصل interactive rebase، squash، fixup و تفاوت merge با rebase را می‌بینیم.

دو ابزار قدرتمند برای حرفه‌ای‌ها: rebase برای تاریخچه تمیز و خطی، و stash برای ذخیره موقت کارهای ناتمام. در این فصل interactive rebase، squash، fixup و تفاوت merge با rebase را می‌بینیم.

Rebase در مقابل Merge

هر دو همان هدف را دارند: ادغام تغییرات. اما روش متفاوت:

قبل:
main:    A ── B ── C ←── main
              
               D ── E ←── feature

بعد از merge feature به main:
main:    A ── B ── C ── M ←── main
                      ╱
               D ── E

بعد از rebase feature روی main:
main:    A ── B ── C ── D'─ E' ←── feature (حالا)
                                  (بعد main:= feature → fast-forward)
ویژگی Merge Rebase
تاریخچه غیرخطی، با merge commit خطی
commit جدید merge commit ساخته می‌شود commit‌ها بازنویسی می‌شوند
تاریخچه محفوظ بله خیر (hash جدید)
مناسب branch‌های public/shared feature branch محلی
conflict یک‌بار در merge ممکن است در هر commit

Rebase ساده

# روی feature branch هستید، می‌خواهید با main همگام شوید
git switch feature
git fetch origin
git rebase origin/main

# Git هر commit شما را یکی یکی روی main جدید پخش می‌کند

# اگر conflict شد:
# 1. حل کنید
nano file.py
git add file.py

# 2. ادامه
git rebase --continue

# لغو
git rebase --abort

# skip یک commit
git rebase --skip

چرا rebase؟

قبل از rebase:
main:    A ── B ── C ── F ── G ←── main
              
               D ── E ←── feature

بعد از rebase:
main:    A ── B ── C ── F ── G ←── main
                              
                               D'── E' ←── feature

# حالا feature روی آخرین main است
# می‌توانید fast-forward merge کنید
git switch main
git merge feature                 # fast-forward، بدون merge commit
قانون طلایی Rebase: هرگز branch‌های public (که دیگران clone کرده‌اند) را rebase نکنید! چون hash‌ها عوض می‌شوند و کار همکاران به هم می‌ریزد.

Interactive Rebase

قدرتمندترین قابلیت Git: ویرایش تاریخچه با جزئیات.

# 5 commit آخر را ویرایش کن
git rebase -i HEAD~5

# یا از یک commit خاص
git rebase -i a3f4b2c

# editor باز می‌شود:
pick a1b2c3d Add login form
pick d4e5f6g Fix typo
pick h7i8j9k Add validation
pick l0m1n2o WIP
pick p3q4r5s Add tests

# Commands:
# p, pick   = استفاده از این commit همان‌طور که هست
# r, reword = تغییر پیام
# e, edit   = توقف برای ویرایش
# s, squash = ادغام در commit قبلی + ویرایش پیام
# f, fixup  = squash بدون ویرایش پیام (پیام قبلی نگه‌داشته می‌شود)
# d, drop   = حذف کامل commit
# x, exec   = اجرای command shell

مثال ۱: Squash چند commit

قبل از:
pick a1b2c3d Add login form
pick d4e5f6g Fix typo
pick h7i8j9k Add validation

تغییر دهید به:
pick a1b2c3d Add login form
fixup d4e5f6g Fix typo                 # ادغام در قبلی
fixup h7i8j9k Add validation           # ادغام در قبلی

# ذخیره و خروج
# تاریخچه نهایی: یک commit
# "Add login form" (شامل همه تغییرات)

مثال ۲: تغییر پیام

pick a1b2c3d Add login form
reword d4e5f6g Fix typo               ← این
pick h7i8j9k Add validation

# پنجره دوم باز می‌شود برای پیام جدید

مثال ۳: حذف commit

pick a1b2c3d Add login form
drop d4e5f6g Add debug logs           ← حذف
pick h7i8j9k Add validation

مثال ۴: تغییر ترتیب

کافی است خطوط را جابه‌جا کنید:

pick h7i8j9k Add validation           # اول
pick a1b2c3d Add login form           # دوم
pick d4e5f6g Fix typo                 # سوم

مثال ۵: ویرایش یک commit

pick a1b2c3d Add login form
edit d4e5f6g Fix typo                ← این
pick h7i8j9k Add validation

# Git در آن commit متوقف می‌شود
# تغییرات بدهید
nano file.py
git add file.py
git commit --amend
git rebase --continue

–autosquash

روش هوشمند برای squash هنگام commit:

# commit اصلی
git commit -m "Add login form"
# a1b2c3d

# بعداً متوجه باگ می‌شوید
git commit --fixup a1b2c3d
# پیام خودکار: "fixup! Add login form"

# یا برای squash با ویرایش پیام
git commit --squash a1b2c3d

# بعد از همه fixup‌ها، rebase خودکار:
git rebase -i --autosquash HEAD~5
# Git خودکار fixup‌ها را زیر commit اصلی می‌چیند

تنظیم همیشگی

git config --global rebase.autosquash true

–onto

برای انتقال یک رشته از commit‌ها به مکان دیگر:

قبل:
main:    A ── B ── C
              
               D ── E ──── F ── G ←── feature

# می‌خواهیم F و G را روی main برداریم (D و E را نه)
git rebase --onto main E feature

بعد:
main:    A ── B ── C
                    
                     F'── G' ←── feature

git stash – ذخیره موقت

سناریو: در میانه کار هستید، ناگهان باید روی main bug fix بزنید. اما کارتان آماده commit نیست.

دستورات اصلی stash

# ذخیره موقت همه تغییرات
git stash

# با پیام
git stash push -m "WIP: working on user profile"

# لیست stash‌ها
git stash list
# stash@{0}: WIP on feature: a3f4b2c WIP working on user profile
# stash@{1}: WIP on main: b2c3d4e Quick fix attempt

# بازگردانی آخرین stash (و حذف از لیست)
git stash pop

# بازگردانی بدون حذف
git stash apply

# بازگردانی stash خاص
git stash apply stash@{2}
git stash pop stash@{2}

# نمایش محتوای stash
git stash show
git stash show -p stash@{0}        # با diff کامل

# حذف stash
git stash drop stash@{1}

# پاک کردن همه stash‌ها
git stash clear

چه چیزهایی stash می‌شود؟

# پیش‌فرض: tracked فایل‌های modified و staged
git stash

# همراه untracked
git stash -u
git stash --include-untracked

# همراه ignored هم
git stash -a
git stash --all

# فقط فایل‌های خاص (Git 2.13+)
git stash push file1.py file2.py

# فقط staged (Git 2.35+)
git stash push --staged

# با interactive (مثل git add -p)
git stash push -p

سناریو کامل

# در حال کار روی feature
# فایل‌های modified زیادی دارید

# bug fix فوری در main لازم است
git stash push -m "WIP: feature work"

# کار روی main
git switch main
git pull
# bug fix...
git commit -m "fix: critical bug"
git push

# بازگشت
git switch feature
git stash pop
# تغییرات شما برگشتند

ساخت branch از stash

# سناریو: stash قدیمی دارید که می‌خواهید روی branch جدید کار کنید
git stash branch new-feature stash@{2}
# branch جدید از زمان stash ساخته می‌شود + stash apply می‌شود

amend vs stash vs rebase

کار دستور
تغییر آخرین commit git commit --amend
تغییر چند commit git rebase -i HEAD~N
ذخیره موقت بدون commit git stash
ادغام چند commit به یک rebase + squash/fixup
همگام با main بدون merge commit git rebase main

جریان کاری حرفه‌ای

# 1. شروع feature
git switch -c feature/payment

# 2. commit‌های زیاد در حال کار
git commit -m "WIP: payment form structure"
git commit -m "fix typo"
git commit -m "add validation"
git commit -m "fix validation bug"
git commit -m "WIP: backend integration"
git commit -m "add tests"
git commit -m "fix test"

# 3. آماده برای PR، تاریخچه را تمیز کن
git rebase -i HEAD~7

# در editor:
pick    a1 WIP: payment form structure
fixup   a2 fix typo
fixup   a3 add validation
fixup   a4 fix validation bug
pick    a5 WIP: backend integration
fixup   a6 add tests
fixup   a7 fix test

# 4. تغییر پیام‌ها
reword  a1 feat: add payment form with validation
reword  a5 feat: integrate payment backend with tests

# 5. push
git push -u origin feature/payment

# 6. اگر main تغییر کرد قبل از merge:
git fetch origin
git rebase origin/main
git push --force-with-lease           # چون rebase تاریخچه را عوض کرد

سیاست تیمی

هر تیم سیاست خودش را دارد. سه گزینه رایج:

۱. همیشه Merge (تاریخچه کامل)

  • تاریخچه کامل اما شلوغ
  • هیچ‌وقت rebase نکنید
  • مناسب: تیم‌هایی که می‌خواهند تاریخچه دقیق

۲. Rebase + Merge (پیشنهاد رایج)

  • محلی rebase کنید
  • هنگام merge به main، با merge commit
  • تاریخچه تمیز اما با group‌بندی PR‌ها

۳. Rebase + Fast-Forward (تاریخچه کاملاً خطی)

  • قبل از merge همیشه rebase
  • merge فقط fast-forward
  • تاریخچه مستقیم بدون شاخه
  • یا squash merge در GitHub

git pull –rebase

به‌جای merge هنگام pull، رفتار rebase:

# pull معمولی → merge commit می‌سازد اگر divergent باشد
git pull

# pull با rebase → تاریخچه خطی
git pull --rebase

# تنظیم default
git config --global pull.rebase true

# برای یک repo خاص
git config pull.rebase true

reset vs rebase vs revert

ابزار کار مناسب برای
reset عقب بردن HEAD local commits
rebase بازنویسی commit‌ها local feature branch
revert commit جدید برای undo commit‌های push شده

نکات کاربردی

۱. تنظیم editor برای rebase

git config --global core.editor "code --wait"
# حالا rebase -i در VS Code باز می‌شود

�2. abort هر rebase که گیر کرده

git rebase --abort
# اگر کاملاً گیر کرده:
git reflog
git reset --hard HEAD@{N}      # قبل از rebase

۳. حفاظت از branch

# هنگام rebase دیگر developer‌ها مشکل پیدا می‌کنند
# main را protected کنید (در فصل ۵)
# تنها از طریق PR + squash merge

۴. stash با دیگر دستورات

# تعویض branch با stash خودکار (Git 2.5+)
git switch --autostash other-branch

# pull با autostash
git pull --autostash

# تنظیم همیشه
git config --global rebase.autostash true

مثال کامل: PR تمیز

# شروع
git switch main
git pull
git switch -c feature/dark-mode

# توسعه با commit‌های زیاد
echo "..." > dark.css
git commit -am "start dark mode"
git commit -am "fix"
git commit -am "more"
git commit -am "WIP"
git commit -am "tests"

# تاریخچه شلوغ - تمیز کن
git rebase -i HEAD~5
# همه را به دو commit معنادار تبدیل کن:
# - "feat: add dark mode CSS and toggle"
# - "test: add dark mode tests"

# همگام با main
git fetch origin
git rebase origin/main

# اگر هیچ تغییر برخوردی نبود، حالا feature branch روی آخرین main است

# push
git push -u origin feature/dark-mode

# اگر قبلاً push کرده بودید:
git push --force-with-lease

# PR باز کنید
# بعد از merge، در GitHub باید "squash and merge" زده شده باشد
# یا اگر تاریخچه تمیز کرده‌اید، "rebase and merge"

بهترین شیوه‌ها

  • هرگز branch‌های public را rebase نکنید
  • قبل از PR، تاریخچه را با interactive rebase تمیز کنید
  • fixup + autosquash برای جریان روزمره
  • stash برای کارهای موقت، نه کارهای طولانی‌مدت
  • force-push با –force-with-lease (نه –force)
  • autostash برای راحت‌تر کردن pull/rebase
  • سیاست تیمی واضح در CONTRIBUTING.md
  • commit‌های semantic (Conventional Commits) قبل از rebase

جمع‌بندی

  • Rebase = بازنویسی تاریخچه برای خطی شدن
  • Interactive rebase: pick، squash، fixup، reword، edit، drop
  • autosquash + commit –fixup برای حرفه‌ای‌ها
  • Stash برای ذخیره موقت کار
  • قانون طلایی: rebase فقط commit‌های local
  • force-push با –force-with-lease
  • سیاست تیمی روشن مهم‌تر از انتخاب صحیح است

در فصل بعد، Tag‌ها و Release‌ها در GitHub را یاد می‌گیریم.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.