دمج التعديلات في Git: دليل شامل مع أمثلة

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

هنا تأتي أهمية دمج التعديلات. دمج التعديلات هو عملية combinar عدة إدخالات تعديل في تعديل واحد متكامل.

على سبيل المثال، لنفترض أننا نعمل على ميزة تنفيذ نموذج تسجيل الدخول، ونقوم بإنشاء الأربع تعديلات التالية:

一旦功能完成,对于整个项目来说,这些提交记录过于详细。我们不需要在将来知道我们在开发过程中遇到了并修复了一个错误。为了确保主分支的历史记录清晰,我们将这些提交合并为一个单独的提交:

如何在 Git 中合并提交:交互式重置

合并提交的最常见方法是使用交互式重置。我们使用以下命令来启动它:

git rebase -i HEAD~<number_of_commits>

<提交数量> 替换为我们想要合并的提交数。

在我们的案例中,我们有四个提交,所以命令是:

git rebase -i HEAD~4

执行这个命令将会打开一个交互式命令行编辑器:

القسم العلوي يعرض التعديلات، بينما يحتوي القسم السفلي على تعليقات حول كيفية دمج التعديلات.

نرى أربع تعديلات. لكل منها، يجب علينا決ید ما الأمر الذي سننفذه. نهتم بأوامر pick (p) و squash (s). لدمج هذه الأربع تعديلات في تعديل واحد، يمكننا اختيار الأول ودمج الثلاثة الباقية.

نطبق الأوامر عن طريق تعديل النص الذي يسبق كل تعديل، Specifically تغییر pick إلى s أو squash للتعديلات الثانية، الثالثة والرابعة. للقيام بهذه التعديلات، نحتاج إلى دخول نمط “الإدخال” في محرر النصوص من سطر الأوامر بالضغط على مفتاح i على لوحة المفاتيح:

بعد الضغط على i، سيظهر النص -- INSERT -- في الأسفل، مما يشير إلى أننا دخلنا في نمط الإدخال. الآن، يمكننا حركة المؤشر باستخدام مفاتيح الأسهم، حذف الأحرف، وكتابة النص كما نفعل في محرر نصوص قياسي:

一旦我们对更改感到满意,我们需要通过按下键盘上的Esc键来退出插入模式。下一步是保存我们的更改并退出编辑器。为此,我们首先按下:键,向编辑器发出我们打算执行命令的信号:

现在,在编辑器的底部,我们看到一个分号:提示我们输入命令。为了保存更改,我们使用命令w,它代表“写入”。要关闭编辑器,使用q,它代表“退出”。这些命令可以组合在一起输入wq

要执行命令,我们按下Enter键。这个操作将关闭当前的编辑器并打开一个新的编辑器,允许我们输入新压缩提交的提交信息。编辑器将显示一个默认信息,包括我们从四个被压缩的提交中合并的消息:

أوصي بتعديل الرسالة ل reflect التغييرات التي تم تنفيذها بواسطة هذه المجموعات المدمجة من التعديلات – بعد كل شيء، هدفت الدمج هي الحفاظ على تاريخ نظيف وسهل القراءة.

لتفاعل مع المحرر وتعديل الرسالة، نضغط على i مرة أخرى لentras إلى وضع التحرير ونعدل الرسالة حسب رغبتنا.

في هذه الحالة، نستبدل رسالة التعديل بـ “Implement login form.” للخروج من وضع التحرير، نضغط على Esc. ثم نحفظ التغييرات بضغط :، إدخال الأمر wq، وضغط Enter.

كيفية عرض تاريخ التعديلات

بشكل عام، استعادة تاريخ التعديلات بأكمله قد تكون صعبة. لعرض تاريخ التعديلات، يمكننا استخدام الأمر git log. في المثال المذكور، قبل القيام بالدمج، تنفيذ الأمر git log سيعرض:

لتنقل بين قائمة التعديلات، استخدم مفاتيح الأسهم للأسفل واللأعلى. للخروج، اضغط على q.

يمكننا استخدام الأمر git log لتأكيد نجاح الدمج. تنفيذه بعد الدمج سيعرض تعديلاً واحداً يحتوي على الرسالة الجديدة:

دفع التعديل المدمج

سيعمل الأمر السابق على المستودع المحلي. لتحديث المستودع البعيد، نحتاج إلى دفع تغييراتنا. ولكن، لأننا غيرنا تاريخ التعديلات، نحتاج إلى استخدام الدفع الإجباري باستخدام الخيار --force:

git push --force origin feature/login-form

سيؤدي الدفع الإجباري إلى الكتابة فوق تاريخ التعديلات في الفرع البعيد وقد ي引起 إزعاجاً لمن يعملون على ذلك الفرع. من الممارسات الجيدة التواصل مع الفريق قبل القيام بذلك

طريقة آمنة لتنفيذ دفع القوة (force push)، تقلل من خطر إرباك المساهمين، هي استخدام خيار --force-with-lease بدلاً من ذلك:

git push --force-with-lease origin feature/login-form

يضمن هذا الخيار أننا سنقوم بتنفيذ دفع القوة فقط إذا لم يتم تحديث الفرع البعيد منذ آخر سحب (fetch) أو سحب لأسفل (pull) لدينا.

دمج commits محددين

تخيل أن لدينا خمس commits:

لنفترض أننا نريد الحفاظ على commits 1، 2، و5 ودمج commits 3 و4.

عند استخدام إعادة الترتيب التفاعلية (interactive rebase)، ستكون Commits مختارة للدمج مدمجة مع Commit السابق مباشرة. في هذه الحالة، يعني ذلك أننا نريد دمج Commit4 بحيث يندمج في Commit3.

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

git rebase -i HEAD~3

ثم ن设定 التوصيل4 إلى s حتى يتم دمجه مع التوصيل3:

بعد تنفيذ هذا الأمر وعرض التوصيلات، نلاحظ أن التوصيلين 3 و4 قد دمجتا معًا بينما بقي الباقي بدون تغيير.

الدمج من توصيل محدد

في الأمر git rebase -i HEAD~3، الجزء HEAD هو اختصار للتعهد الأحدث. بنية ~3 تُستخدم لتحديد أسلاف التعهد. على سبيل المثال، HEAD~1 يشير إلى الوالد لتوصيل HEAD.

في الإعادة التفاعلية، تشمل الالتزامات المعنية جميع الالتزامات السلفية التي تؤدي إلى الالتزام المحدد في الأمر. ي请注意 أن الالتزام المحدد لا يُشمل:

بدلاً من استخدام HEAD، يمكننا تحديد哈希 الالتزام مباشرة. على سبيل المثال، Commit2 يحتوي على哈希 dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf، لذا الأمر:

git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf

سيبدأ إعادة التأسيس باعتبر جميع الالتزامات التي أجريت بعد Commit2. لذا، إذا كنا نريد بدء إعادة التأسيس عند التزام معين وتضمين ذلك الالتزام، يمكننا استخدام الأمر:

git rebase -i <commit-hash>~1

حل النزاعات عند الدمج

عندما ندمج الالتزامات، ن combined عدة تغييرات الالتزام في التزام واحد، مما يمكن أن يؤدي إلى نزاعات إذا تزامنت التغييرات أو تباينت بشكل كبير. إليك بعض السيناريوهات الشائعة التي قد تنشأ فيها النزاعات:

  1. التغييرات المتداخلة: إذا كان هناك إثنين أو أكثر من التعديلات المدمجة قد قاموا بتعديل نفس السطور من ملف أو سطور قريبة منها، قد لا يتمكن Git من توفيق هذه التغييرات بشكل تلقائي.
  2. حالة التغييرات المختلفة: إذا قام تعديل ب��加 جزء معين من الكود وتعديل آخر يعدل أو يحذف نفس الجزء من الكود، فإن دمج هذه التعديلات يمكن أن يؤدي إلى تعارض يجب حله.
  3. إعادة تسمية وتعديل: إذا قام تعديل بإعادة تسمية ملف و التعديلات اللاحقة تقوم بتعديل الاسم القديم، فإن دمج هذه التعديلات يمكن أن يربك Git، مما يسبب تعارض.
  4. التغييرات في الملفات الثنائية: الملفات الثنائية لا تتجمع جيدًا باستخدام أدوات diff النصية. إذا قام أكثر من تعديل بتغيير نفس الملف الثنائي وحاولنا دمجها، قد يحدث تعارض لأن Git لا يمكنه توفيق هذه التغييرات آليًا.
  5. التاريخ المعقد: إذا كان للتغييرات تاريخ معقد مع العديد من الدمجات، أو الفروع، أو إعادة التأسيس فيما بينها، فإن دمجها قد يؤدي إلى تعارضات بسبب طبيعة التغييرات غير الخطية.

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

سيتم标记 النزاع بعلامات النزاع <<<<<< و >>>>>>. لمعالجة النزاعات، نحتاج لفتح الملفات وحل كل منها يدويًا باختيار الجزء من الكود الذي نريد الاحتفاظ به. 

بعد حل النزاعات، نحتاج لتحديد الملفات المحلولة باستخدام أمر git add. ثم يمكننا متابعة الإعادة الأساسية باستخدام الأمر التالي:

git rebase --continue

لمزيد من المعلومات حول نزاعات Git، قم بالاطلاع على هذا الدليل حول كيفية حل نزاعات الدمج في Git.

البدائل لسحق النزاعات باستخدام الإعادة الأساسية

أمر git merge --squash هو طريقة بديلة لـ git rebase -i لدمج عدة تعديلات في تعديل واحد. هذا الأمر مفيد بشكل خاص عندما نريد دمج التغييرات من فرع إلى الفرع الرئيسي بينما ندمج جميع التعديلات الفردية في واحد. إليك نظرة عامة حول كيفية الدمج باستخدام git merge:

  1. نتوجه إلى الفرع المستهدف الذي نريد دمج التغييرات فيه.
  2. ننفذ الأمر git merge --squash <اسم-الفرع> ب替换 <اسم-الفرع> باسم الفرع.
  3. نقوم بالتزام التغييرات باستخدام git commit لإنشاء تعديل واحد يمثل جميع التغييرات من الفرع الفرعي.

على سبيل المثال، لنفترض أننا نريد دمج التغييرات من الفروع feature/login-form إلى main كالتزام واحد:

git checkout main git merge --squash feature-branch git commit -m "Implement login form"

هذه هي القيود لهذه الطريقة مقارنة بـ git rebase -i:

  • دقة التحكم: أقل تحكم على التزامات فردية. باستخدام إعادة الأساس نستطيع اختيار الالتزامات التي نريد دمجها بينما يفرض الدمج دمج جميع التغييرات في التزام واحد.
  • التاريخ المتوسط: عند استخدام الدمج، تُفقَد تاريخ التعهدات الفردية من الفروع الخاصة بالخصائص في الفرع الرئيسي. هذا قد يجعل متابعة التغييرات التدريجية التي أجريت خلال تطوير الميزة أكثر صعوبة.
  • مراجعة ما قبل التعهد:Since it stages all changes as a single change set, we cannot review or test each commit individually before squashing, unlike during an interactive rebase where each commit can be reviewed and tested in sequence.

الخلاصة

incorporates frequent and small commits into the development workflow promotes collaboration and clear documentation, but it can also clutter the project’s history. Squashing commits strikes a balance, preserving the important milestones while eliminating the noise of minor iterative changes.

للحصول على مزيد من المعلومات حول Git، أنصح بهذه الموارد:

Source:
https://www.datacamp.com/tutorial/git-squash-commits