البحث في الموقع
المحتوى عن 'تفريع'.
-
يعرض هذا المقال أساسيات التفريع والدمج في Git. نبدأ بمثال سهل لسيناريو يحدث كثيرا أثناء تطوير البرمجيات ونرى كيفية تطبيقه على التفريع والدمج. لنفترض حدوث الخطوات التالية: كنت تعمل على موقع ويب وأنجزت الجزء الأهم وهو الآن مفتوح للزوار. أنشأت تفريعا جديدا وبدأت العمل على ميزات ستضيفها لاحقا للموقع. عند هذه النقطة وردك اتصال يخبرك عن مشكل في الموقع. المشكل خطير ويحتاج حلا سريعا. تنتقل لنسخة الموقع الموجودة على بيئة الإنتاج (إصدار الموقع المفتوح للزوار). أي أنك انتقلت للتفريع الذي أطلقت منه الموقع (وليكن التفريع master). تنشئ تفريعا جديدا انطلاقا من تفريع الإنتاج بهدف إصلاح الخلل (الترقيع). تعمل على التفريع الجديد وتختبر التعديلات حتى تتأكد من جاهزيتك لإضافتها إلى بيئة الإنتاج. تدمج تفريع الترقيع مع تفريع الإنتاج وتدفع التغييرات إلى الموقع. أصلحت المشكل وبإمكانك الآن العودة إلى تفريع تحسين الميزات الذي كنت تعمل عليه قبل ورود الاتصال. المبادئ الأساسية للتفريع نفرض أنك تعمل على مشروع سبق أن أضفت إليه إيداعين. سجل إيداعات مستقيم قررت أنك ستعمل على إضافة الميزة رقم 53. تنشئ لهذا الغرض تفريعا جديدا. لإنشاء تفريع والانتقال للعمل عليه في نفس الوقت نستخدم الأمر git checkout مع الخيار b-: git checkout -b iss53 Switched to a new branch "iss53" هذا الأمر هو اختصار للأمرين: git branch iss53 git checkout iss53 إنشاء مؤشر تفريع جديد تنفيذ أمر git checkout يعني نقل المؤشر HEAD ليحيل إلى التفريع iss53. استمريت في العمل على موقعك، وتنفيذ إيداعات: vim index.html git commit -a -m 'added a new footer [issue 53]' وهو ما يجعل التفريع iss53 يتقدم بتتالي الإيداعات: تقدّم التفريع iss53 بتوالي الإيداعات يأتي الآن الاتصال الذي ينبئ بوجود مشكل في الموقع؛ ويجب التغلب على هذا المشكل في أقرب وقت. لا تحتاج مع Git لنشر الترقيع العاجل مع ترقيع الميزة 53؛ كما أنك لا تحتاج لبذل الكثير من الجهد للتراجع عن التعديلات التي أضفتها أثناء عملك على الميزة 53 حتى تعود إلى حالة الموقع الموجود الآن أمام الزوار. كل ما عليك فعله هو العودة إلى التفريع الرئيس master وترك التفريع iss53 لحين الفراغ من العمل العاجل. انتبه إلى أن Git لن يسمح لك بالانتقال إلى تفريع جديد إن كان مجلد العمل أو منطقة الإدراج يحوي تغييرات تتعارض مع التفريع الذي تريد الانتقال إليه. توجد طرق للالتفاف حول هذا الأمر، وهي ادّخار الإيداعات Stashing وتصحيحها Amending وهو ما سنتطرّق إليه لاحقا عند الحديث عن الادّخار والتنظيف Cleaning. سنفترض في الوقت الحالي أن جميع التعديلات أودعت وبإمكننا الانتقال إلى التفريع الرئيس: git checkout master Switched to branch 'master' يكون مجلد العمل في المشروع بالوصول إلى هذه النقطة مماثلا تماما لما كان عليه قبل بدء العمل على الميزة 53، ويمكنك الآن التركيز على العلة العاجلة. من المهم تذكر هذا الأمر: يعيد Git عند الانتقال إلى تفريع، مجلد العمل إلى ما كان عليه بعد آخر مرة نفّذت فيها إيداعا على هذا التفريع. فيضيف ملفات وينقل أخرى أو يغيرها تلقائيا للتأكد من أن نسخة مجلد العمل مطابقة لما كان عليه بعد آخر عملية إيداع على التفريع. لدينا ترقيع عاجل يجب إصداره. ننشئ تفريعا خاصا hotfix للعمل على إصدار ترقيع في أقرب وقت: git checkout -b hotfix Switched to a new branch 'hotfix' vim index.html git commit -a -m 'fixed the broken email address' [hotfix 1fb7853] fixed the broken email address 1 file changed, 2 insertions(+) إنشاء تفريع hotfix انطلاقا من التفريع master يمكن الآن اختبار التعديلات والتأكد من أن الترقيع يؤدي العمل المطلوب ثم بعد ذلك ندمج التفريع hotfix (باستخدام أمر git merge) مع التفريع الرئيس لنشره على بيئة الإنتاج. ننفذ الأوامر التالية لهذا الغرض: git checkout master git merge hotfix Updating f42c576..3a0874c Fast-forward index.html | 2 ++ 1 file changed, 2 insertions(+) لاحظ جملة fast-forward (تقدّم سريع) بعد تنفيذ أمر الدمج. يعود السبب في ذلك إلى أن الإيداع الذي يشير إليه التفريع الذي دمجت فيه يوجد ضمن سوابق الإيداع الذي دمجته. بعبارة أخرى؛ عندما تريد دمج إيداع “أ” مع إيداع “ب” يوجد من بين سوابق الإيداع المدموج ("أ") فإن Git يسهّل الأمر بتقديم مؤشر التفريع “ب” ليحيل إلى الإيداع “أ”؛ يفعل Git هذا الأمر بسبب عدم وجود إيداعات متنافرة بين التفريعين لدمجها. بمعنى أنه في حالتنا لم يطرأ أي إيداع على التفريع master منذ إنشاء التفريع hotfix. نقول في هذه الحالة إن Git أجرى تقدما سريعا fast-forward. توجد التعديلات الآن في اللقطة التي يشير إليها التفريع master ويمكن نشر الترقيع ليصبح متاحا للعموم. التقدم السريع للتفريع master إلى التفريع hotfix أنت جاهز بعد نشر الترقيع العاجل للعودة إلى العمل الذي انقطعت عنه. لكن يجب أولا حذفُ تفريع hotfix الذي لم تعد تحتاج إليه؛ تفريع master يشير الآن إلى نفس الإيداع. يمكن حذف التفريع hotfix باستخدام أمر git branch مع خيار d-: git branch -d hotfix Deleted branch hotfix (3a0874c). يمكن العودة إلى التفريع iss53 واستكمال العمل على الميزة 53: git checkout iss53 Switched to branch "iss53" vim index.html git commit -a -m 'finished the new footer [issue 53]' [iss53 ad82d7a] finished the new footer [issue 53] 1 file changed, 1 insertion(+) استكمال العمل على الميزة 53 يجب الانتباه هنا إلى أن العمل المضاف إلى التفريع hotfix غير موجود في ملفات التفريع iss53. يوجد لديك خياران لإضافة تعديلات hotfix؛ إما دمج التفريع الرئيس في تفريع iss53 أو الانتظار حتى تكمل العمل على التفريع iss53 ثم تدمجه في التفريع الرئيس. المبادئ الأساسية لدمج التفريعات أكملت العمل على الميزة 53 وأنت جاهز لدمجها في التفريع الرئيس. يجري دمج التفريع iss53 في التفريع الرئيس بنفس الطريقة التي دمجنا بها التفريع hotfix في التفريع الرئيس: الانتقال إلى التفريع الرئيس بالأمر git checkout ثم تنفيذ أمر الدمج: git checkout master Switched to branch 'master' git merge iss53 Merge made by the 'recursive' strategy. index.html | 1 + 1 file changed, 1 insertion(+) يوجد اختلاف بين الدمج في هذه الحالة ودمج التفريع hotfix السابق. مصدر الاختلاف هو حدوث إيداعات على كل من التفريعين بالتوازي؛ الإيداع الذي يشير إليه التفريع الرئيس الآن ليس هو الإيداع الذي كان يشير إليه عند إنشاء التفريع iss53. يعني هذا أن على Git اتخاذ طريقة مغايرة للتقدم السريع آنفة الذكر. ينفذ Git في هذه الحالة ما يعرف بالدمج الثلاثي Three-way merge فيستخدم اللقطتين المشار إليهما في الصورة أدناه والإيداع المشترك بين الفرعين. 3 لقطات مستخدمة في الدمج ينشئ Git، بدلا من تقديم المؤشر إلى آخر إيداع في التفريع iss53 مثل ما فعل مع التفريع hotfix، ينشئ لقطة جديدة ناتجة عن الدمج الثلاثي، وينشئ تلقائيا إيداعا جديدا يشير إلى اللقطة الجديدة. يُسمّى هذا الإيداع بإيداع الدمج Merge commit، ويتميز بكونه جزءا من متتاليتي إيداعات (أي أن له سابقين). إيداع دمج تجدر الإشارة هنا إلى أن Git يحدّد الإيداع الأفضل من بين الإيداعات السابقة لاستخدامه قاعدة للدمج. يختلف الأمر عن نظم إدارة نسخ أخرى مثل CVS وSubversion (قبل الإصدار 1.5) يُطلب فيها من المطور تحديد أفضل قاعدة إيداعات للدمج وفقا لها؛ وهو ما يجعل دمج التفريعات في Git أسهل كثيرا من النظم الأخرى. لم نعد نحتاج الآن، بعد دمج الميزة في التفريع الرئيس، للتفريعة 53. يمكن إغلاق التذكرة Ticket في نظام إدارة التذاكر لديك وحذف التفريع: git branch -d iss53 التعارض في عمليات الدمج لا تجري الأمور دائما بنفس السهولة المذكورة في الفقرة السابقة. إن غيّرت نفس الأجزاء من نفس الملف في فرعين تريد دمجهما فلن يكون بمقدور Git دمجهما بطريقة صحيحة. مثلا، إن عدّل العمل على الميزة 53 والترقيع في التفريع hotfix نفس الجزء من نفس الملف فسيظهر لديك تعارض Conflict في الدمج كما في المثال أدناه: git merge iss53 Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result. لم ينشئ Git في المثال أعلاه إيداعا للدمج؛ بل أوقف عملية الدمج إلى أن تحل مشكلة التعارض. إن أردت رؤية الملفات التي لم تطلها عملية الدمج بعد فيمكنك تنفيذ الأمر git status: git status On branch master You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add <file>..." to mark resolution) both modified: index.html no changes added to commit (use "git add" and/or "git commit -a") تظهر جميع الملفات التي يوجد بها تعارض يمنع دمجها تحت بند Unmerged (غير مدموج). يضيف Git علامات قياسية لحل التعارض إلى الملفات المعنية ليمكن للمطور فتحها ومن ثم حلّ التعارضات. يحوي الملف فقرة تشبه ما يلي: <<<<<<< HEAD:index.html <div id="footer">contact : email.support@github.com</div> ======= <div id="footer"> please contact us at support@github.com </div> >>>>>>> iss53:index.html يعني هذا أن النسخة التي يحيل إليها المؤشر HEAD هي تلك الموجودة في الأعلى (كل ما يوجد فوق الخط ======)؛ بينما يظهر محتوى الملف الموجود في التفريع iss53 في الأسفل. للتذكير، يحيل المؤشر HEAD إلى التفريع الرئيس الآن، ويعود السبب في ذلك إلى أننا انتقلنا إليه بتنفيذ الأمر git checkout قبل محاولة الدمج. يجب اختيار محتوى أحد الملفين أو دمجهما يدويا. يمكن على سبيل المثال تغيير كامل الجزء المعلّم (يبدأ بـ<<<<<<< وينتهي بـ >>>>>>>) ووضع محتوى جديد مكانه: <div id="footer"> please contact us at email.support@github.com </div> يحوي هذا الحل جزءًا من كل فقرة، ويُلاحظ أن الأسطر >>>>>>>، <<<<<<< و ======= اختفت بالكامل. نفذ أمر git add على كل ملف بعد التخلص من التعارض. إضافة ملف به تعارض إلى منطقة الإدراج في Git يعني أن التعارض في الملف حُلّ. نفّذ أمر git mergetool إن أردت استخدام أداة رسومية لحل التعارضات. يُظهِر الأمر الأداة الرسومية المناسبة لحل التعارضات: git mergetool This message is displayed because 'merge.tool' is not configured. See 'git mergetool --tool-help' or 'git help config' for more details. 'git mergetool' will now attempt to use one of the following tools: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge Merging: index.html Normal merge conflict for 'index.html': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (meld): اضغط زر Enter للبدء في استخدام الأداة الافتراضية (في المثال أعلاه يظهر اسم الأداة meld بين قوسين لأن المستخدم يشغّل نظام لينكس). إن أردت استخدام أداة مغايرة فيمكنك الاختيار بين الأدوات المدعومة التي يسردها أمر git mergetool مباشرة بعد جملة one of the following tools ثم كتابة اسم الأداة والضغط على زر Enter. يسألك Git بعد الخروج من الأداة ما إذا كان الدمج ناجحا. فإن أجبت بنعم (زر y على لوحة المفاتيح) فإنه يعلمّ الملفات للإشارة لتجاوز التعارض ويضيفها بالتالي إلى منطقة الإدراج. يمكن تنفيذ أمر git statusمرة أخرى للتأكد من أن جميع التعارضات حُلّت: git status On branch master All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: index.html ثم تنفيذ أمر الإيداع git commit لاستكمال الدمج، بعد التأكد من أن جميع الملفات التي يوجد بها تعارض أضيفت إلى منطقة الإدراج. تبدو رسالة الإيداع المبدئية على النحو التالي (عند تنفيذ أمر git commit بعد دمج الملفات المتعارضة): Merge branch 'iss53' Conflicts: index.html # # It looks like you may be committing a merge. # If this is not correct, please remove the file # .git/MERGE_HEAD # and try again. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # All conflicts fixed but you are still merging. # # Changes to be committed: # modified: index.html # يمكنك تعديل الرسالة لإضافة تفصيلات عن كيفية حل التعارض إن كنت ترى أن ذلك سيفيد الآخرين عند النظر في هذا الدمج في المستقبل. لماذا فعلت ما فعلت، خصوصا إن لم يكن بديهيا. ترجمة -بتصرف- للفصل Git Branching - Basic Branching and Merging من كتاب Pro Git لصاحبه Scott Chacon.
-
توجد طريقتان أساسيتان في Git لتضمين التعديلات من تفريع إلى آخر: merge و rebase. يشرح هذا المقال ماهية إعادة تأسيس التفريعات Rebasing، كيفيتها، الفائدة منها والحالات التي يجدر بك عدم استخدام إعادة التأسيس فيها. مبادئ إعادة التأسيس يمكن بالعودة إلى المثال في درس المبادئ الأساسية لدمج التفريعات رؤية تمايز أعمالك بإضافة إيداعات إلى كلٍّ من التفريعين. استخدام أمر git merge هو أسهل طريقة، مثل ما رأينا سابقا، لتضمين تفريع في آخر. ينفّذ الأمرُ الدمجَ الثلاثي بين آخر لقطتين في كلّ تفريع (C3 وC4) واللقطة المشتركة الأقرب (C2) منشئا بذلك لقطة (وإيداعا) جديدة. إلا أنه توجد طريقة أخرى: يمكنك أخذ الترقيع 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 يعمل الأمر بالرجوع إلى الإيداع السابق المشترك بين التفريعين (التفريع الذي تعمل عليه الآن، والتفريع الذي تريد إعادة التأسيس عليه)، الحصول على التغييرات التي أدرجها كل إيداع على التفريع الحالي وتخزينها في ملف ظرفي، ضبط التفريع الحالي على آخر إيداع في التفريع الذي نريد إعادة التأسيس عليه ثم في الأخير تطبيق التعديلات بالترتيب. نستطيع بالوصول إلى هذه النقطة العودة إلى التفريع master وتنفيذ دمج بتقدم سريع: git checkout master git merge experiment يشير الإيداع 'C4 إلى نفس اللقطة التي يشير إليها الإيداع C5 في مثال الدمج الذي تحدثنا عنه في البداية. لا يوجد فرق في المنتوج النهائي بين الاثنين؛ إلا أن إعادة التأسيس تجعل سجل التعديلات أكثر وضوحا. يبدو سجل الإيداعات في تفريع أعيد تأسيسه خطيًّا: تظهر الأعمال كما لو أنها حدثت بالتتالي، حتى لو كان الواقع أنها كانت بالتوازي. يُلجَأ عادة لتأسيس التفريعات للتأكد من أن الإيداعات تُطبَّق دون مشاكل على تفريع بعيد؛ مثلا عند المشاركة في مشروع لا تتولى صيانته. تعمل في هذه الحالة على تفريع ثم تعيد تأسيس التفريع على origin/master عندما تكون جاهزا لإرسال الترقيع إلى المشروع الرئيس. لن يحتاج مسؤول المشروع في هذه الحالة إلا إلى دمج سريع. انتبه إلى أن اللقطة التي يشير إليها التفريع هي نفسها، سواء كانت الأخيرة من إيداعات بعد إعادة تأسيس التفريع أو إيداع دمج. وجه الاختلاف هو فقط سجل التغييرات. تكرّر إعادة التأسيس التغييرات من خط عمل على آخر بالترتيب الذي حدثت به في حين يأخذ الدمج نقطتي النهاية ويدمجهما معا. إعادات تأسيس متقدمة يتيح Git إمكانية تطبيق إعادة التأسيس على أمور أخرى غير التفريع المستهدف بإعادة التأسيس. نأخذ المثال الموضَّح في الصورة أدناه. أنشأت تفريعا جديدا باسم server (تفريع موضوع Topic branch) لإضافة ميزات من جهة الخادوم إلى مشروعك وأضفت إيداعا إلى هذا التفريع؛ ثم أنشأت بعدها تفريعا جديدا لميزات تعمل في جانب العميل client وأضفت عليه إيداعات متتابعة. عدت في الأخير إلى تفريع server وأضفت إليه إيداعات أخرى. نريد الآن دمج التغييرات في جانب العميل إلى التفريع الرئيس وفي نفس الوقت ترك التعديلات في جانب الخادوم لاختبارها أكثر. نستخدم خيار onto-- مع أمر git rebase لأخذ الإيداعات الموجودة على التفريع client دون أن تكون موجودة على server (أي الإيداعات C8 وC9): git rebase --onto master server client يمكن تفسير الأمر السابق بـ”افحص تفريع client، اعثر على الإيداعات التالية للإيداع المشترك بينه والتفريع server ثم طبقها على التفريع master“. يبدو الأمر معقّدا إلا أن النتيجة مذهلة. يمكن بعدها تطبيق دمج سريع على التفريع الرئيس: git checkout master git merge client نرغب الآن في تضمين التفريع server أيضا. تمكننا إعادة تأسيس التفريع server على التفريع master دون الحاجة لفحصه أولا بتنفيذ الأمر [git rebase [basebranch] [topicbranc. يفحص الأمر تفريع الموضوع (أي التفريع server في هذه الحالة) تلقائيا ثم يؤسسه على التفريع القاعدي (master): git rebase master server يُعيد الأمر تأسيس التفريع server على التفريع master على النحو الموضَّح في الصورة التالية. نطبق دمجا سريعا للتفريع القاعدي master: git checkout master git merge server يمكن الآن حذف التفريعين server وclient لأن جميع الأعمال فيهما أدمجت في التفريع الرئيس ولم تعد بحاجة إليها. git branch -d client git branch -d server يبدو سجل التعديلات كما يظهر في الصورة أدناه. مشاكل إعادة التأسيس يمكن إجمال مخاطر إعادة تأسيس التفريعات في النصيحة التالية: لا تُعِد تأسيس إيداعات موجودة خارج مستودعك. تهجُر عندما تعيد تأسيس تفريع، إيداعاتٍ موجودةً وتبدلها بأخرى جديدة مشابهة إلا أنها مختلفة. تصبح الأمور صعبة وغير مرتبة عند استخدام إعادة التأسيس بما يخالف النصيحة أعلاه، مثلا عندما تدفع إيداعات إلى مستودع مشترك ثم يجلبها آخرون ويعملون عليها، ثم تعيد كتابة الإيداعات باستخدام أمر git rebase وتدفعها إلى المستودع مرة أخرى. سيحتاج مشاركوك في هذه الحالة لإعادة دمج أعمالهم ويمكن أن تحدث مشاكل عندما يريدون دفع أعمالهم بعدك. فلنأخذ مثالا لحالة يمكن أن تؤدي فيها إيداعات - أعيد تأسيسها ووُفِّرت للعموم - إلى مشاكل. نفترض أنك نسخت مستودعا من خادوم مركزي ثم أجريت أعمالا عليه. يبدو سجل الإيداعات على النحو الموضَّح في الصورة التالية. يعمل أحدهم في هذه الأثناء على إضافة تعديلات إلى المستودع من ضمنها دمج تفريع ثم يدفع النتيجة إلى الخادوم المركزي، تأتي أنت وتفتش عن الجديد على المستودع البعيد وتدمج التفريع البعيد الجديد إلى عملك؛ يصبح سجل الإيداعات على النحو التالي. يقرّر الشخص الذي دفع التفريع المدموج التراجع وإعادة تأسيس التفريع بدلا من ذلك؛ ينفذ أمر: git push --force لتغيير سجل الإيداعات على الخادوم، ثم تأتي أنت للتفتيش عن جديد المستودع البعيد وتجلب الإيداعات الجديدة. أنتما الآن في ورطة: تنفيذك لأمر: git pull سينشئ إيداعَ دمج يتضمّن سجلي التعديلات وسيبدو المستودع لديك كالتالي: سيظهر عند تنفيذ أمر 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. تعمل الطريقة أعلاه فقط إن كان الإيداعان 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.
-
المراجع البعيدة في Git هي مؤشرات Pointers إلى المستودعات البعيدة بما تتضمنه من تفريعات، وسوم وغيرها. يمكنك الحصول على لائحة كاملة بالمراجع البعيدة بتنفيذ الأمر: git ls-remote [remote] أو: git remote show [remote] لعرض التفريعات البعيدة إضافة لمعلومات متفرقة. إلا أنه توجد طريقة أخرى أكثر شيوعا وهي الاستفادة من تفريعات التتبع عن بعد Remote-tracking branches تفريعات التتبع عن بعد هي مراجع لحالة التفريعات البعيدة. توجد هذه التفريعات محليا إلا أنه ليس بالإمكان تحريكها فهي تتحرك تلقائيا عندما تتبادل بيانات مع الخادوم البعيد. تعمل تفريعات التتبع عن بعد كإشارة مرجعية لتذكيرك بموضع تفريعات المستودعات البعيدة في آخر مرة اتصلت فيها بالخادوم. تأخذ تفريعات التتبع عن بعد الهيئة (remote)/(branch). إن أردت مثلا رؤية الحالة التي كان عليها التفريع master على مستودع origin البعيد في آخر مرة اتصلت فيها به فستفحص Checkout التفريع origin/master. إن كنت تعمل عن بعد مع زملاء، ثم دفعوا Push تفريعا باسم iss53 فيمكن أن يكون لديك تفريع محلي بنفس الاسم بينما يشير تفريع التتبع عن بعد إلى الإيداع على origin/iss53. قد يبدو الأمر معقّدا قليلا لذا سنأخذ مثالا. فلنفترض أن لديك خادوم Git على العنوان git.ourcompany.com. إن نسخت Clone مستودعا من الخادوم فإن أمر git clone سيسميه تلقائيا origin، يجلب جميع البيانات إلى المستودع المحلي، ينشئ مؤشرا إلى آخر إيداع في التفريع master ويسميه محليا بـorigin/master. يوفر Git أيضا تفريع master محليا يبدأ من نفس مبدأ التفريع master الخاص بـorigin. ملحوظة: origin ليس مستودعا ذا خصوصية. لا يمثل origin مستودعا ذا خصوصية تماما كما أن master ليس تفريعا ذا دلالة خاصة في Git. يسمّي Git التفريع َالمبدئي بـmaster عند تنفيذ أمر git init كما يسمي أمر git clone المستودع البعيد مبدئيا بـ origin؛ لهذا السبب فقط يكثُر استخدام التسميتين. إن نفذت أمر git clone مع خيار o- مثل: git clone -o booyah فسيكون التفريع booyah/master هو التفريع المبدئي لديك. إن عملت على التفريع master المحلي وأثناء عملك دفع أحدهم تغييرات إلى git.ourcompany.com وحدّث التفريع master على الخادوم فسيختلف سجل الإيداعات المحلي عن البعيد. في تلك الأثناء لن يتغير تفريع origin/master ما لم تدخل في تبادل بيانات مع الخادوم. نفذ أمر: git fetch origin لمزامنة تفريع التتبع عن بعد. يبحث الأمر عن خادوم المستودع origin (أي git.ourcompany.com )، يفتش عن البيانات الموجودة على الخادوم التي لا توجد محليا ثم يحدّث البيانات المحلية محرّكا المؤشر origin/master إلى موضعه الجديد. نأخذ مثالا لتوضيح كيف تبدو تفريعات التتبع عن بعد لخواديم بعيدة متعدّدة. نفترض أن لديك خادوم Git داخلي تستخدمه إحدى فرق العمل لأغراض التطوير والاختبار. عنوان هذا الخادوم هو git.team1.ourcompany.com. تمكن إضافة مرجع للخادوم البعيد هذا إلى المشروع الذي تعمل عليه حاليا بتنفيذ الأمر: git remote add كما تطرقنا لذلك في درس العمل على المستودعات البعيدة في Git. نضيف المستودع مع استخدام الاختصار teamone. يمكنك الآن تنفيذ الأمر: git fetch teamone للبحث عن البيانات الموجودة على الخادوم الجديد دون أن تكون موجودة محليا. يوجد على الخادوم teamone جزء من العمل الموجود على الخادوم الأول؛ يعني هذا أن Git بعد تنفيذ الأمر السابق لن ينزل إيداعات جديدة لكنه سيكتفي بإنشاء تفريع تتبع عن بعد جديد ويسميه teamone/master يشير إلى آخر إيداع في التفريع master على الخادوم teamone. دفع البيانات عندما تريد مشاركة تفريع تعمل عليه مع آخرين فستحتاج لدفع البيانات إلى مستودع بعيد لديك صلاحية الكتابة عليه. لا يُزامن Git التفريعات المحلية تلقائيا مع التفريعات البعيدة، بل يجب أن تزامن التفريع الذي ترغب في مشاركته يدويا. تستخدم بهذه الطريقة تفريعات خاصة (محلية) للأعمال التي لا تريد مشاركتها بينما تدفع بيانات التفريعات التي تود مشاركتها إلى خادم مشترك. إن كان لديك تفريع باسم serverfix تريد التشارك بالعمل عليه مع آخرين فيمكنك دفع البيانات إليه بنفس طريقة دفع التفريع الأخرى بتنفيذ الأمر: git push <remote> <branch> كما في المثال التالي: git push origin serverfix Counting objects: 24, done. Delta compression using up to 8 threads. Compressing objects: 100% (15/15), done. Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done. Total 24 (delta 2), reused 0 (delta 0) To https://github.com/schacon/simplegit * [new branch] serverfix -> serverfix يأخذ Git تلقائيا محتوى التفريع serverfix إلى refs/heads/serverfix:refs/heads/serverfix وهو ما يعني أخذ بيانات التفريع المحلي serverfix ودفعها لتحديث بيانات التفريع البعيد serverfix. سنعرج للحديث عن refs/heads/ عندما نتحدث عن أمور داخلية في Git، يمكن الآن تجاوزها. يمكن أيضا تنفيذ الأمر: push origin serverfix:serverfix الذي يؤدي نفس المهمة. يُفسَّر الأمر بـ "خذ التفريع serverfix المحلي واجعله هو تفريع serverfix البعيد". يمكن استخدام هذه الطريقة لدفع بيانات تفريع محلي إلى تفريع بعيد يختلف عنه في الاسم. أما إن كنت ترغب في تغيير اسم التفريع في المستودع البعيد فيمكنك تنفيذ الأمر: git push origin serverfix:awesomebranch لدفع بيانات تفريع serverfix المحلي إلى التفريع awesomebranch على الخادوم البعيد. لا تعد كتابة كلمة السر في كل مرة! يطلُب Git عند دفع البيانات إلى مستودع يستخدم روابط مؤمنة بيانات الاستيثاق (اسم المستخدم وكلمة السر) ويظهر محثّ Prompt في سطر الأوامر لتلقي هذه البيانات. تستطيع استخدام تخبئة اعتمادات Credential cache لتفادي إدخال اسم المستخدم وكلمة السر في كل مرة تدفع فيها البيانات. الطريقة الأسهل لإعدادها هي تنفيذ أمر: git config --global credential.helper cache يحصُل مشاركوك في المستودع عند تنفيذ أمر git fetch مستقبلا على مرجع لمكان نسخة التفريع serverfix الموجودة على الخادوم: git fetch origin remote: Counting objects: 7, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 3 (delta 0) Unpacking objects: 100% (3/3), done. From https://github.com/schacon/simplegit * [new branch] serverfix -> origin/serverfix من المهم الانتباه إلى أن تفريعات التتبع عن بعد التي تنتج عن تنفيذ أمر git fetch لا تعطيك تلقائيا نسخة يمكن تحريرها من هذه التفريعات. يعني هذا بعبارة أخرى أن الأمر أعلاه لا ينشئ تفريع serverfix جديدا بل مؤشرا باسم origin/serverfix لا يمكن تعديله. نفذ أمر: git merge origin/serverfix لدمج التفريع البعيد في التفريع الحالي لديك. إن كنت تريد تفريع serverfix خاصا بك للعمل عليه فيمكنك تأسيسه على تفريع التتبع عن بعد: git checkout -b serverfix origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch 'serverfix' يعطيك الأمر أعلاه تفريعا محليا يبدأ من حيث يوجد origin/serverfix ويمكنك العمل عليه. تفريعات التتبع Tracking branches يؤدي فحصُ تفريع محلي من تفريع تتبع عن بعد تلقائيا إلى إنشاء ما يُسمّى تفريع تتبع. تفريعات التتبع هي تفريعات محلية ذات علاقة مباشرة مع مستودع بعيد (يُسمَّى التفريع البعيد بالتفريع العلوي Upstream branch). إذا كنت تعمل على تفريع تتبع ثم نفذت أمر git pull فإن Git سيعرف تلقائيا من أين سيجلب البيانات والتفريعَ الذي يجب دمجها فيه. ينتج عادة عن نسخ مستودع إنشاء تفريع master لتتبع origin/master. يمكنك إن أردت إعداد تفريعات تتبع أخرى؛ أو التخلي عن تتبع التفريع master الذي أُعدّ تلقائيا أثناء نسخ المستودع. أقرب مثال لإنشاء تفريعات تتبع هو تنفيذ الأمر: git checkout -b [branch] [remotename]/[branch] هذا الأمر شائع لدرجة أن Git لديه اختصار للأمر: git checkout --track origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch 'serverfix' في الواقع، الأمر شائع لدرجة وجود اختصار للاختصار أعلاه! ينشئ Git عند تنفيذ أمر git checkout تفريع تتبع إذا توفر الشرطان: (أ) التفريع غير موجود سلفا، (ب) يوافق اسم التفريع بالضبط تفريعا بعيدا وحيدا: git checkout serverfix Branch serverfix set up to track remote branch serverfix from origin. Switched to a new branch 'serverfix' تتاح أيضا إمكانية إعداد تفريع محلي باسم مختلف عن التفريع البعيد باستخدام النسخة الأولى من الأمر مع ذكر اسم التفريع المحلي على النحو التالي: git checkout -b sf origin/serverfix Branch sf set up to track remote branch serverfix from origin. Switched to a new branch 'sf' بهذا يجلب تنفيذ الأمر git pull على التفريع sf البيانات تلقائيا من origin/serverfix. استخدم خيار u- أو set-upstream-- إذا كنت تريد إعداد تفريع محلي تعمل عليه لتتبع تفريع بعيد أو تغيير التفريع العلوي الذي يتتبّعه التفريع المحلي: git branch -u origin/serverfix Branch serverfix set up to track remote branch serverfix from origin. ملحوظة: اختصار التفريع العلوي. تستطيع بعد إعداد تفريع تتبع استخدام الاختصار {upstream}@ أو {u}@ للإشارة إلى التفريع العلوي المرتبط به. نفترض أنك تعمل على التفريع master الذي يتتبع التفريع origin/master؛ يمكنك في هذه الحالة تنفيذ الأمر المختصر {git merge @{u بدلا من git merge origin/master. استخدم خيار vv- مع أمر git branch لعرض تفريعات التتبع التي أعددتها. يسرد الأمر قائمة بالتفريعات المحلية مع معلومات أكثر عن كل تفريع بما فيها التفريعات العلوية وهل تفريع التتبع يتقدم على التفريع العلوي أم يتأخر عنه أم الاثنان معا. git branch -vv iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets master 1ae2a45 [origin/master] deploying index fix * serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it testing 5ea463a trying something new تشير نتيجة الأمر في المثال أعلاه إلى أن التفريع iss53 يتتبّع التفريع origin/iss53، وأنه يتقدم عليه بإيداعين (ahead 2)؛ بمعنى أنه يوجد إيداعان محليان لم يدفعا بعدُ إلى الخادوم. يظهر أن التفريع master يتتبّع التفريع origin/master وأنه محدَّث. نرى في السطر الموالي أن serverfix يتتبّع server-fix-good على الخادوم teamone وأنه يتقدم عليه بـ3 ويتأخر عنه ب1؛ أي أنه يوجد إيداع واحد على الخادوم لم يُدمج محليَّا بعد، كما توجد ثلاثة إيداعات محلية لم تُدفع إلى الخادوم. يظهر في السطر الأخير أن التفريع testing لا يتتبّع أي تفريع بعيد. يجب الانتباه إلى أن هذه الأعداد تحيل إلى البيانات المخبَّأة منذ آخر مرة بُحث فيها عن بيانات على كل خادوم؛ فالأمر أعلاه لا يدخل في اتصال مع الخواديم بل يخبرك فقط بالمعلومات الموجودة محليا. إن كنت تريد بيانات حديثة كليًّا فيجب البحث عنها أولا على الخواديم جميعا بتنفيذ الأمر git fetch عليها. يمكن مثلا تحديث البيانات من جميع الخواديم كالتالي: git fetch --all; git branch -vv جلب البيانات ينزّل أمر git fetch كل التغييرات الموجودة على الخادوم التي لا توجد عندك محليا؛ ولكنها لا تغير مجلد العمل بتاتا، بل تكتفي بتنزيل البيانات وترك مهمة دمجها لك. إلا أنه يوجد أمر git pull الذي ينزّل البيانات ويدمجها في مجلد العمل لديك؛ ينتج عن تنفيذ أمر git pull غالبا تنفيذ أمر git fetch متبوعا مباشرةً بأمر git merge. يؤدي تنفيذ git pull على تفريع تتبع مُعدّ مثل ما وضحنا في الفقرة السابقة، إما بإعداده صراحة أو عن طريق أمر git clone أو git checkout، يؤدي إلى البحث في المستودع العلوي المناسب عن البيانات الجديدة ثم دمجها فورا في تفريع التتبع المحلي. من الأفضل عموما استخدام أمري: git fetch و git merge بالتتابع إذ أن أمر git pull قد يكون مربكا. حذف التفريعات البعيدة لنفترض أنك أنهيت العمل على تفريع بعيد (أكملت أنت ومشاركوك العمل على ميزة وأُدمجت في التفريع الرئيس الذي توجد به الشفرة المستقرة مهما كان اسمه، master أو أي شيء آخر)؛ يمكنك حذف هذا التفريع باستخدام الخيار delete-- في أمر git push. نفذ الأمر التالي إذا أردت حذف التفريع serverfix من الخادوم: git push origin --delete serverfix To https://github.com/schacon/simplegit - [deleted] serverfix يتلخص عمل الأمر في حذف مؤشر التفريع من الخادوم. يحتفظ خادوم Git عادة ببقية البيانات لمدة إلى أن تتم عملية جمع النفايات Garbage collection؛ تتيح هذه الطرقة إمكانية إرجاع التفريع إن كان حذفه عرضيا. ترجمة -بتصرف- للفصل Git Branching - Remote Branches من كتاب Pro Git لصاحبه Scott Chacon.
-
تعرفنا في الدرسين السابقين على كيفية إنشاء التفريعات (Branches) في Git و كيفية دمجها وحذفها. سنرى في هذا الدرس أدوات لإدارة التفريعات وخططا لتسيير أعمال التطوير باستخدام التفريعات في Git. إدارة التفريعات لا ينحصر عمل أمر git branch على إنشاء التفريعات وحذفها، بل يتعدّى ذلك. إن نفّذت الأمر من دون خيارات فستحصُل على قائمة بالتفريعات الحالية: git branch iss53 * master testing لاحظ علامة * أمام تفريع master: تعني هذه العلامة أن التفريع master هو آخر تفريع انتقلت إليه، أي أن المؤشّر HEAD يشير الآن إلى هذا التفريع). يعني هذا أيضا أنك إن أضفت إيداعا الآن فإن تفريع master سيتقدّم إلى الأمام. استخدم خيار v- لعرض آخر إيداع على كل تفريع: git branch -v iss53 93b412c fix javascript issue * master 7a98805 Merge branch 'iss53' testing 782fd34 add scott to the author list in the readmes يمكن أيضا استخدام الخيارين merged-- و no-merged-- لترشيح نتيجة الأمر والإبقاء فقط على التفريعات التي دُمجت أم لا في التفريع الذي توجد عليه الآن: git branch --merged iss53 * master يظهر التفريع iss53 لأنك دمجته سابقا في التفريع الحالي master. في الحالة العامة يمكن حذف التفريعات التي لا تظهر أمامها علامة * في نتيجة الأمر أعلاه إذ يدل ذلك على أن محتواها دُمج سابقا في تفريع آخر (الحالي) وبالتالي فلن تخسر شيئا بحذفها. استخدم الخيار no-merged-- لعرض التفريعات التي تحوي أعمالا لم تُدمج بعد: git branch --no-merged testing تُظهر نتيجة الأمر السابق التفريع الآخر الذي يوجد به محتوى لم يُدمَج بعد. إن جربت حذف هذا التفريع بالأمر: git branch -d فلن ينجح: git branch -d testing error: The branch 'testing' is not fully merged. If you are sure you want to delete it, run 'git branch -D testing'. تظهر رسالة خطأ لتعلمك بوجود محتوى على التفريع testing لم يُدمج بعد. إن أردت رغم ذلك حذف التفريع وبالتالي فقدان المحتوى فيمكنك فرض الحذف باستخدام الخيار D- كما تشير بذلك رسالة المساعدة في نتيجة الأمر السابق. تسيير العمل باستخدام التفريعات تعرفنا على أساسيات التفريع والدمج ولكن كيف يمكننا استخدامها في تسيير عمليات التطوير؟ سنغطي في هذه الفقرة طريقتين يكثر استخدامهما لتسيير الأعمال بالاعتماد على مبادئ التفريع. التفريعات طويلة الأمد تسهّل قاعدة الدمج الثلاثي من إمكانية دمج تفريع في آخر أكثر من مرة في فترة زمنية طويلة. يعني هذا أنه بالإمكان الإبقاء دائما على تفريعات مفتوحة لاستخدامها في أطوار مختلفة من دورة تطوير البرنامج ودمج بعضها في أخرى من حين لآخر. يختار كثير من مستخدمي Git هذه المقاربة القائمة على الحفاظ على الشفرة البرمجية المستقرة - التي صُدّرت لبيئة الإنتاج أو في طريقها إلى ذلك - في التفريع الرئيس. يوجد بالتوازي مع التفريع الرئيس تفريع آخر باسم develop أو next للعمل عليه أو لاختبار الاستقرار. ليس بالضرورة أن يكون هذا التفريع مستقرا دائما، ولكنه حالما يصل إلى حالة استقرار يُدمج في التفريع الرئيس. يُستخدَم التفريع الموازي لتُدمج فيه التفريعات قصيرة الأمد (تفريع لميزة محدّدة) عندما تكون جاهزة والتأكد من استقرار العمل وخلوه من العلل بعد إضافة الميزة الجديدة قبل أن يُدمج في التفريع الرئيس. تُفهم هذه المقاربة بالنظر إلى موقع مؤشر التفريع على الخط الزمني للإيداعات. توجد الإيداعات المستقرة في نقطة أقدم على الخط الزمني، بينما توجد إيداعات التفريعات الجديدة في موقع أحدث. رؤية خطية للتفريع حسب تقدم الاستقرار طريقة أخرى لفهم المقاربة هي النظر إلى التفريعات على أنها مدرَّجات. تنتقل مجموعة إيداعات إلى درجة أعلى (أكثر استقرارا) بعد أن تُختبر. تدرج الإيداعات حسب درجة الاستقرار يمكن استخدام مستويات استقرار متعددة. يوجد في بعض المشاريع تفريع باسم proposed (مُقترَح) أو pu (اختصار لـproposed updates أي تحديثات مقترحة) لتضمين التفريعات التي لم تجهز بعد للدمج في تفريع next أو master. الفكرة هي أن تكون التفريعات على مستويات مختلفة من الاستقرار؛ وعندما تصل إلى مستوى استقرار أعلى تُدمج في التفريع الموالي. ليس من الضروري أن تكون لديك تفريعات متعدّدة ولكن ذلك يساعد غالبا خصوصا في المشروعات المعقّدة أو الكبيرة. تفريعات المواضيع تفيد تفريعات المواضيع، وهي تفريعات قصيرة الأمد تُنشأ لميزة أو عمل محدّد مرتبط بالمشروع، مهما كان حجمه. ليس شائعا استخدام هذه الطريقة في التفريع في نظم إدارة النسخ الأخرى لثقل آليات التفريع والدمج فيها؛ على العكس من Git الذي تُستخدم فيه هذه الطريقة كثيرا. رأينا مثالا على طريقة التفريع هذه عندما أنشأنا التفريعين iss53 و hotfix في درس أساسيات التفريع والدمج: أضفنا بضع إيداعات إلى التفريعين ثم حذفناهما مباشرة بعد دمجهما في التفريع الرئيس. تتيح هذه الطريقة سهولة تغيير السياق تماما وبسرعة؛ نظرا لكون العمل مقسَّمًا حسب مواضيع فإن كل تفريع يحتفظ فقط بالتغييرات المتعلقة بموضوعه. يمكن إبقاء التغييرات في التفريع لدقائق، أيام أو أشهر ثم دمجها عندما تكون جاهزة بغض النظر عن ترتيب إنشاء تفريعات المواضيع أو ترتيب العمل عليها. فلنفترض المثال التالي: كنت تعمل على التفريع الرئيس (master) ، أنشأت تفريعا جديدا لعلة اكتشفتها (iss91) واستمريت في العمل عليها لبعض الوقت ثم أنشأت تفريعا جديدا من التفريع iss91 لتجربة طريقة أخرى في حل العلة (iss91v2)؛ ثم عدت للعمل على التفريع الرئيس وعملت عليه لبعض الوقت ثم أنشأت تفريعا جديدا لتجربة فكرة لست متأكدا من نجاحها (التفريع dumbidea). يبدو سجل التفريع لهذا المثال على النحو التالي فلنفرض أن الحل الثاني للعلة (التفريع iss91v2) هو الأفضل، وأن الفكرة الجديدة نالت إعجاب زملائك في العمل. يمكن الآن التخلي عن التفريع iss91 (مما يعني خسارة الإيداعين C5 وC6) ثم دمج التفريعين المتبقيين في التفريع الرئيس. يصبح سجل الإيداعات على النحو التالي من المهم تذكر أن جميع هذه التفريعات محلية. كل التغييرات التي تفعلها عند إنشاء تفريعات جديدة أو دمج تفريعات موجودة تتم في مستودع Git دون حدوث أي تواصل مع خادوم بعيد. توجد خطط أخرى لتسيير العمل عند التعامل مع المستودعات البعيدة سنعرض لها في مقال لاحق. ترجمة -بتصرف- للفصل Git Branching - Branching Workflows من كتاب Pro Git لصاحبه Scott Chacon.
-
يدعم Git على غرار غالبية أنظمة إدارة النسخ التفريع Branching. يُقصَد بالتفريع الانتقال للعمل على خط تطوير مغاير لخط التطوير الرئيس والاستمرار في العمل على هذا الخط دون تداخل مع الخط الرئيس. يتطلّب التفريع في كثير من أنظمة إدارة النسخ إنشاء نسخة جديدة من مجلد الشفرة المصدرية، وهو أمر مكلّف ويأخذ الكثير من الوقت في المشاريع الكبيرة. أساسيات التفريع نحتاج لفهم آلية التفريع للعودة قليلا إلى الوراء ومراجعة الكيفية التي يخزّن بها Git بياناته. ذكرنا في درس مبادئ Git الأساسية أن البرنامج لا يخزّن البيانات على هيئة مجموعة فروق أو تغييرات؛ لكنه بدلا من ذلك يخزّن متتالية من اللقطات Snapshots. ما يحدُث عند إيداع البيانات هو أن Git يخزّن كائن إيداع Commit object يحوي مؤشرا على لقطة للمحتوى الذي أدرجته. يوجد بهذا الكائن أيضا اسمُ كاتب الإيداع Author وبريده الإلكتروني، الرسالة التي أضفتها مع الإيداع ومؤشرات Pointers على الإيداع أو الإيداعات التي تأتي مباشرة قبل الإيداع الذي يمثّله الكائن المذكور (الإيداع أو الإيداعات السابقة): لا سابق بالنسبة لأول إيداع، سابق واحد لإيداع عاديّ وإيداعات سابقة متعدّدة لإيداع ناتج عن دمج Merge تفريعين أو أكثر. سنفرض أن لدينا ثلاثة ملفات، أضفناها جميعا إلى منطقة الإدراج ثم نفّذنا أمر الإيداع. ينشئ الإدراج جمع تحقق Checksum لكلّ ملف، يخزّن نسخة الملف في مستودع Git (يسمّي Git هذه النسخ بالكتل Blobs) ثم يضيف جموع التحقق إلى منطقة الإدراج: git add README test.rb LICENSE git commit -m 'The initial commit of my project' ينشئ Git عند تنفيذ أمر commit جمع تحقق لكل مجلّد فرعي (المجلّد الجذر فقط في المثال الحالي) ويخزّن كائنات الشجرة في مستودع Git؛ ثم ينشئ بعد ذلك كائن إيداع لديه بيانات وصفية Metadata ومؤشرًا يحيل إلى شجرة جذر المشروع مما يسمح له بإنشاء لقطة من المجلد عند الحاجة. يحوي مستودع Git الآن خمسة كائنات: كتلة واحدة لمحتوى كلٍّ من الملفات الثلاثة، شجرة واحدة تسرُد لائحة بمحتوى المجلّد وتحدّد الكتل التي تخزِّن أسماء الملفات وكائن إيداع يوجد فيه مؤشر إلى جذر الشجرة إضافة لكلّ البيانات الوصفية الخاصة بالإيداع. شجرة البيانات الخاصة بالإيداع إن أجريت تعديلات ثم نفذت أمر commit من جديد فإن الإيداع الجديد سيخزّن مؤشرا على الإيداع الذي سبقه. إيداع والإيداعات السابقة عليه تفريع Git ليس سوى مؤشر على واحد من هذه الإيداعات؛ يتميز هذا المؤشر بكونه قابلا للنقل. يدعى التفريع المبدئي في Git بـmaster. ينشئ Git عند بدء الإيداعات تفريعا يؤشِّر على آخر إيداع؛ وفي كلّ مرة تضيف إيداعا جديدا ينتقل المؤشر تلقائيا إليه. ملحوظة: تفريع master ليس تفريعًا خاصًّا فهو مماثل لأي تفريع آخر في Git. يعود السبب في كون كلّ مستودعات Git تقريبا تحوي تفريعًا بهذا الاسم إلى أنّ أمر git init ينشئ مبدئيا تفريعا بهذا الاسم، والكثيرون لا يكلّفون أنفسهم عناء تغييره. تفريع وسجل الإيداعات الخاصة به إنشاء تفريع جديد في Git مالذي يحدث بالضبط عندما تنشئ تفريعا جديدا؟ يعني هذا أنك تنشئ مؤشرا جديدا للتنقل به. فلنفترض أننا أنشأنا تفريعا جديدا باسم testing باستخدام أمر git branch التالي: git branch testing ينشئ الأمر مؤشرا جديدا يحيل إلى نفس الإيداع الذي توجد عليه. أي أن لدينا مؤشرين يحيلان إلى نفس المتتالية من الإيداعات. تفريعان يشيران إلى نفس متتالية الإيداعات كيف يعرف Git التفريع الذي توجد عليه الآن؟ يحتفظ Git لهذا الغرض بمؤشر خاص يُسمّى HEAD (المقدّمة). ينبغي الانتباه إلى أن HEAD في Git مختلف تماما عنه في أنظمة إدارة نسخ أخرى مثل Subversion و CVS. يحيل HEAD إلى التفريع المحلي الذي تعمل عليه الآن. في المثال أعلاه فنحن لا زلنا على التفريع master حتى بعد إنشاء تفريع testing الجديد؛ فأمر git branch ينشئ تفريعا جديدا ولكنّه لا ينقُل إلى التفريع الجديد. مؤشر HEAD يشير إلى تفريع master تسهُل رؤية هذا الأمر بتنفيذ أمر git log الذي يعرض عند تحديد الخيار decorate-- الإيداعات التي تحيل إليها مؤشرات التفريعات: git log --oneline --decorate f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface 34ac2 Fixed bug #1328 - stack overflow under certain conditions 98ca9 The initial commit of my project يظهر اسما التفريعين master وtesting بجانب الإيداع f30ab. التبديل بين التفريعات يُستخدم أمر git checkout للانتقال إلى تفريع وبدء العمل عليه. ننفذ الأمر التالي للانتقال إلى التفريع testing الذي أنشأناه للتو: git checkout testing ينقل أمر git checkout أعلاه مؤشرَ HEAD إلى تفريع testing. يشير HEAD الآن إلى تفريع testing ما دلالة نقل مؤشّر HEAD؟ سنضيف إيداعا جديدا وسنرى: vim test.rb git commit -a -m 'made a change' يتقدم مؤشر HEAD إلى الإيداع الأخير في التفريع ينتقل مؤشر HEAD بتنفيذ الأمر commit. أي أن تفريع testing تقدم بإيداع بينما لا زال تفريع master على ما كان عليه عند تنفيذ أمر الانتقال git checkout. نعود إلى التفريع الرئيس master بتنفيذ الأمر التالي: git checkout master ينتقل مؤشر HEAD إلى التفريع الرئيس ينقل الأمر السابق مؤشر HEAD ليحيل إلى التفريع الرئيس ثم يرجع الملفات الموجودة في مجلد العمل إلى اللقطة التي يشير إليها التفريع master. يعني هذا أيضا أن التعديلات من الآن فصاعدا ستكون على نسخة قديمة من المشروع. يبدو الأمر كما لو أنك تراجعت عن التعديلات التي أجريتها بعد الانتقال إلى تفريع testing؛ وبدأت في تغييرات جديدة. ملحوظة: الانتقال إلى تفريع يغيّر ملفات مجلد العمل. ينبغي الانتباه إلى أن الانتقال إلى تفريع يؤدي إلى تغير الملفات الموجودة في مجلد العمل. إن انتقلت إلى تفريع قديم فسيعود محتوى مجلد العمل إلى ما كان عليه بعد آخر إيداع على هذا التفريع. إن لم يستطع Gitفعل ذلك فلن يسمح لك بتاتا بالانتقال إلى التفريع الجديد نعدّل على أحد الملفات ثم نضيف إيداعا جديدا: vim test.rb git commit -a -m 'made other changes' للتذكير نحن نعمل على التفريع master. بالإيداع أعلاه يبدأ سجلّ التفريعين بالتباعد كما هو موضح في الشكل أدناه. أنشأنا تفريعا جديدا وانتقلنا للعمل عليه، أجرينا بضعة تغييرات ثم عدنا من جديد للعمل على التفريع الرئيس. كل من هذه التغييرات معزول عن الآخر في تفريع مختلف: يمكن العمل على تفريع، ثم الانتقال إلى تفريع آخر والعمل عليه ثم العودة إلى التفريع الأول والعمل عليه أيضا؛ وعندما تكون جاهزا يمكن أن تدمج الاثنين. يؤدّى كل هذا العمل بسهولة بالأوامر checkout ،branch وcommit. تباعد التفريعات عن بعضها يمكن استخدام الأمر التالي لعرض سجل التغييرات على شكل مخطّط يوضّح إلى أين تحيل مؤشرات التفريعات وكيف تباعدت عن بعضها: git log --oneline --decorate --graph --all مثال على النتيجة: * c2b9e (HEAD, master) made other changes | * 87ab2 (testing) made a change |/ * f30ab add feature #32 - ability to add new formats to the * 34ac2 fixed bug #1328 - stack overflow under certain conditions * 98ca9 initial commit of my project يسهُل إنشاء تفريعات ومحوها في Git، إذ لا يتطلّب ذلك سوى إنشاء ملفّ من مجموع تحقق ذي 40 محرفا يمثّل الإيداع الذي يحيل إليه مؤشّر التفريع. يختلف Git عن نظم إدارة نسخ أخرى يتطلب التفريع فيها نسخ جميع ملفات المشروع إلى مجلد ثان ممّا يدوم ثواني عدّة وأحيانا دقائق حسب حجم المشروع؛ بينما يكاد يكون الأمر في Git لحظيا. زيادة على ذلك فإن تخزين سوابق الإيداع تجعل من العثور على قاعدة مناسبة لدمج تفريعين أسهل وفي كثير من الأحيان تلقائيا. تشجّع هذه الميزة التي سنتطرّق إليها في المقال التالي المطوّرين على إنشاء التفريعات واستخدامها أكثر. ترجمة -بتصرف- للفصل Git Branching - Branches in a Nutshell من كتاب Pro Git لصاحبه Scott Chacon.