بازگشت به وبلاگ

Git cherry-pick: چند commit، شاخه‌های مختلف و حل تعارض

۲۱ شهریور ۱۴۰۵

TL;DR: git cherry-pick <sha> یک commit را از هر شاخه‌ای روی شاخه فعلی شما کپی می‌کند — همان patch، SHA جدید، بدون جابه‌جایی تاریخچه. برای چند commit، فهرست SHA ها یا یک بازه (git cherry-pick A..B)؛ دقت کنید بازه A را کنار می‌گذارد، پس برای شمول آن بنویسید A^..B. اگر روی تعارض ایست، رفعش کنید، git add، بعد git cherry-pick --continue. cherry-pick برای جابه‌جا کردن یک fix مشخص است — وقتی همه‌چیز شاخه مقابل را می‌خواهید، merge یا rebase به‌کار ببرید.

git cherry-pick دقیقاً چه می‌کند؟

cherry-pick یک commit موجود را برمی‌دارد و diff آن را به‌عنوان یک commit جدید روی شاخه فعلی اعمال می‌کند. commit اصلی سر جایش می‌ماند؛ نسخه کپی SHA تازه می‌گیرد. Git هیچ چیز را «جابه‌جا» نمی‌کند — کسانی که بعداً تعجب می‌کنند چرا commit هنوز روی شاخه قدیمی دیده می‌شود، دقیقاً همین را می‌بینند.

همین مدل کپی-به‌جای-جابه‌جایی تعیین می‌کند cherry-pick کِی ابزار درست است:

  • یک fix واحد از شاخه feature لازم دارید همین حالا روی main بنشیند، بدون merge بقیه.
  • یک hotfix که روی شاخه اشتباه commit شده باید روی شاخه درست فرود بیاید.
  • یک patch باید روی شاخه release تکرار شود که هرگز از main merge نمی‌کند.

مهارت مکملش این است که بدانید چطور یک commit اشتباه را پس بگیرید — سازوکارش در لغو آخرین commit در Git: تغییرات بماند پوشش داده شده.

چطور یک commit را از شاخه دیگری cherry-pick کنم؟

SHA را پیدا کنید، به شاخه مقصد بروید، بردارید:

# 1. Locate the commit on the source branch
git log feature/payment-fix --oneline -5

# 2. Switch to the branch that should receive it
git switch main

# 3. Copy it over
git cherry-pick 1a2b3c4

دو راحتی که دانستنش می‌ارزد:

  • git cherry-pick <branch> commit نوکِ آن شاخه را برمی‌دارد — کاربردی، ولی آسان اتفاقی می‌شود با تصویر ذهنی کهنه از اینکه «نوک» الان کجاست.
  • بعد از برداشتن، git log -1 --stat تأیید می‌کند چه فرود آمده. یک ثانیه خواندن، یک revert کمتر.

commit ها روی feature/payment-fix می‌مانند؛ هر وقت خواستید آن شاخه را حذف کنید — نسخه برداشته‌شده روی main SHA خودش را دارد و به نسخه قدیمی هیچ وابستگی ای ندارد.

چطور چند commit را cherry-pick کنم؟

سه شکل، به ترتیب کاربرد:

# 1. Explicit list — picked in the order you list them
git cherry-pick 1a2b3c4 5d6e7f8

# 2. Range — everything after A up to and including B
git cherry-pick A..B

# 3. Range including A
git cherry-pick A^..B

تمایز A..B با A^..B همان سورپرایز کلاسیک است: A..B شامل A نیست. اگر بازه را از git log تجسم کنید و oldest..newest بردارید، بی‌سروصدا قدیمی‌ترین commit را جا می‌اندازید. وقتی هدف «چند commit قدیمی، به ترتیب» است، بنویسید oldest^..newest و off-by-one محو می‌شود.

برای تاشدن چند برداشت در یک commit به‌جای سه‌تا، بدون commit مرحله‌بندی کنید با -n / --no-commit، بعد یک بار commit بزنید:

git cherry-pick -n 1a2b3c4 5d6e7f8
git commit -m "Backport: payment retry fixes"

چرا git cherry-pick کار نمی‌کند؟

چهار علت واقعی، به ترتیب بسامد:

1. یک تعارض برداشت را متوقف کرده. Git patch را اعمال می‌کند، به خطی می‌رسد که روی هر دو شاخه تغییر کرده، و وسط دنباله می‌ایستد:

# resolve the files, then:
git add <resolved-files>
git cherry-pick --continue   # or --abort to return to the pre-pick state

--continue اختیاری نیست و ضمنی هم نمی‌شود — تا اجرایش نکنید، داخل یک دنباله cherry-pick متوقف‌شده هستید و git status مدام همان را می‌گوید.

2. برداشت خالی است («The previous cherry-pick is now empty»). تغییر از قبل روی این شاخه وجود دارد، اغلب از یک cherry-pick قبلی یا یک merge فشرده. با git cherry-pick --skip ردش کنید، یا اگر واقعاً نشانگر را لازم دارید با --allow-empty یک commit خالی بسازید.

3. آن commit یک merge commit است. merge دو والد دارد، پس «این diff را اعمال کن» مبهم است — git حدس نمی‌زند، رد می‌کند. بگویید نسبت به کدام والد diff می‌گیرید:

git cherry-pick -m 1 <merge-sha>   # parent 1 = the branch you merged *into*

4. working tree اشتباه یا HEAD جداشده. برداشت هر جا فرود می‌آید که HEAD اشاره می‌کند. قبل از برداشتن git branch --show-current؛ اگر چیزی چاپ نکرد، در detached HEAD هستید و commit موقع جابه‌جایی یتیم می‌شود.

cherry-pick در مقابل merge در مقابل rebase: کدام کجا؟

دستورچه چیزی روی مقصد می‌نشیندتاریخچهکِی استفاده کنید
git cherry-pick <sha>فقط commit های نام‌بردهکپی، SHA های جدیدیک fix مشخص باید همین حالا جابه‌جا شود
git merge <branch>همه‌چیز روی شاخهmerge commit یا fast-forwardکل شاخه را می‌خواهید، واگرایی دیده شود
git rebase <base>همه commit های شاخه، بازپخش‌شدهخطی، SHA های بازنویسی‌شدهشاخه را یک دنباله خطی تمیز می‌خواهید
git revert <sha>وارونه یک commitیک commit باطل‌کننده اضافه می‌کندیک commit فرودآمده باید روی تاریخچه مشترک باطل شود

قاعده یک‌خطی: cherry-pick یک انتخاب را جابه‌جا می‌کند؛ merge و rebase همه‌چیز را. سراغ cherry-pick رفتن برای «sync» شدن با یک شاخه علامت این است که در واقع merge می‌خواهید — و اگر بحث main فورک شما در مقابل upstream باشد، روال کامل در همگام‌سازی fork با upstream در Git: 3 روش امن است.

یک عادت که باید رها شود: cherry-pick کردن بلندمدتِ همان commit به چند شاخه. هر fix آینده روی شاخه مبدا یک برداشت دیگر می‌خواهد، و بالاخره شاخه‌ها وا می‌روند. Backport به شاخه‌های release الگوی نرمالی است؛ یک جهان موازی دائمی نه.

گردش‌کار cherry-pick، فشرده

git log <source-branch> --oneline -5   # find the SHA(s)
git switch <target-branch>             # land in the right place
git cherry-pick A^..B                  # range, list, or single SHA
# on conflict: resolve → git add → git cherry-pick --continue
git log -1 --stat                      # confirm what landed

پیدا کن، برو، بردار، راستی‌آزمایی کن. این دستور آوازه خطرناک‌بودنی دارد که لایقش نیست — یا diff اعمال می‌شود یا می‌ایستد و می‌گوید چرا. تنها اشتباه واقعاً مخرب، برداشتن روی شاخه اشتباه است، و git log -1 قبل از push باعث می‌شود آن به‌سختی از چشم بیفتد.

FAQ

git cherry-pick چه می‌کند؟

تفاوت یک commit را به‌عنوان یک commit جدید روی شاخه فعلی شما کپی می‌کند — همان تغییر، SHA جدید.

چطور چند commit را cherry-pick کنم؟

git cherry-pick A^..B یک بازه را به ترتیب تکرار می‌کند. تعارض اجرا را متوقف می‌کند؛ رفع کنید، بعد cherry-pick --continue.

آیا cherry-pick بهتر از merge است؟

برای بردن یک fix به شاخه release، بله. cherry-pick های تکراریِ کل شاخه بدهی merge است — merge یا rebase به‌کار ببرید.

— mrsaynothing

— mrsaynothing

یادداشت‌های میدانی درباره AI، لینوکس و self-hosting.

این نوشته را در dev.to بحث کنید dev.to ↗

آموزش بعدی با ایمیل

هر نوشته یک ایمیل. درستش کن، برو سراغ بعدی.

self-hosted · بدون واسطه‌های ثالث · لغو اشتراک با یک کلیک

این چیست؟

cron در مقابل timer های systemd: کدام را به‌کار ببرید؟

این نوشته‌ها را می‌پسندید؟ این همان کاری است که برای زندگی از آن درمی‌آورم. استخدامم کنید