واحدة من التحذيرات الأولى التي يتلقاها شاب باداوان مع إمكانية الوصول إلى مستودعات git هي: "لا
git push -f". نظرًا لأن هذه واحدة من مئات المبادئ التي يحتاج مهندس البرمجيات المبتدئ إلى تعلمها ، فلا أحد يأخذ الوقت الكافي لتوضيح سبب عدم القيام بذلك. إنه مثل الأطفال والنار: "أعواد الثقاب ليست ألعابًا للأطفال" وهذا كل شيء. لكننا ننمو ونتطور كأشخاص وكمحترفين ، ويومًا ما كان السؤال "لماذا ، في الواقع؟ يرتفع في النمو الكامل. تمت كتابة هذه المقالة بناءً على لقائنا الداخلي ، حول موضوع: "متى يمكن ويجب عليك إعادة كتابة تاريخ الالتزامات."
لقد سمعت أن القدرة على الإجابة على هذا السؤال في مقابلة في بعض الشركات معيار لإجراء المقابلات للمناصب العليا. ولكن لفهم الإجابة عليها بشكل أفضل ، تحتاج إلى معرفة سبب كون إعادة كتابة التاريخ أمرًا سيئًا على الإطلاق؟
للقيام بذلك ، نحتاج بدورنا إلى رحلة سريعة إلى الهيكل المادي لمستودع git. إذا كنت متأكدًا من أنك تعرف كل شيء عن جهاز الريبو ، فيمكنك تخطي هذا الجزء ، ولكن حتى أثناء عملية الاكتشاف ، تعلمت الكثير من الأشياء الجديدة بنفسي ، واتضح أن شيئًا قديمًا غير مناسب تمامًا.
في أدنى مستوى ، git repo عبارة عن مجموعة من الكائنات والمؤشرات لها. كل كائن له تجزئة فريدة مكونة من 40 حرفًا (20 بايت سداسي عشري) ، والتي يتم حسابها بناءً على محتويات الكائن.
رسم توضيحي مأخوذ من كتاب Git Community Book
أنواع الكائنات الرئيسية هي blob (فقط محتويات الملف) ، والشجرة (مجموعة من المؤشرات للنقاط والأشجار الأخرى) ، والتنفيذ. كائن من نوع الالتزام هو فقط مؤشر لشجرة ، إلى الالتزام السابق ، ومعلومات الخدمة: التاريخ / الوقت ، المؤلف والتعليق.
أين الفروع والعلامات التي اعتدنا العمل بها؟ وهي ليست كائنات ، إنها مجرد مؤشرات: يشير الفرع إلى آخر التزام فيه ، وتشير العلامة إلى ارتكاب تعسفي في الريبو. أي عندما نرى فروعًا مرسومة بشكل جميل مع دوائر الالتزام عليها في IDE أو عميل واجهة المستخدم الرسومية ، فإنها تُبنى بسرعة كبيرة ، وتعمل على طول سلاسل الالتزام من نهايات الفروع وصولاً إلى "الجذر". الالتزام الأول في الريبو ليس له أي سابق ، بدلاً من المؤشر هناك فارغ.
نقطة مهمة يجب فهمها: يمكن أن يظهر الالتزام نفسه في عدة فروع في نفس الوقت. لا يتم نسخ الالتزامات عند إنشاء فرع جديد ، بل يبدأ فقط في "النمو" من حيث كان HEAD عند إصدار الأمر
git checkout -b <branch-name>.
فلماذا تكون إعادة كتابة تاريخ المستودع ضارة؟
أولاً ، وهذا واضح ، عندما تقوم بتحميل قصة جديدة إلى المستودع الذي يعمل معه الفريق الهندسي ، فقد يفقد الأشخاص الآخرون تغييراتهم.
git push -f يزيل الأمر من الفرع الموجود على الخادم جميع الالتزامات غير الموجودة في الإصدار المحلي ، ويكتب أوامر جديدة.
لسبب ما ، قلة من الناس يعرفون أن الفريق
git pushلديه مفتاح "آمن" لفترة طويلة--force-with-leaseمما يؤدي إلى فشل الأمر إذا تمت إضافة التزامات بواسطة مستخدمين آخرين إلى المستودع البعيد. أنا أوصي دائمًا باستخدامه بدلاً من ذلك -f/--force.
السبب الثاني
git push -fلاعتبار الأمر ضارًا هو أنه عند محاولة دمج فرع مع تاريخ معاد كتابته مع الفروع التي تم الاحتفاظ بها (بتعبير أدق ، تم حفظ الالتزامات التي تمت إزالتها من السجل المعاد كتابته) ، سنحصل على عدد من التعارضات (حسب الرقم) يرتكب ، في الواقع). هناك إجابة بسيطة لهذا: إذا اتبعت بعناية Gitflow أو Gitlab Flow ، فلن تظهر مثل هذه المواقف على الأرجح.
وأخيرًا ، هناك جانب غير سار لإعادة كتابة التاريخ: تلك الالتزامات التي تمت إزالتها ، كما كانت ، من الفرع ، في الواقع ، لا تختفي في أي مكان وتبقى معلقة إلى الأبد في الريبو. تافه ، لكنها غير سارة. لحسن الحظ ، عالج مطورو git هذه المشكلة أيضًا باستخدام أمر جمع البيانات المهملة
git gc --prune. يقوم معظم مضيفي git ، على الأقل GitHub و GitLab ، بعمل ذلك في الخلفية من وقت لآخر.
لذا ، بعد أن بددنا مخاوف تغيير تاريخ المستودع ، يمكننا أخيرًا الانتقال إلى السؤال الرئيسي: لماذا نحتاج إليه ومتى يكون مبررًا؟
في الواقع ، أنا متأكد من أن كل مستخدم أكثر أو أقل نشاطًا قد غيّر السجل مرة واحدة على الأقل ، عندما اتضح فجأة أن شيئًا ما قد حدث خطأ في الالتزام الأخير: تسلل خطأ مطبعي مزعج إلى الشفرة ، وارتكب التزامًا ليس من ذلك مستخدم (من البريد الإلكتروني الشخصي بدلاً من العمل أو العكس) ، نسيت إضافة ملف جديد (إذا كنت ، مثلي ، ترغب في استخدامه
git commit -a). حتى تغيير وصف الالتزام يؤدي إلى الحاجة إلى إعادة كتابته ، لأنه يتم احتساب التجزئة من الوصف أيضًا!
لكن هذه حالة تافهة. دعونا نلقي نظرة على أكثر إثارة للاهتمام.
لنفترض أنك أنشأت ميزة كبيرة رأيتها لعدة أيام ، فأرسلت نتائج العمل اليومية إلى المستودع على الخادم (4-5 عقود) ، وأرسلت تغييراتك للمراجعة. قام اثنان أو ثلاثة من المراجعين الدؤوبين بإغراقك بتوصيات كبيرة وصغيرة لإجراء تعديلات ، أو حتى العثور على عضادات (4-5 عمليات التزام أخرى). ثم عثرت QA على العديد من حالات الحافة التي تتطلب أيضًا إصلاحات (2-3 عمليات إضافية). وأخيرًا ، أثناء الدمج ، تم اكتشاف بعض حالات عدم التوافق أو تم إجراء اختبارات تلقائية ، والتي تحتاج أيضًا إلى الإصلاح.
إذا ضغطت الآن على زر الدمج دون النظر ، فحينئذٍ تلتزم دزينة ونصف مثل "ميزتي ، اليوم الأول" ، "اليوم الثاني" ، "إصلاح الاختبارات" ، "إصلاح المراجعة" إلخ هذا ، بالطبع ، يساعد وضع الاسكواش ، الموجود الآن في كل من GitHub و GitLab ، ولكن عليك أن تكون حذرًا معه: أولاً ، يمكن استبدال وصف الالتزام بشيء لا يمكن التنبؤ به ، وثانيًا ، استبدال مؤلف الميزة على الشخص الذي ضغط على زر الدمج (في بلدنا ، هذا بشكل عام روبوت يساعد مهندس الإصدار في بناء نشر اليوم). لذلك ، سيكون أبسط شيء ، قبل الدمج النهائي في الإصدار ، هو طي جميع ارتباطات الفرع إلى واحدة تستخدم
git rebase.
ولكن يحدث أيضًا أنك قد اقتربت بالفعل من مراجعة الكود مع تاريخ إعادة الشراء الذي يذكرنا بسلطة أوليفر. يحدث هذا إذا تم نشر الميزة لعدة أسابيع ، لأنها كانت ضعيفة التحلل ، أو على الرغم من تعرض الفرق المحترمة للضرب بشمعدان لهذا الغرض ، فقد تغيرت المتطلبات أثناء عملية التطوير. على سبيل المثال ، إليك طلب دمج حقيقي أتى إليّ للمراجعة قبل أسبوعين:
وصلت يدي تلقائيًا إلى زر "الإبلاغ عن إساءة استخدام" ، لأنه بخلاف ذلك يمكنك وصف طلب بـ 50 التزامًا مع ما يقرب من 2000 سطر متغير؟ وكيف يتساءل المرء أن يراجعها؟
بصراحة ، استغرق الأمر يومين فقط لإجبار نفسي على بدء هذه المراجعة. وهذا رد فعل طبيعي للمهندس. شخص ما في موقف مشابه ، فقط دون النظر ، يضغط على "موافقة" ، مدركًا أنه في غضون فترة زمنية معقولة لن يكون قادرًا على القيام بمهمة مراجعة هذا التغيير بجودة كافية.
ولكن هناك طريقة لجعل الحياة أسهل بالنسبة لصديق. بالإضافة إلى العمل التمهيدي على تحليل أفضل للمشكلة ، بعد الانتهاء من كتابة الكود الرئيسي ، يمكنك تحويل تاريخ كتابتها إلى شكل أكثر منطقية ، وتقسيمها إلى التزامات ذرية مع اختبارات خضراء في كل منها: "إنشاء خدمة جديدة وطبقة نقل لها" ، "بناء النماذج وكتابتها فحص الثوابت "،" إضافة التحقق من الصحة ومعالجة الاستثناءات "،" كتب الاختبارات ".
يمكن مراجعة كل من هذه الالتزامات بشكل منفصل (يمكن لكل من GitHub و GitLab القيام بذلك) والقيام بذلك في غارات عند التبديل بين المهام أو في فترات الراحة.
سيساعدنا نفس
git rebaseالمفتاح الذي يحتوي على المفتاح على القيام بكل هذا --interactive. كمعامل ، تحتاج إلى تمرير تجزئة الالتزام ، والتي ستحتاج منها إلى إعادة كتابة السجل. إذا كنا نتحدث عن آخر 50 التزامًا ، كما في المثال الموضح في الصورة ، فيمكنك الكتابة git rebase --interactive HEAD~50(استبدل رقمك بـ "50").
بالمناسبة ، إذا أضفت الفرع الرئيسي لنفسك في عملية العمل على مهمة ، فستحتاج أولاً إلى إعادة تحديد هذا الفرع حتى لا يتم الخلط بين التزامات الدمج والالتزامات من السيد تحت قدميك.
مسلحًا بمعرفة العناصر الداخلية لمستودع git ، يجب أن يكون من السهل فهم كيفية عمل تغيير الأساس على المستوى الرئيسي. يأخذ هذا الأمر جميع الالتزامات في فرعنا ويغير أصل أول واحد إلى آخر التزام في الفرع الرئيسي. انظر الرسم التخطيطي:
الرسوم التوضيحية مأخوذة من كتاب Pro Git
إذا كانت التغييرات في C4 و C3 تعارض ، فبعد حل التعارضات ، سيغير الالتزام C4 محتواه ، لذلك يتم إعادة تسميته في الرسم التخطيطي الثاني إلى C4 '.
بهذه الطريقة ، ستنتهي بفرع يتألف فقط من تغييراتك وينمو من أعلى السيد. بالطبع ، يجب أن يكون المعلم محدثًا. يمكنك فقط استخدام الإصدار من الخادم:
git pull --rebase origin/master(كما تعلم ، git pullمكافئ git fetch && git merge، --rebaseوسيفرض المفتاح git على إعادة التأسيس بدلاً من الدمج).
دعنا نعود أخيرًا إلى
git rebase --interactive... تم صنعه من قبل المبرمجين للمبرمجين ، وإدراكًا منا للتوتر الذي سيواجهه الناس في هذه العملية ، حاولنا الحفاظ على أعصاب المستخدم قدر الإمكان وإنقاذه من الحاجة إلى الإجهاد المفرط. هذا ما ستراه على الشاشة:
هذا هو مستودع حزمة Guzzle الشهيرة. يبدو أنه يمكنه استخدام قاعدة جديدة ...
يفتح الملف الذي تم إنشاؤه في محرر نصي. ستجد أدناه معلومات مفصلة حول ما يجب القيام به هنا. بعد ذلك ، في وضع التحرير السهل ، عليك أن تقرر ما ستفعله بالالتزامات في فرعك. كل شيء بسيط مثل العصا: اختر - اتركه كما هو ، أعد صياغة - قم بتغيير وصف الالتزام ، اسكواش - اندمج مع السابق (تعمل العملية من الأسفل إلى الأعلى ، أي السابقة هي السطر أدناه) ، قم بإسقاط - حذف تمامًا ، تحرير - وهذا هو الشيء المثير للاهتمام هو التوقف والتجميد. بعد أن يواجه git أمر التحرير ، سيأخذ الموضع الذي تمت فيه بالفعل إضافة التغييرات في الالتزام إلى الوضع التدريجي. يمكنك تغيير أي شيء في هذا الالتزام ، وإضافة القليل فوقه ، ثم الأمر
git rebase --continueلمتابعة عملية تغيير الأساس.
أوه ، وبالمناسبة ، يمكنك مبادلة الالتزامات. قد يؤدي هذا إلى حدوث تعارضات ، ولكن بشكل عام ، نادرًا ما تكون عملية تغيير العنوان الأساسي خالية تمامًا من التعارض. كما يقولون ، بعد أن خلعوا رؤوسهم ، لا يبكون على شعرهم.
إذا شعرت بالارتباك وبدا أن كل شيء قد انتهى ، فلديك زر طرد للطوارئ
git rebase --abortسيعيد كل شيء إليه على الفور.
يمكنك إعادة وضع الأسس عدة مرات ، ولمس سوى أجزاء من القصة ، وترك الباقي كما هو ، مما يمنح قصتك مظهرًا مكتملًا أكثر فأكثر ، مثل إبريق الخزاف. من الممارسات الجيدة ، كما كتبت أعلاه ، التأكد من أن الاختبارات في كل التزام ستكون خضراء (لهذا ، يساعد التحرير بشكل مثالي وفي التمريرة التالية - الاسكواش).
الأكروبات الأخرى ، مفيدة في حال احتجت إلى تحليل عدة تغييرات في نفس الملف إلى التزامات مختلفة -
git add --patch. يمكن أن يكون مفيدًا من تلقاء نفسه ، ولكن بالاقتران مع توجيه التحرير ، سيسمح لك بتقسيم التزام واحد إلى عدة ، والقيام بذلك على مستوى الأسطر الفردية ، والتي ، إذا لم أكن مخطئًا ، فلا يوجد عميل واجهة مستخدم رسومية ولا IDE لا يسمح بذلك.
مما يجعل مرة أخرى من أن كل شيء في محله، يمكنك أخيرا مع راحة البال لعمل شيء ما، ما بدأ هذا البرنامج التعليمي:
git push --force. أوه ، هذا بالطبع --force-with-lease!
في البداية ، من المرجح أن تقضي ساعة في هذه العملية (بما في ذلك تغيير الأساس الأولي على المستوى الرئيسي) ، أو حتى ساعتين إذا كانت الميزة مترامية الأطراف حقًا. ولكن حتى هذا أفضل بكثير من الانتظار يومين حتى يجبر المراجع نفسه على قبول طلبك أخيرًا ، ويومين آخرين حتى يتفاهى به. في المستقبل ، من المرجح أن تستغرق 30-40 دقيقة. تعتبر منتجات IntelliJ المزودة بأداة مدمجة لحل النزاعات (الإفصاح الكامل: تدفع FunCorp لموظفيها مقابل هذه المنتجات) مفيدة بشكل خاص في ذلك.
آخر شيء أريد تحذيرك منه هو عدم إعادة كتابة تاريخ الفرع أثناء مراجعة الكود. تذكر أن المراجع الضميري يمكنه استنساخ الكود محليًا حتى يتمكن من الاطلاع عليها من خلال IDE وإجراء الاختبارات.
شكرا لكل من قرأ حتى النهاية! آمل أن تكون هذه المقالة مفيدة ليس لك فقط ، ولكن أيضًا للزملاء الذين يتلقون الكود الخاص بك للمراجعة. إذا كان لديك بعض الاختراقات الرائعة git - شاركها في التعليقات!