حتى الآن، جرى كل العمل على اللعبة في حاسوب محمول واحد. في المختبر، يعمل شريككم على اللعبة نفسها من حاسوب محمول آخر. وهذا ليس مجرد ترتيب خاص بالمختبر. في كل قاعدة كود تقريبًا ستعملون عليها، يعمل عليها عدة أشخاص، كلّ منهم على حاسوبه الخاص. في هذا الفيديو نرى كيف يُبقي Git نسخهم متزامنة، عبر مستودع بعيد (remote) على خادم مثل GitHub. ثم نشارك ما سجّلناه من commits في لعبتنا، ونجمع التغييرات عندما يكون لدى كلّ منكما شيء جديد.
في المشاريع الحقيقية، يعمل عدة أشخاص على قاعدة كود واحدة. لدى كلّ منهم نسخة كاملة من المستودع على حاسوبه، ويسجّل فيها عمله في commits. ومع كل commit جديد، تبتعد النسخ بعضها عن بعض. كل حاسوب فيه عمل لم يره الآخرون.
لتوحيد النسخ، يستخدم Git مستودعًا بعيدًا (remote). وهو نسخة إضافية من المستودع، على خادم يصل إليه الجميع، عادةً GitHub. كل حاسوب يتبادل الـ commits مع الـ remote، ولا يتبادلها أبدًا مباشرةً مع حاسوب آخر.
لذا فللعمل مع الآخرين خطوات ثابتة. شاركوا الـ commits الخاصة بكم عبر الـ remote، ونزّلوا ما شاركه الآخرون. في هذا الفيديو نفعل ذلك مع شريك واحد، على لعبتنا. والخطوات نفسها تصلح لفريق كامل.
اللعبة موجودة على GitHub، وقد دُمجت في main ميزات الإيقاف المؤقت، والوضع الداكن، وإعادة التشغيل. يشغّل وكيل شريككم git clone مع عنوان المستودع. يستنسخ الأمر clone المستودع كله، بكل ملفاته وسجلّه الكامل، إلى حاسوب شريككم. المستودع على GitHub، الذي يتبادل معه الحاسوبان العمل، يُسمّى remote. كل حاسوب يتواصل مع الـ remote، ولا يتواصل أبدًا مع الحاسوب الآخر مباشرةً.
يطلب شريككم من وكيله صوتًا عندما تزيد النقاط. يبني الوكيل الميزة في فرع ميزة، فيراجعها شريككم، ثم تُدمج في main لديه. هذا الـ commit موجود على حاسوبه فقط. إنشاء commit لا يرسل شيئًا إلى GitHub، ولا يغيّر شيئًا على حاسوبكم.
يشغّل وكيله الآن git push، الذي يرسل الـ commits الجديدة إلى الـ remote. صار لدى GitHub الآن commit الصوت. أما حاسوبكم فما زالت عليه النسخة السابقة. يجعل push العمل متاحًا على الـ remote. لكنه لا يغيّر أيّ ملف على أيّ من الحاسوبين.
في الأثناء، تطلبون من وكيلكم حفظ أعلى نتيجة وعرضها بجانب النتيجة الحالية. تُراجَع الميزة وتُدمج في main لديكم، بالطريقة نفسها. الـ commit لديكم والـ commit لدى شريككم بدأ كلاهما من النسخة السابقة نفسها. وكلّ منهما يحتوي على عمل ليس في الآخر.
يشغّل وكيلكم الآن git push، فيرفض Git. تقول الرسالة إن الـ remote يحتوي على عمل ليس لديكم. الفرع main لديكم لا يتضمّن commit الصوت من شريككم، ولن يحذفه Git. يجب الجمع بين السجلّين قبل مشاركة النتيجة.
تطلبون من الوكيل ضمّ عمل شريككم عن طريق الدمج، مع الإبقاء على الميزتين. والأمر المناسب هو git pull، مع الخيار --no-rebase، الذي يطلب من Git الجمع بين العملين عن طريق الدمج. يقوم pull بخطوتين متتاليتين، وسنشاهدهما واحدة تلو الأخرى.
يشغّل الوكيل الآن git pull. الخطوة الأولى هي التنزيل. يستقبل مستودعكم commit الصوت، والسجلّ الذي يحتاج إليه. ما زال main لديكم يشير إلى commit أعلى نتيجة، وملفاتكم لم تتغيّر بعد. وجود commit في المستودع لا يعني وجود تغييراته في ملفاتكم.
الخطوة الثانية هي الدمج. يدمج Git في فرعكم العمل الذي نُزِّل، تمامًا كما دمج الميزتين المحليّتين من قبل. يجمع الدمج بين تغيير أعلى نتيجة وتغيير الصوت. وللـ commit الجديد أبوان: commit أعلى نتيجة وcommit الصوت. إذا تعارض التغييران، يتوقّف Git كما رأينا من قبل. أخبروا الوكيل كيف يجب أن تعمل النتيجة، واقرؤوا حلّه قبل إتمام الدمج.
تحقّقوا من اللعبة المدمَجة قبل مشاركتها. يجب أن يبقى الصوت عند تسجيل النقاط، وأن تبقى أعلى نتيجة بعد إعادة التحميل. ويشغّل الوكيل أيضًا node --test. قد ينتهي الدمج دون أيّ تعارض، ويترك مع ذلك برنامجًا يحتاج إلى إصلاح.
يشغّل وكيلكم git push مرة أخرى، وهذه المرة ينجح، لأن main لديكم يحتوي على كل ما في الـ remote. يستقبل GitHub السجلّ المدمَج، وفيه أعلى نتيجة من عملكم والصوت من عمل شريككم. حاسوب شريككم لم يتغيّر بعد.
يشغّل وكيل شريككم الآن git pull، فيستقبل حاسوبه النسخة المدمَجة. يتقدّم main عبر fast-forward. commit الدمج لديكم يتضمّن بالفعل commit الصوت من شريككم، لذا ينتقل main لديه إليه ببساطة، دون commit دمج ثانٍ.
الـ commit نفسه موجود الآن في المستودعات الثلاثة. وهي متطابقة لأن كلًّا منكما نفّذ push وpull، لا لأن Git يُبقي الحواسيب متزامنة من تلقاء نفسه. ما لم يُسجَّل في commit ثم يُرسَل بـ push يبقى على حاسوب واحد فقط.
هناك طريقة أخرى لتحديث الميزة بآخر ما في main، وهي rebase. تفضّلها فِرق كثيرة، وربما أنتم أيضًا. لنعد إلى ما قبل الجمع بين الميزتين، ولننظر في طريقة أخرى للقيام بذلك. لنفترض أن تغيير أعلى نتيجة ما زال في فرعه الخاص، بينما يحتوي main بالفعل على commit الصوت. تشغيل git rebase main في ذلك الفرع يعيد تطبيق تغييركم فوق أحدث commit في main. يحصل الفرع على commit جديد يحلّ محلّ القديم. إذا تعارض التغييران، تحلّون التعارض هنا، في فرعكم الخاص، قبل أن يصل أيّ شيء إلى main.
بعد أن يصبح الفرع فوق main، يتقدّم main عبر fast-forward. لا يوجد commit دمج، ويبدو السجلّ خطًّا واحدًا. وهذا هو الهدف من طريقة العمل هذه. وفّقوا بين التغييرات في فرعكم أولًا، ليكون كل دمج في main بسيطًا. أما الدمج الذي أجريناه من قبل، والمعروض هنا أسفل الشريحة، فيحتفظ بكلا الـ commit الأصليَّين، ويُظهر أين التقى الخطّان، ويُحلّ التعارض داخل الدمج نفسه. كلتا الطريقتين جيدة، والاختيار مسألة ذوق. جرّبوا كلًّا منهما في المختبر، وانظروا أيّهما تفضّلون. وقاعدة واحدة للحالتين. لأن rebase يستبدل الـ commits، لا تُجروا rebase أبدًا على commits وصلت إلى الآخرين، وإلا فلن يتطابق سجلّهم مع سجلّكم.
هذه خطواتكم الثابتة في المختبر. لكل ميزة يعمل عليها وكيل فرعٌ ومجلدٌ خاصّان بها. راجعوا العمل وسجّلوه في commit، وادمجوه في main، وتحقّقوا من اللعبة المدمَجة. ثم شاركوه. أرسلوا عملكم بـ push، واجلبوا عمل شريككم بـ pull. لعبتنا فيها الآن خمس ميزات، من حاسوبين، وسجلّ واحد. تبادلوا العمل المكتمل باستمرار، ليقلّ ما يجب جمعه في كل مرة. وعندما يُرفض الـ push، اقرؤوا السبب، واطلبوا من الوكيل أن يشرح ما يجب أن يحدث قبل أن يحاول مرة أخرى.