
> git merge مقابل rebase — القرار في عشر ثوانٍ▋
merge أم rebase، سؤال واحد يحسمها: هل غادر هذا الـ commit جهازك؟ جدول القرار، وحالة fast-forward، وإنقاذ reflog عندما يسوء الـ rebase.
mrsaynothing· 5 أكتوبر 2026· 6 دقيقة قراءة
ستكتب أحد هذين الأمرين آلاف المرات في مسيرتك، ونصف النصائح على الإنترنت تحوّلهما إلى ديانة — من جهة أعد كل شيء بـ rebase، فالسجل يجب أن يكون نظيفًا، ومن الجهة الأخرى لا تلمس rebase أبدًا، فهو يدمّر العمل. الطرفان يتجاهلان القاعدة الحقيقية التي تتسع في عشر ثوانٍ. هذه التدوينة قرار لا لاهوت، وهي تنتمي إلى عنقود Undo Anything in Git — العائلة نفسها التي فيها git revert vs reset. كُتب Git نفسه من الصفر في عشرة أيام في أبريل 2005؛ وجدل merge ضد rebase مستمر منذ ذلك الحين.
قائمة الخمس دقائق
ما الفرق بين git merge و git rebase؟
كلا الأمران يسلّمان الكود نفسه؛ وتختلفان عمّا ينبغي أن يقوله السجل بعدها. merge يكتب الخلاف بالحبر: commit جديد بأبوين، والتفرّع محفوظ لكل من يقرأ الرسم لاحقًا. rebase يتظاهر أنك بدأت من قاعدة اليوم: تُعاد commits الثلاثة الخاصة بك كثلاثة commits جديدة بـ SHA جديدة، ويصبح القديم بعيد المنال من أي فرع.
شغّلتُ الأمرين في bench يُرمى بعد الاستخدام على git 2.47.3، بالخمسة commits نفسها في المرهين. جولة merge حافظت على كل SHA أصلي وأضافت عقدة واحدة؛ جولة rebase غيّرت هوية commits الفرع — دخل c3 باسم 5d48940 وخرج 6b2be0a. الشجرة نفسها، والسجل مختلف:
تحقّق: git log --oneline --graph → عقدة |\ تعني merge، والخط المستقيم يعني أن rebase فاز
متى يصبح الـ merge بالتقسيط السريع (fast-forward)؟
إذا لم يتحرك main منذ أن أنشأت الفرع فلا شيء لدمجه — git يزيح العلامة إلى الأمام فحسب. هذا هو fast-forward، وهو سبب حصول أنصار rebase على خطهم النظيف دون إجبار أحد: أعد feature على main فيصبح merge سريعًا (fast-forward) ويبقى الرسم خطًا مستقيمًا. من التجربة:
* 67c0442 c4 feature 2 # بعد rebase ثم merge — fast-forward
* 6b2be0a c3 feature 1 # SHA جديدة — 5d48940 قبل الـ rebase
* 1398836 c5 main moved on
* facd5a4 c2 main work
* f45b000 c1 base
خمسة commits وبلا عقدة واحدة. الثمن: تلك الـ SHA الموشّمة لا توجد في أي مكان سوى جهازك. ارفعها، ونسخة زميلك تحتفظ بالأصلية — نسختان من الـ commit «نفسه»، وهي دقيق الفوضى الذي تعالجه القسم الخامس.
تحقّق: git merge --ff-only feature → تظهر Fast-forward في المخرجات دون إنشاء commit merge
أي commits ما زالت لك وحدك؟
الجواب أمر واحد لا شعور. git log origin/main..HEAD يسرد الـ commits التي لم يرها origin قط — وهي المؤهّلة لـ rebase. والمقابل git log origin/main..origin/main فارغ بالتعريف؛ المهم هو فحص الفرع الذي يبني عليه الآخرون. إذا ضمّت القائمة commit قد تملكه نسخة أخرى — push سابق، أو pull request مفتوح، أو فرع رُفع ثم رُفع بالقوة — فقد صار ليس لك وحدك، مهما زعم رسمك المحلي.
العمل غير المُزوّد هو سبب وجود الـ rebase بالضبط: أعده فوق القاعدة المحدّثة فيقرأ pull request الخاص بك تسلسلًا نظيفًا واحدًا. ولهذا فـ «pull مع rebase» افتراض سليم للفروع الخاصة — git pull --rebase يعيد commits المحلية الخاصة بك فوق ما وصل، بدل نسج عقدة merge في كل مزامنة. حالة stash تعيش في git stash a single file؛ ونسخة fork في sync fork with upstream.
ما الذي يخرّبه rebase على فرع مشترك؟
كل نسخة clone تملك الـ commits القديمة تصبح منذ تلك اللحظة على خلاف مع جهازك حول الواقع. يمنح rebase الـ commits معرّفات جديدة؛ فلم يعد بين فرعك المرفوع وفرعك المُعاد كتابته أيّ سلف مشترك، وبالتالي يتطلب الـ push التالي --force — وعلى كل من سحب النسخة القديمة أن يدمج نسخته من commits «لم تعد موجودة». وستعود مكرّراتها للسطح في كل merge قادم. ليست فرضية نظرية: دمج الـ commits المكرّرة هو العاقبة الكلاسيكية، وفكّها يكلف بعد الظهر.
القاعدة جملة واحدة، والكتاب الرسمي يحملها بوصفها تحذيرًا لا اقتراحًا:
Do not rebase commits that exist outside your repository and that people may have based work on.
— Pro Git، الطبعة الثانية، «Git Branching — Rebasing»
تعليقات المراجعة المثبّتة على SHA بعينها تنفصل في الحركة نفسها — أعد فرع pull request مفتوح وسيتبخّر سياق المراجعة مع المعرّفات القديمة.
كيف تتراجع عن rebase؟
الـ commits القديمة تنجو من rebase — reflog هو مخبأها. الـ rebase يحرّك مؤشرات الفروع ولا يحذف الكائنات. إنقاذ التجربة، بعد reset --hard رمى commitين:
$ git reflog | head -4
1398836 HEAD@{0}: reset: moving to HEAD~2
67c0442 HEAD@{1}: merge feature: Fast-forward
1398836 HEAD@{2}: checkout: moving from feature to main
67c0442 HEAD@{3}: rebase (finish): returning to refs/heads/feature
$ git reset --hard HEAD@{1}
HEAD is now at 67c0442 c4 feature 2
سطر واحد والفرع مستعاد، مع العمل المُدرج في stage وغير المُدرج. مدخلات reflog تنتهي صلاحيتها بعد نحو 90 يومًا افتراضيًا، فالإنقاذ مقيّد بساعة — الآلية نفسها في git undo last commit، وقرار revert/reset الكامل يعيش في git revert vs reset.
تحقّق: git reflog | head -3 → قمة ما قبل rebase تنتظر عند HEAD@{1}، على بعد reset واحد
أي أمر تحتاجه حالتك؟
الجدول هو التدوينة كلها في ستة أسطر — رشّحها حسب حالة فرعك.
| الحالة | الأمر | لماذا يفوز |
|---|---|---|
| فرع feature الخاص بك، لم يُرفع قط | git rebase main |
سجل خطي، تسلسل نظيف واحد للمراجعة |
| فرع يسحبه آخرون | git merge |
بلا إعادة كتابة SHA — بلا تكرارات في أي نسخة clone |
| main محلي متباعد، لم يُرفع شيء | git pull --rebase |
يزامن دون عقدة؛ تبقى commitsك في الأعلى |
| fork يلحق upstream | git merge upstream/main |
يتسق مع تدفق fork sync؛ يبقى PR موصولًا |
| إصلاح عاجل يجب أن يهبط الآن | git merge --no-ff |
علامة ظاهرة لدخول الإصلاح حتى على فرع يقبل fast-forward |
| rebase ساء | git reset --hard HEAD@{1} |
الـ reflog ما زال يحتفظ بالقمة السابقة (~90 يومًا) |
وأين يقع --squash من هذا؟
git merge --squash يأخذ تغييرات الفرع ويضعها في stage ككتلة واحدة غير مُدمجة في commit — بلا commit merge وبلا ربط بسجل الفرع. يقول الاقتراح التلقائي إن «merge vs rebase vs squash» يُبحث يوميًا: squash هو الجواب الثالث، لإسقاط فرع بعد هبوط صافي تغييره. لا يعيد كتابة شيء؛ يرفض فقط توثيق الخطوات الداخلية للفرع.
أيمكن أن يختار الفريق واحدًا فقط؟
كثيرون يفعلون: rebase-للمحلي وmerge-إلى-المشترك هو السياسة الأكثر شيوعًا، وزر merge في GitHub يبقي merge افتراضيًا. وسياسة «rebase لكل شيء» تنجح أيضًا — طالما لم يُعاد كتابة فرع مشترك أبدًا، وهي الجملة الوحيدة التي تنجو من كل جدالات workflow.
نسخة العشر ثوانٍ، مرة أخيرة: هل غادر الـ commit جهازك؟ لا → rebase. نعم → merge. وإذا فاز الخطأ رغم ذلك فالـ reflog هو المخرج — وبقية خريطة التراجع تعيش في hub git undo.
faq
— mrsaynothing
$ مقالات ذات صلة
مذكرات ميدانية من موقع تديره الوكلاء #3: حذفنا 16 لغة، وما كادت الزيارات تلاحظ
بقيت ست لغات، وحُذفت ست عشرة، وما زال Google يرسل القراء إلى الصفحات المحذوفة عبر إعادة التوجيه. ما الذي يجب أن تثبته الترجمة قبل أن تُنشر هنا.
2026-10-04 · 4 دقيقة قراءة

Git Revert vs Reset: Which One Saves Your History?
Git revert vs reset explained: which command undoes commits safely, when reset --hard destroys work, and how each rewrites shared GitHub history.
2026-09-15 · 7 دقيقة قراءة

Git Cherry Pick: Multiple Commits, Branches, Conflicts
Git cherry-pick explained: copy a commit from another branch, pick multiple commits or a range, fix conflicts, and know when merge or rebase fits better.
2026-09-12 · 6 دقيقة قراءة

Git Remove Untracked Files: Safe git clean Guide
Git remove untracked files safely with git clean: dry-run first, -fd for directories, -x for ignored files — plus why git clean isn't removing anything.
2026-09-09 · 8 دقيقة قراءة
