AICODEAgentic Programming · 67723 · 2026-7

Week 3 · video 2 of 7 · transcript

Separate Features, Then Merge Them

ترجمة آلية لم يراجعها إنسان.AI translation, not reviewed by a person.

0:00

عندما يعمل وكيلان على مشروع في الوقت نفسه، علينا أن نُبقي تغييراتهما غير المكتملة منفصلة. وعلينا أيضًا مراجعة كل ميزة قبل ضمّها إلى العمل المقبول. سنرى كيف تمنح الـ worktrees كل وكيل مجلد عمل منفصلًا، وكيف تتتبّع الفروع الـ commits الخاصة بكل ميزة. ثم سيضيف وكيلان إلى لعبتنا الوضع الداكن ومفتاحًا لإعادة التشغيل. سنراجع كل ميزة وندمجها في الفرع main، أولًا عبر fast-forward، ثم مع تعارض دمج علينا حلّه.

0:29

في فيديو سابق، عمل وكيل واحد في مجلد واحد، وأضاف إلى اللعبة مفتاح الإيقاف المؤقت. وشكّلت الـ commits خطًّا واحدًا في السجلّ، لأننا أنهينا كل مهمة قبل أن نبدأ التالية.

0:41

نريد الآن أن يعمل وكيلان في الوقت نفسه. يحتاج كل وكيل إلى ملفات عمل خاصة به، كي لا تغيّر تعديلاته غير المكتملة الملفاتِ التي يستخدمها الوكيل الآخر. ونحتاج أيضًا إلى تتبّع commits كل ميزة على حدة. فهذا يتيح لنا مراجعة ميزة دون أن يختلط بها عمل الميزة الأخرى.

1:00

نجمع العمل الذي نقبله في الفرع المسمّى main. وتبقى كل ميزة منفصلة حتى نقرّر دمجها فيه. الـ worktree يمنح الوكيل مجلد عمل منفصلًا، والفرع يتتبّع خطًّا من العمل المسجَّل في commits. والدمج يُدخل عمل فرع آخر إلى الفرع الذي نختاره. لنرَ أولًا لماذا تظلّ المهام المنفصلة بحاجة إلى ملفات عمل منفصلة.

1:23

لنفترض أن الوكيلين يعملان في المجلد نفسه، وأن كليهما قرأ النسخة نفسها من ملف اللعبة. يبدأ الوكيل A بإضافة الوضع الداكن، والوكيل B بإضافة ميزة إعادة التشغيل. يكتب الوكيل B كود إعادة التشغيل في الملف المشترك. أما الوكيل A فما زال يعمل على النسخة التي قرأها من قبل، وتلك النسخة لا تحتوي على كود إعادة التشغيل.

1:44

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

2:04

ثم يضيف الوكيل B المفتاح الذي يُفترض أن يستدعي دالة إعادة التشغيل. لكن تلك الدالة اختفت، فصار المفتاح يشير إلى كود غير موجود. ويحتوي الملف أيضًا على تغييرات الوضع الداكن التي لم يُجرِها الوكيل B ولم يحسب حسابها. إعطاء الوكيلين مهام منفصلة لم يمنحهما ملفات منفصلة. فكل وكيل يحتاج إلى مجلد عمل خاص به.

2:27

في فيديو سابق، سمّينا الملفات التي نفتحها ونعدّلها شجرة العمل (working tree). يدعم Git أشجار عمل إضافية في مجلدات منفصلة مرتبطة بالمستودع نفسه. وكل مجلد عمل إضافي منها يُسمّى worktree. نعطي وكيل الوضع الداكن worktree، ووكيل إعادة التشغيل worktree آخر. يعدّل كل وكيل الملفات في مجلده الخاص، فكتابته لملف اللعبة لا تستبدل ملف اللعبة لدى الوكيل الآخر. ويبقى مجلدنا الأصلي دون تغيير، فصار لدينا الآن ثلاثة مجلدات عمل. ونحتاج أيضًا إلى طريقة لتتبّع العمل المسجَّل لكل ميزة.

3:01

الفرع خطّ مسمّى من العمل المسجَّل في commits. ويستخدم Git اسم الفرع للدلالة على أحدث commit فيه. نسمّي أحد الفرعين dark-mode والآخر restart، لنتتبّع عمل كل ميزة على حدة. ونستخدم main للعمل الذي نقبله. في البداية، تشير أسماء الفروع الثلاثة إلى commit واحد، هو commit الإيقاف المؤقت. وإنشاء هذه الفروع لم يُضف أيًّا من الميزتين بعد.

3:25

إنشاء فرع لا يفعل سوى إضافة اسم يشير إلى commit موجود. ولا يُنشئ نسخة أخرى من السجلّ. لكل worktree ملفات عمل ومنطقة staging خاصة به. فيستطيع كل وكيل أن يعدّل ميزته ويضيفها إلى الـ staging باستقلال، بينما تتشارك الـ worktrees الثلاثة مستودعًا واحدًا.

3:44

تحتوي شجرة العمل على الملفات التي يقرؤها الوكيل ويعدّلها ويشغّلها. أما الفرع فيحدّد الـ commit الذي يُبنى عليه هذا العمل. التعديل يغيّر شجرة العمل، أما تسجيل commit فينقل اسم الفرع إلى الـ commit الجديد. أعطوا كل وكيل فرعًا وworktree خاصّين به. سننشئهما قبل أن يبدأ أيّ من الوكيلين بتعديل اللعبة.

4:06

قبل أن يبدأ الوكيل العمل على الوضع الداكن، نطلب منه إنشاء فرع وworktree لهذه الميزة. ونطلب منه أيضًا أن يعمل في ذلك المجلد الجديد.

4:15

يشغّل الوكيل git worktree add، مع الخيار -b لإنشاء الفرع dark-mode. والمسار يحدّد المجلد الجديد، game-dark، بجانب مجلد مشروعنا. والوسيط الأخير، main، يحدّد commit البداية.

4:30

ويُجري الأمر أيضًا checkout للفرع الجديد في ذلك المجلد. أي أنه يضع ملفات الفرع هناك، جاهزة للتعديل.

4:38

تؤكّد المُخرَجات أن الـ worktree الجديد يبدأ من commit الإيقاف المؤقت. وعلى الوكيل أن يعمل في ذلك المجلد كي تبقى تعديلاته منفصلة.

4:46

ونطلب الشيء نفسه لميزة إعادة التشغيل. فيشغّل الوكيل git worktree add مرة أخرى، وينشئ الفرع restart والمجلد game-restart. يبدأ المجلدان الجديدان من commit الإيقاف المؤقت، لأن الفرع main يشير إليه. يستطيع الوكيلان الآن التعديل في الوقت نفسه، وتبقى الملفات في مجلدنا دون تغيير.

5:07

عندما يسجّل وكيل عمله في commit على فرعه، يسجّل Git نسخة جديدة، وينقل اسم ذلك الفرع إلى الـ commit الجديد. أما أسماء الفروع الأخرى فتبقى في مكانها. غيّر وكيل الوضع الداكن ملفين: منطق اللعبة والألوان. وفي الـ worktree الخاص به، يضيفهما إلى الـ staging بالأمر git add، ثم يشغّل git commit مع الرسالة «Add dark mode». يشير الفرع dark-mode الآن إلى الـ commit الجديد، المبنيّ على commit الإيقاف المؤقت. أما الفرعان main وrestart فلم يتحرّكا.

5:38

ويُنهي وكيل إعادة التشغيل عمله أيضًا. وفي الـ worktree الخاص به، يضيف ملف اللعبة إلى الـ staging بالأمر git add، ثم يشغّل git commit مع الرسالة «Add restart control». ينتقل الفرع restart إلى الـ commit الجديد، المبنيّ هو أيضًا على commit الإيقاف المؤقت. ويبقى الفرع dark-mode حيث هو، وما زال main يشير إلى commit الإيقاف المؤقت. بُنيت الميزتان انطلاقًا من نسخة البداية نفسها. وتسجيل ميزة في commit على فرعها لا يضيفها إلى main ولا إلى فرع الميزة الأخرى.

6:09

تستخدم الـ worktrees الثلاثة المستودع نفسه، لذا يمكننا رؤية هذه الـ commits من أيّ مجلد من المجلدات الثلاثة. فالسجلّ مشترك، مع أن ملفات العمل منفصلة. ما زال مجلدنا على main، وفيه الإيقاف المؤقت دون الميزتين الجديدتين. ومجلد إعادة التشغيل فيه الإيقاف المؤقت وإعادة التشغيل، دون الوضع الداكن. رؤية commit في السجلّ لا تُدخل تغييراته إلى المجلد.

6:33

سُجّل commit الوضع الداكن، لكن ما زال علينا مراجعة الميزة قبل قبولها في main. نقارن ملفاتها بالنسخة التي بدأ منها الوكيل. في worktree الوضع الداكن، نشغّل git diff main بأنفسنا ونقرأ المُخرَجات. يقارن الأمر الملفات المتتبَّعة الحالية بالـ commit الذي يدلّ عليه main. هنا، ما زال main يشير إلى commit الإيقاف المؤقت، حيث بدأت هذه الميزة. يُظهر هذا المقتطف إضافة «D: dark mode» إلى نص مفاتيح التحكّم. والتغيير كله يشمل ملفين: منطق اللعبة والألوان. اقرؤوا التغييرات في الملفين قبل تجربة اللعبة.

7:10

ثم شغّلوا اللعبة من worktree الوضع الداكن، كي تختبروا الملفات التي راجعتموها للتوّ. تحقّقوا من أن الألوان تتغيّر، وأن D يبدّل المظهر، وأن P ما زال يوقف اللعبة مؤقتًا. تكون الميزة جاهزة للدمج عندما تكون تغييراتها منطقية، ويطابق سلوكها ما طلبناه. الآن يمكننا إدخال هذا العمل الذي راجعناه إلى main.

7:31

الدمج يُدخل العمل المسجَّل في فرع آخر إلى الفرع الحالي. نريد الوضع الداكن في main، لذا نطلب من الوكيل تنفيذ الدمج في مجلدنا الأصلي، حيث الفرع الحالي هو main. في مخطط السجلّ، ما زال الفرع main يشير إلى commit الإيقاف المؤقت. وقد بُني commit الوضع الداكن مباشرة على ذلك الـ commit، لذا فهو يتضمّن كل ما في main، إضافةً إلى الميزة.

7:56

يشغّل الوكيل git merge dark-mode. وبما أن main لم يُضَف إليه أيّ commit جديد، فإن commit الوضع الداكن يتضمّن بالفعل كل ما في main. ينقل Git الفرع main إلى commit الوضع الداكن الموجود، دون إنشاء commit جديد. يُسمّى هذا fast-forward. ويحدّث Git أيضًا الملفات في مجلدنا، فصارت اللعبة هناك تتضمّن الوضع الداكن. جرّبوا الوضع الداكن والإيقاف المؤقت في مجلدنا بعد الدمج. ميزة إعادة التشغيل ما زالت منفصلة، وسنراجعها بعد ذلك.

8:26

بدأ وكيل إعادة التشغيل من commit الإيقاف المؤقت، لكن الفرع main يشير الآن إلى commit الوضع الداكن. لو قارنّا الفرع restart بالفرع main، لظهرت أيضًا فروق سببها الوضع الداكن. في worktree إعادة التشغيل، نشغّل git diff مع مُعرِّف commit الإيقاف المؤقت الظاهر على الشريحة. وهذا الأمر يقارن الملفات المتتبَّعة الحالية بالنسخة التي بدأت منها ميزة إعادة التشغيل. يضيف الـ diff «R: restart» إلى نص مفاتيح التحكّم، ويعرّف دالة إعادة التشغيل، ويربط المفتاح R بها. شغّلوا اللعبة من هذا الـ worktree. وتحقّقوا من أن R يعيد تشغيل اللعبة، وأن P ما زال يوقفها مؤقتًا ويستأنفها.

9:04

يحتوي الفرعان الآن على عمل مختلف. في الفرع main الوضع الداكن دون إعادة التشغيل، وفي restart إعادة التشغيل دون الوضع الداكن. نقلُ main إلى commit إعادة التشغيل سيُسقط الوضع الداكن من نسخته الحالية. لذلك لا يستطيع Git أن يجمع الميزتين عبر fast-forward. بل يحتاج إلى نسخة جديدة تتضمّنهما معًا.

9:24

لجمع الفرعين، يقارن Git نسخة كل فرع بنسخة البداية المشتركة، أي commit الإيقاف المؤقت. وهكذا يحدّد التغييرات التي أجرتها كل ميزة. يجمع Git مجموعتَي التغييرات في نسخة واحدة ويسجّلها في commit جديد. يُسمّى هذا commit دمج. ومعظم عمليات الدمج تنتهي دون تعارض. في مثالنا، هناك سطر كود غيّره كلا الوكيلين: فكلتا الميزتين غيّرت النص الذي يشرح مفاتيح التحكّم في اللعبة. سيجمع Git التغييرات الأخرى تلقائيًا، ويتوقّف عند ذلك السطر لنحلّ التعارض فيه.

9:59

يشغّل الوكيل git merge restart في مجلدنا الأصلي، على main. يجمع Git معظم التغييرات تلقائيًا. لكن الميزتين أجرتا تعديلين مختلفين على سطر مفاتيح التحكّم، لذا لا يستطيع Git جمع هذين التغييرين وحده. فيتوقّف ويُبلغ عن تعارض دمج. لم يُنشأ commit الدمج بعد، لذا علينا حلّ التعارض قبل إتمام الدمج.

10:23

بدأت الميزتان من نص مفاتيح تحكّم لا يذكر إلا «P: pause». تضيف نسخة main «D: dark mode»، وتضيف نسخة restart «R: restart». لا يستطيع Git أن يقرّر أيّ النسختين صحيحة، فيترك القرار لنا. نريد إبقاء الإيقاف المؤقت وذكر المفتاحين الجديدين، لذا يجب أن تجمع النتيجة بين الإضافتين.

10:48

يكتب Git علامات التعارض في ملف اللعبة في مجلدنا الأصلي. سطر الأقواس الزاوية الأول يفتح التعارض. واسمه HEAD، وهو يدلّ هنا على نسختنا الحالية في main.

11:00

سطر علامات المساواة يفصل بين نسخة main ونسخة restart. وسطر الأقواس الزاوية الأخير يغلق التعارض، واسمه restart.

11:11

الكود بين العلامات يُظهر البديلين اللذين علينا التوفيق بينهما. استبدلوا كتلة التعارض كلها بالكود المقصود، واحذفوا أسطر العلامات.

11:21

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

11:40

شغّلوا اللعبة المدمَجة من مجلدنا. تحقّقوا من أن P يوقف اللعبة مؤقتًا ويستأنفها، وأن D يبدّل المظهر، وأن R يعيد تشغيلها.

11:50

بعد التحقّق من اللعبة المدمَجة، نطلب من الوكيل إتمام الدمج. فيشغّل git add على ملف اللعبة بعد حلّ التعارض، ليضيفه إلى الـ staging، فيُعَدّ التعارض محلولًا. ثم يشغّل الوكيل git commit مع الرسالة «Merge restart and dark mode». وهذا يسجّل النسخة المدمَجة في commit دمج. الـ commit الأب (parent) هو commit بُني عليه commit آخر. ولـ commit الدمج هذا أبوان: commit الوضع الداكن وcommit إعادة التشغيل. يشير الفرع main الآن إلى النسخة المدمَجة، وفيها الإيقاف المؤقت والوضع الداكن وإعادة التشغيل.

12:26

بعد دمج الميزتين والتحقّق منهما، نطلب من الوكيل أن يتحقّق من خلوّ مجلدَي الميزتين من أي عمل لم يُسجَّل في commit. بعد هذا الفحص، يحذف الـ worktrees المنتهية وأسماء فروعها. يشغّل الوكيل git worktree remove لكل مجلد ميزة، ثم git branch مع الخيار -d لفرعَي الميزتين. تحذف هذه الأوامر المجلدات وأسماء الفروع، بينما تبقى الـ commits المدمَجة في سجلّ main. لا ندمج إلا الميزات التي نقرّر قبولها. والميزة التي لا نريدها تبقى ببساطة خارج main.

13:00

يُظهر السجلّ أن الميزتين بدأتا من commit الإيقاف المؤقت والتقتا في commit الدمج الذي يدلّ عليه main الآن. كتبنا كل ميزة وراجعناها في الـ worktree الخاص بها قبل دمجها. هكذا يستطيع عدة وكلاء أن يعملوا على ميزات منفصلة، بينما نقرّر نحن ما يدخل إلى المشروع. أعطوا كل وكيل فرعًا وworktree خاصّين به، وراجعوا ميزته، ثم ادمجوها وتحقّقوا من النتيجة.

13:24

نراكم في الفيديو التالي.

13:26 of video · 1,840 words · 37 slides← Back to the video