اذهب إلى المحتوى

إعادة تأسيس التفريعات (Rebasing) في Git


محمد أحمد العيل

توجد طريقتان أساسيتان في Git لتضمين التعديلات من تفريع إلى آخر: merge و rebase. يشرح هذا المقال ماهية إعادة تأسيس التفريعات Rebasing، كيفيتها، الفائدة منها والحالات التي يجدر بك عدم استخدام إعادة التأسيس فيها.

git-rebasing.png.4ea0e0373d5ea9e5b6127e6

مبادئ إعادة التأسيس

يمكن بالعودة إلى المثال في درس المبادئ الأساسية لدمج التفريعات رؤية تمايز أعمالك بإضافة إيداعات إلى كلٍّ من التفريعين.

01_rebasing.thumb.png.d8d3d809f54b40b530

استخدام أمر git merge هو أسهل طريقة، مثل ما رأينا سابقا، لتضمين تفريع في آخر. ينفّذ الأمرُ الدمجَ الثلاثي بين آخر لقطتين في كلّ تفريع (C3 وC4) واللقطة المشتركة الأقرب (C2) منشئا بذلك لقطة (وإيداعا) جديدة.

02_rebasing.thumb.png.0f84f6373bdf5073c5

إلا أنه توجد طريقة أخرى: يمكنك أخذ الترقيع Patch الذي أضافه الإيداع C4 ثم تطبيقه على الإيداع C3. تسمى هذه العملية في Git بإعادة التأسيس. يتيح الأمر rebase أخذ جميع الإيداعات التي أضيفت إلى تفريع وتكرارها على تفريع آخر.

يكون تطبيق هذا المبدأ على المثال في الصورة بتنفيذ الأمر على النحو التالي:

git checkout experiment
git rebase master
  First, rewinding head to replay your work on top of it...
  Applying: added staged command

يعمل الأمر بالرجوع إلى الإيداع السابق المشترك بين التفريعين (التفريع الذي تعمل عليه الآن، والتفريع الذي تريد إعادة التأسيس عليه)، الحصول على التغييرات التي أدرجها كل إيداع على التفريع الحالي وتخزينها في ملف ظرفي، ضبط التفريع الحالي على آخر إيداع في التفريع الذي نريد إعادة التأسيس عليه ثم في الأخير تطبيق التعديلات بالترتيب.

03_rebasing.thumb.png.0407ae1e987aa1339c

نستطيع بالوصول إلى هذه النقطة العودة إلى التفريع master وتنفيذ دمج بتقدم سريع:

git checkout master
git merge experiment

04_rebasing.thumb.png.d6a80a7f679ed65000

يشير الإيداع 'C4 إلى نفس اللقطة التي يشير إليها الإيداع C5 في مثال الدمج الذي تحدثنا عنه في البداية. لا يوجد فرق في المنتوج النهائي بين الاثنين؛ إلا أن إعادة التأسيس تجعل سجل التعديلات أكثر وضوحا. يبدو سجل الإيداعات في تفريع أعيد تأسيسه خطيًّا: تظهر الأعمال كما لو أنها حدثت بالتتالي، حتى لو كان الواقع أنها كانت بالتوازي.

يُلجَأ عادة لتأسيس التفريعات للتأكد من أن الإيداعات تُطبَّق دون مشاكل على تفريع بعيد؛ مثلا عند المشاركة في مشروع لا تتولى صيانته. تعمل في هذه الحالة على تفريع ثم تعيد تأسيس التفريع على origin/master عندما تكون جاهزا لإرسال الترقيع إلى المشروع الرئيس. لن يحتاج مسؤول المشروع في هذه الحالة إلا إلى دمج سريع.

انتبه إلى أن اللقطة التي يشير إليها التفريع هي نفسها، سواء كانت الأخيرة من إيداعات بعد إعادة تأسيس التفريع أو إيداع دمج. وجه الاختلاف هو فقط سجل التغييرات. تكرّر إعادة التأسيس التغييرات من خط عمل على آخر بالترتيب الذي حدثت به في حين يأخذ الدمج نقطتي النهاية ويدمجهما معا.

إعادات تأسيس متقدمة

يتيح Git إمكانية تطبيق إعادة التأسيس على أمور أخرى غير التفريع المستهدف بإعادة التأسيس. نأخذ المثال الموضَّح في الصورة أدناه. أنشأت تفريعا جديدا باسم server (تفريع موضوع Topic branch) لإضافة ميزات من جهة الخادوم إلى مشروعك وأضفت إيداعا إلى هذا التفريع؛ ثم أنشأت بعدها تفريعا جديدا لميزات تعمل في جانب العميل client وأضفت عليه إيداعات متتابعة. عدت في الأخير إلى تفريع server وأضفت إليه إيداعات أخرى.

05_rebasing.thumb.png.bd2e27cd4c5eeef826

نريد الآن دمج التغييرات في جانب العميل إلى التفريع الرئيس وفي نفس الوقت ترك التعديلات في جانب الخادوم لاختبارها أكثر. نستخدم خيار onto-- مع أمر git rebase لأخذ الإيداعات الموجودة على التفريع client دون أن تكون موجودة على server (أي الإيداعات C8 وC9):

git rebase --onto master server client

يمكن تفسير الأمر السابق بـ”افحص تفريع client، اعثر على الإيداعات التالية للإيداع المشترك بينه والتفريع server ثم طبقها على التفريع master“. يبدو الأمر معقّدا إلا أن النتيجة مذهلة.

06_rebasing.thumb.png.472a503c4cc4cdeba0

يمكن بعدها تطبيق دمج سريع على التفريع الرئيس:

git checkout master
git merge client

07_rebasing.thumb.png.9bf962b7f52b8aa9cb

نرغب الآن في تضمين التفريع server أيضا. تمكننا إعادة تأسيس التفريع server على التفريع master دون الحاجة لفحصه أولا بتنفيذ الأمر [git rebase [basebranch] [topicbranc. يفحص الأمر تفريع الموضوع (أي التفريع server في هذه الحالة) تلقائيا ثم يؤسسه على التفريع القاعدي (master):

git rebase master server

يُعيد الأمر تأسيس التفريع server على التفريع master على النحو الموضَّح في الصورة التالية.

08_rebasing.thumb.png.ae2f0e49c6536f48dc

نطبق دمجا سريعا للتفريع القاعدي master:

git checkout master
git merge server

يمكن الآن حذف التفريعين server وclient لأن جميع الأعمال فيهما أدمجت في التفريع الرئيس ولم تعد بحاجة إليها.

git branch -d client
git branch -d server

يبدو سجل التعديلات كما يظهر في الصورة أدناه.

09_rebasing.thumb.png.86bf3f5fa148c06cfd

مشاكل إعادة التأسيس

يمكن إجمال مخاطر إعادة تأسيس التفريعات في النصيحة التالية: لا تُعِد تأسيس إيداعات موجودة خارج مستودعك.

تهجُر عندما تعيد تأسيس تفريع، إيداعاتٍ موجودةً وتبدلها بأخرى جديدة مشابهة إلا أنها مختلفة. تصبح الأمور صعبة وغير مرتبة عند استخدام إعادة التأسيس بما يخالف النصيحة أعلاه، مثلا عندما تدفع إيداعات إلى مستودع مشترك ثم يجلبها آخرون ويعملون عليها، ثم تعيد كتابة الإيداعات باستخدام أمر git rebase وتدفعها إلى المستودع مرة أخرى. سيحتاج مشاركوك في هذه الحالة لإعادة دمج أعمالهم ويمكن أن تحدث مشاكل عندما يريدون دفع أعمالهم بعدك.

فلنأخذ مثالا لحالة يمكن أن تؤدي فيها إيداعات - أعيد تأسيسها ووُفِّرت للعموم - إلى مشاكل. نفترض أنك نسخت مستودعا من خادوم مركزي ثم أجريت أعمالا عليه. يبدو سجل الإيداعات على النحو الموضَّح في الصورة التالية.

10_rebasing.thumb.png.041a6404cef6350521

يعمل أحدهم في هذه الأثناء على إضافة تعديلات إلى المستودع من ضمنها دمج تفريع ثم يدفع النتيجة إلى الخادوم المركزي، تأتي أنت وتفتش عن الجديد على المستودع البعيد وتدمج التفريع البعيد الجديد إلى عملك؛ يصبح سجل الإيداعات على النحو التالي.

11_rebasing.thumb.png.76bfb6169ff92ada9a

يقرّر الشخص الذي دفع التفريع المدموج التراجع وإعادة تأسيس التفريع بدلا من ذلك؛ ينفذ أمر:

 git push --force 

لتغيير سجل الإيداعات على الخادوم، ثم تأتي أنت للتفتيش عن جديد المستودع البعيد وتجلب الإيداعات الجديدة.

12_rebasing.thumb.png.5c73d893789e9aae01

أنتما الآن في ورطة: تنفيذك لأمر:

 

git pull 

سينشئ إيداعَ دمج يتضمّن سجلي التعديلات وسيبدو المستودع لديك كالتالي:

13_rebasing.thumb.png.7903ef4657c6aa0a85

سيظهر عند تنفيذ أمر git log على سجل التعديلات أعلاه إيداعان لديهما نفس الكاتب، التاريخ والرسالة؛ أمر مربك. علاوة على ذلك، يؤدي دفع بيانات بسجل التغيير هذا إلى الخادوم إلى إدخال كل هذه الإيداعات المعاد تأسيسها إلى الخادوم وهو ما سيربك الآخرين أكثر. من الآمَن في هذه الحالة افتراضُ أن المطوّر الآخر لا يريد أن يَبقى الإيداعان C4 وC6 في سجل الإيداعات؛ ولهذا السبب أعاد التأسيس أصلا.

إصلاح ما أفسدته إعادة التأسيس

يوفر Git آليات إضافية لحالات مثل التي رأيناها في الفقرة السابقة. التحدي في المواقف التي يدفع فيها أحد أعضاء الفريق تعديلات تغير تلك التي أعدت تأسيس عملك عليها هو التفريق بين عملك وبين ما أعيدت كتابته. يحسب Git، إضافة إلى مجموع التحقق (دالة SHA-1) الخاص بالإيداع مجموع تحقق خاص بالترقيع Patch الذي أضيف مع الإيداع؛ يسمّى مجموع التحقق هذا بمعرّف الترقيع Patch id.

يمكن لـGit، عندما تجلب بيانات أعيدت كتابتها ثم تعيد التأسيس لها على الإيداعات الجديدة من الشريك، أن يتعرّف على الإيداعات الخاصة بك ثم يطبقها على التفريع الجديد.

بدلا من تنفيذ أمر git merge بعد أن نفّذ الشريك أمر git push --force في المثال السابق (الحالة الموضَّحة في الصورة قبل الأخيرة) نفّذ الأمر git rebase teamone/master وسيقوم Git بالتالي:

  • تحديد الإيداعات الخاصة بتفريعك فقط (أي الإيداعات C6، C4، C3، C2 وC7).
  • التعرف على الإيداعات التي ليست إيداعات دمج (C3، C2 وC4).
  • تحديد الإيداعات التي لم تُعد كتابتها في التفريع الوجهة (التفريعان C2 وC3 فالإيداعان C4 و'C4 هما نفس الترقيع، أي أن تعديلاتهما هي نفسها).
  • تطبيق هذه الإيداعات على التفريع teamone/master.

ينتج عن الخطوات المتّبعة الوضعية التالية بدلا من تلك التي كنا سنجد فيها أنفسنا لو نفّذنا أمر git pull.

14_rebasing.thumb.png.69cbff3d89d3fe30e8

تعمل الطريقة أعلاه فقط إن كان الإيداعان C4 و'C4 الذين أضافهما شريكك متطابقين تقريبا؛ وإلا فإن أمر rebase لن يكون قادرا على معرفة أيهما المكرَّر وسيضيف إيداعا جديدا مشابها لـ'C4 (يُحتمَل جدّا ألا يُطبَّق نظرا لأن التعديلات ستكون غالبا موجودة في مكان ما).

يمكن تسهيل الأمر بتنفيذ git pull --rebase بدلا من git pull عادي؛ أو التأسيس يدويا بتنفيذ أمر git fetch تتبعه بـ git rebase teamone/master في هذه الحالة.

توجد طريقة لجعل خيار rebase-- مبدئيا عند تنفيذ أمر git pull وهي إعداد المعطى pull.rebase في الإعدادات ليأخذ القيمة git config --global pull.rebase true.

ستجري الأمور بخير ما دمت تستخدم إعادة التأسيس بوصفها طريقة لتنسيق الإيداعات والعمل عليها قبل دفعها وما ظللت ملتزما بإعادة تأسيس الإيداعات التي لم تتوفر أبدا للعموم دون غيرها. في الجانب الآخر، تتعرّض للكثير من المخاطر بإعادة التأسيس على إيداعات سبق دفعها إلى مستودعات عمومية ويمكن أن يكون بعض شركائك أسسوا عملهم عليها. تأكد من تنفيذ جميع أعضاء الفريق لأمر git pull --rebase لمحاولة التقليل من المشاكل بعد إعادة تأسيس أحدهم على إيداعات جلبها من المستودع العمومي.

إعادة التأسيس أم الدمج؟

ربما تتساءل الآن بعد الخوض في كلّ من إعادة التأسيس والدمج، أيهما أنسب؟ سنعود إلى الوراء قليلا قبل الإجابة على التساؤل لندقّق في معنى سجل المستودع.

يمكن رؤية الموضوع على أساس أن سجل المستودع هو تسجيل لما حدث فيه، بمعنى أنه وثيقة تاريخية قيّمة بذاتها، ولا يجوز التلاعب بها. يعدّ تغيير السجلّ من وجهة النظر هذه خطأ كبيرا؛ فذلك يعني تزوير الوقائع. ماذا لو حدثت فوضى في المستودع بعد متتالية من الإيداعات؟ هذه هي الوقائع ويجب أن تظل مسجّلة تقول وجهة النظر هذه.

توجد رؤية مغايرة للموضوع، تقول إن سجل الإيداعات هو رواية لكيفية بناء المشروع. يحتاج دليل صيانة البرنامج لنفس الحذر الذي يتطلبه نشر كتاب، فلا يجوز أن تنشر كتابا مازال في طور المسودة. يتبنى وجهة النظر هذه من يستخدمون أدوات مثل git rebaseوgit filter-branch لسرد القصة بأفضل طريقة للقراء المستقبليين.

يتضح الآن أن سؤال أيها أستخدم، إعادة التأسيس أم الدمج؟ ليس بتلك السهولة. Git أداة قوية تسمح بفعل الكثير من الأشياء للسجّل وبه؛ إلا أن كلّ فريق مختلف وكذلك المشاريع. يعود الأمر إليك، بعد فهم طريقة عمل الاثنين، لاختيار ما يناسب الفريق الذي تعمل معه والمشروع الذي تعمل عليه.

المبدأ العام للحصول على أفضل ما في الطريقتين هو إعادة تأسيس التعديلات المحلية التي لم تشارَك بعد قبل أن تدفعها من أجل تقديم القصة؛ والابتعاد تماما عن إعادة تأسيس أي شيء سبق دفعه إلى مستودع عمومي.

خاتمة

غطينا في هذه السلسلة أساسيات التفريع والدمج في Git. يجدر بك أن تكون مرتاحا للعمل على إنشاء التفريعات الجديدة والانتقال للعمل عليها، التبديل بين التفريعات ودمجها ودمج التفريعات المحلية معا؛ بالإضافة إلى تشارك التفريعات بدفعها إلى خادوم مشترك، العمل مع آخرين على تفريعات مشتركة وإعادة تأسيس تفريعاتك فبل أن تشاركها. الخطوة الموالية هي معرفة ما تحتاجه لتشغيل خادوم Git خاص بك لتشارك المستودعات.

ترجمة -بتصرف- للفصل Git Branching - Rebasing من كتاب Pro Git لصاحبه Scott Chacon.


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...