<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Git on kenji.blog</title><link>http://kenji.blog/ar/categories/git/</link><description>Recent content in Git on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ar</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ar/categories/git/index.xml" rel="self" type="application/rss+xml"/><item><title>【أوامر Git】الفرق بين rebase و merge، والاستخدام الصحيح في العمل الفعلي</title><link>http://kenji.blog/ar/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post 【أوامر Git】الفرق بين rebase و merge، والاستخدام الصحيح في العمل الفعلي" />&lt;h1 id="1-مقدمة-لماذا-يمثل-الاختيار-بين-merge-و-rebase-تحديا-مستمرا">1. مقدمة: لماذا يمثل الاختيار بين &amp;ldquo;merge و rebase&amp;rdquo; تحدياً مستمراً؟
&lt;/h1>&lt;p>Git هو نظام تحكم في الإصدارات لا غنى عنه في تطوير البرمجيات الحديثة. عندما يقوم العديد من المطورين بتعديل قاعدة الكود في وقت واحد، فإن نموذج الفروع القوي في Git يظهر فاعليته. ومع ذلك، في تطوير الفرق، يمثل النقاش حول &amp;ldquo;أيهما يجب استخدامه: &lt;code>merge&lt;/code> أو &lt;code>rebase&lt;/code>&amp;rdquo; موضوعاً يؤرق دائماً المطورين، من المبتدئين إلى الخبراء.&lt;/p>
&lt;p>في هذا المقال، سنتعمق في الفروق بين آليات &lt;code>git merge&lt;/code> و &lt;code>git rebase&lt;/code>، من خلال استكشاف البنية الداخلية لـ Git، مثل الرسم البياني الموجه غير الدوري (DAG) والخصائص الرياضية لتجزئة الالتزام (Commit Hash). وبعد ذلك، سنشرح بالتفصيل كيفية التمييز بينهما في العمل الفعلي، مع تقديم أمثلة على سير العمل (Workflows). بدلاً من مجرد سرد الأوامر، فإن فهم العمليات الحسابية التي يقوم بها Git في الخلفية سيزيل الخوف من التعارضات (Conflicts)، مما يتيح لك بناء سجل تاريخي نظيف وقابل للتتبع.&lt;/p>
&lt;hr>
&lt;h1 id="2-البنية-الداخلية-لـ-git-تجزئة-الالتزام-ونموذج-الكائنات">2. البنية الداخلية لـ Git: تجزئة الالتزام ونموذج الكائنات
&lt;/h1>&lt;p>لفهم كيف يقوم Git بدمج السجل التاريخي، يجب أن نعرف أولاً كيف يخزن Git البيانات. لا يقوم Git فقط بحفظ اختلافات (Patches) تغييرات الملفات، بل يحتفظ بلقطة (Snapshot) لنظام الملفات بأكمله في نقطة زمنية معينة.&lt;/p>
&lt;h2 id="21-الخصائص-التشفيرية-لتجزئة-الالتزام-commit-hash">2.1 الخصائص التشفيرية لتجزئة الالتزام (Commit Hash)
&lt;/h2>&lt;p>يتم تعريف كل التزام (Commit) في Git بشكل فريد بواسطة سلسلة من 40 حرفاً سداسياً عشرياً (Hexadecimal)، يتم حسابها بناءً على محتواه باستخدام دالة التجزئة SHA-1 (Secure Hash Algorithm 1). يتكون كائن الالتزام من العناصر التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>مؤشر لكائن Tree&lt;/strong>: لقطة من هيكل الدليل والملفات (Blob) في تلك النقطة.&lt;/li>
&lt;li>&lt;strong>مؤشر للالتزام الأب (Parent Commit)&lt;/strong>: قيمة تجزئة واحدة أو أكثر للالتزامات الآباء (الالتزام الأول لا يحتوي على أب، والتزام الدمج يحتوي على أبوين أو أكثر).&lt;/li>
&lt;li>&lt;strong>معلومات المؤلف (Author)&lt;/strong>: الشخص الذي كتب الكود والوقت.&lt;/li>
&lt;li>&lt;strong>معلومات الملتزم (Committer)&lt;/strong>: الشخص الذي أنشأ أو طبق الالتزام والوقت.&lt;/li>
&lt;li>&lt;strong>رسالة الالتزام (Commit Message)&lt;/strong>: نص يوضح القصد من التغيير.&lt;/li>
&lt;/ol>
&lt;p>رياضياً، تُعرّف قيمة التجزئة $H(C)$ لكائن الالتزام $C$ على النحو التالي:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>هنا، تمثل $\parallel$ دمج (Concatenation) البيانات. نظراً لخصائص دالة التجزئة، فإن تغيير حرف واحد فقط في رسالة الالتزام، أو حتى وجود التزام أب مختلف، سيؤدي إلى إنشاء قيمة تجزئة مختلفة تماماً. وهذا يعني أن &lt;strong>الالتزام غير قابل للتغيير (Immutable)&lt;/strong>. عندما يقال إن &lt;code>rebase&lt;/code> &amp;ldquo;يعيد كتابة السجل التاريخي&amp;rdquo;، فهذا يعني فعلياً أنه &amp;ldquo;ينشئ التزامات جديدة بمحتوى مشابه ولكن بقيم تجزئة مختلفة&amp;rdquo;.&lt;/p>
&lt;p>حجم مساحة التجزئة هو $2^{160}$، ويمكن تقريب احتمال $P$ لحدوث تصادم (Collision) (أي أن يكون لالتزامين مختلفين نفس قيمة التجزئة) باستخدام نظرية مفارقة عيد الميلاد (Birthday Paradox) على النحو التالي (حيث $n$ هو عدد الالتزامات):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>هذا الاحتمال منخفض للغاية، وعملياً من شبه المستحيل أن تتصادم تجزئة التزامات Git.&lt;/p>
&lt;hr>
&lt;h1 id="3-نظرية-الرسومات-البيانية-و-dag-النموذج-الرياضي-لسجل-git">3. نظرية الرسومات البيانية و DAG: النموذج الرياضي لسجل Git
&lt;/h1>&lt;p>يُنمذج السجل التاريخي لالتزامات Git على أنه &amp;ldquo;رسم بياني موجه غير دوري&amp;rdquo; (Directed Acyclic Graph, DAG) في نظرية الرسومات البيانية.&lt;/p>
&lt;h2 id="31-ما-هو-dag-الرسم-البياني-الموجه-غير-الدوري">3.1 ما هو DAG (الرسم البياني الموجه غير الدوري)؟
&lt;/h2>&lt;p>في الرسم البياني $G = (V, E)$، تمثل $V$ مجموعة الالتزامات (العُقد - Vertices)، وتمثل $E$ مجموعة الحواف الموجهة (Directed Edges) التي تشير إلى علاقات الآباء والأبناء بين الالتزامات. في Git، يتجه اتجاه الحافة من &amp;ldquo;الالتزام الابن إلى الالتزام الأب&amp;rdquo;. هذا لأن الالتزام الجديد يحتفظ بمؤشر إلى الالتزام السابق.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;الالتزام A (الأولي)&amp;#34;]
B[&amp;#34;الالتزام B&amp;#34;]
C[&amp;#34;الالتزام C (الرئيسي)&amp;#34;]
D[&amp;#34;الالتزام D (ميزة)&amp;#34;]
E[&amp;#34;الالتزام E (دمج)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>أهم ميزة لـ DAG هي &amp;ldquo;عدم وجود دورات (Cycles)&amp;rdquo;. بفضل هذا، فإن الخوارزمية التي تتتبع السجل التاريخي للوراء لا تقع في حلقة مفرغة، وتصل بالتأكيد إلى النهاية (الالتزام الأولي).&lt;/p>
&lt;h2 id="32-الفرز-الطوبولوجي-وترتيب-السجل">3.2 الفرز الطوبولوجي وترتيب السجل
&lt;/h2>&lt;p>عند عرض السجل التاريخي باستخدام أوامر مثل &lt;code>git log&lt;/code>، يتم ترتيب DAG كقائمة أحادية البعد بواسطة خوارزمية الفرز الطوبولوجي (Topological Sort). لأي حافة موجهة $u \to v$ في DAG (حيث $u$ هو ابن $v$)، يتم ترتيبها بحيث يظهر $u$ قبل $v$ في القائمة.&lt;/p>
&lt;hr>
&lt;h1 id="4-آليات-git-merge-وأنواعها">4. آليات git merge وأنواعها
&lt;/h1>&lt;p>الأمر الأساسي لدمج التغييرات من الفروع هو &lt;code>git merge&lt;/code>. ومع ذلك، بناءً على الحالة الحالية، يختار Git تلقائياً استراتيجية دمج مختلفة.&lt;/p>
&lt;h2 id="41-دمج-التقديم-السريع-fast-forward---ff">4.1 دمج التقديم السريع (Fast-Forward) (&amp;ndash;ff)
&lt;/h2>&lt;p>إذا كان الفرع الوجهة (مثال: &lt;code>main&lt;/code>) هو السلف المباشر للفرع المصدر (مثال: &lt;code>feature&lt;/code>)، يقوم Git بتنفيذ دمج &amp;ldquo;التقديم السريع (Fast-Forward)&amp;rdquo;. هذه العملية لا تُنشئ التزاماً جديداً، بل مجرد تحريك مؤشر الفرع للأمام.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>يحافظ دمج Fast-Forward على السجل التاريخي في خط مستقيم، ولكن من عيوبه فقدان السياق حول &amp;ldquo;أي مجموعة من الالتزامات تمثل تطوير ميزة (feature) واحدة&amp;rdquo;.&lt;/p>
&lt;h2 id="42-الدمج-بدون-التقديم-السريع-non-fast-forward---no-ff">4.2 الدمج بدون التقديم السريع (Non-Fast-Forward) (&amp;ndash;no-ff)
&lt;/h2>&lt;p>عند تحديد &lt;code>git merge --no-ff&lt;/code> صراحةً، حتى وإن كان الموقف يسمح بـ Fast-Forward، يتم دائماً إنشاء &amp;ldquo;التزام دمج (Merge Commit)&amp;rdquo; جديد. التزام الدمج هو التزام خاص يمتلك أبوين.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;العمل الرئيسي 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>ميزة هذه الطريقة هي أن وجود وتاريخ فرع الميزة يبقى بوضوح على DAG. عند حدوث مشكلة، يمكن التراجع بأمان عن الميزة بأكملها دفعة واحدة عن طريق تنفيذ &lt;code>git revert -m 1 &amp;lt;تجزئة التزام الدمج&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="43-خوارزمية-الدمج-ثلاثي-الاتجاهات-3-way-merge">4.3 خوارزمية الدمج ثلاثي الاتجاهات (3-Way Merge)
&lt;/h2>&lt;p>إذا كان لدى كل من الفرع الوجهة والفرع المصدر التزامات خاصة بهما، ينفذ Git دمجاً ثلاثي الاتجاهات. في هذه الحالة، يبحث Git في DAG للعثور على &amp;ldquo;أدنى سلف مشترك (Lowest Common Ancestor, LCA)&amp;rdquo; للفرعين.&lt;/p>
&lt;p>التعقيد الزمني $T_{\text{LCA}}$ لخوارزمية العثور على LCA يمكن تنفيذه في وقت خطي بالنسبة لعدد العُقد $|V|$ وعدد الحواف $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>يقارن Git بين ثلاثة أشياء: &amp;ldquo;حالة LCA&amp;rdquo;، &amp;ldquo;حالة الفرع الحالي&amp;rdquo;، و&amp;quot;حالة الفرع الآخر&amp;quot;، وإذا لم تتعارض التغييرات، يقوم تلقائياً بإنشاء التزام دمج.&lt;/p>
&lt;hr>
&lt;h1 id="5-آلية-git-rebase-وإعادة-بناء-السجل-التاريخي">5. آلية git rebase وإعادة بناء السجل التاريخي
&lt;/h1>&lt;p>بينما يقوم &lt;code>git merge&lt;/code> بـ &amp;ldquo;دمج&amp;rdquo; السجل، يقوم &lt;code>git rebase&lt;/code> بـ &amp;ldquo;إعادة بناء (إعادة ربط)&amp;rdquo; السجل.&lt;/p>
&lt;h2 id="51-ما-يحدث-خلف-كواليس-rebase">5.1 ما يحدث خلف كواليس Rebase
&lt;/h2>&lt;p>عندما تقوم بإعادة ربط (Rebase) فرع &lt;code>feature&lt;/code> بفرع &lt;code>main&lt;/code> (باستخدام &lt;code>git rebase main&lt;/code>)، تكون العمليات الداخلية كما يلي:&lt;/p>
&lt;ol>
&lt;li>إيجاد السلف المشترك (LCA) لفرعي &lt;code>feature&lt;/code> و &lt;code>main&lt;/code>.&lt;/li>
&lt;li>حفظ الفروقات (Diffs) من LCA إلى طرف فرع &lt;code>feature&lt;/code> في منطقة مؤقتة.&lt;/li>
&lt;li>نقل مؤشر فرع &lt;code>feature&lt;/code> إلى طرف فرع &lt;code>main&lt;/code>.&lt;/li>
&lt;li>تطبيق الفروقات المحفوظة تباعاً واحداً تلو الآخر (Cherry-Pick) فوق الأساس الجديد (طرف &lt;code>main&lt;/code>)، وإنشاء التزامات جديدة.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;الالتزام A&amp;#34;] --&amp;gt; B[&amp;#34;الالتزام B&amp;#34;]
B --&amp;gt; C[&amp;#34;الالتزام C (الرئيسي)&amp;#34;]
B --&amp;gt; D[&amp;#34;الالتزام D (الميزة القديمة)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;الالتزام &amp;#39;D (الميزة الجديدة)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>النقطة المهمة هنا هي أن الالتزام $'D$ الناتج عن إعادة الربط &lt;strong>له تجزئة مختلفة تماماً&lt;/strong> عن الالتزام الأصلي $D$، لأنه يمتلك التزاماً أباً مختلفاً (راجع تعريف دالة التجزئة $H(C)$ المذكور سابقاً).&lt;/p>
&lt;h2 id="52-إعادة-الربط-التفاعلية-interactive-rebase">5.2 إعادة الربط التفاعلية (Interactive Rebase)
&lt;/h2>&lt;p>يتيح لك استخدام &lt;code>git rebase -i&lt;/code> (أو &lt;code>--interactive&lt;/code>) التلاعب بالسجل التاريخي للالتزامات كما تشاء. إنها الأداة الأقوى لترتيب السجل المحلي.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>: احتفاظ بالالتزام كما هو.&lt;/li>
&lt;li>&lt;code>reword&lt;/code>: تعديل رسالة الالتزام فقط.&lt;/li>
&lt;li>&lt;code>edit&lt;/code>: الإيقاف المؤقت لتعديل محتوى الالتزام.&lt;/li>
&lt;li>&lt;code>squash&lt;/code>: دمج هذا الالتزام مع الالتزام السابق ودمج الرسائل معاً.&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>: مثل &lt;code>squash&lt;/code> ولكن مع تجاهل رسالة هذا الالتزام.&lt;/li>
&lt;li>&lt;code>drop&lt;/code>: حذف الالتزام تماماً.&lt;/li>
&lt;/ul>
&lt;p>من الناحية الرياضية، إذا كان هناك $N$ من الالتزامات في فرع ما، فإن المتغيرات (التباديل) الممكنة $P$ للسجل الخطي التي يمكن إنشاؤها عن طريق تغيير ترتيب إعادة الربط هي كما يلي:&lt;/p>
$$
P = N!
$$&lt;p>يمنح Git المطورين حرية اختيار من بين $N!$ طريقة، مما يتيح إبقاء السجل التاريخي منطقياً وجميلاً.&lt;/p>
&lt;hr>
&lt;h1 id="6-القاعدة-الذهبية-لإعادة-الربط-the-golden-rule-of-rebase">6. القاعدة الذهبية لإعادة الربط (The Golden Rule of Rebase)
&lt;/h1>&lt;p>بالرغم من قوة &lt;code>rebase&lt;/code> الهائلة، إلا أن هناك قاعدة واحدة مطلقة.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;يجب ألا تقوم أبداً بإعادة ربط (Rebase) السجل التاريخي العام (Public History).&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-لماذا-لا-يجب-إعادة-ربط-السجل-العام">6.1 لماذا لا يجب إعادة ربط السجل العام؟
&lt;/h2>&lt;p>Git هو نظام لا مركزي. التزاماتك التي تم دفعها إلى &lt;code>origin/main&lt;/code> تم استنساخها (Cloned) بالفعل في المستودعات المحلية للمطورين الآخرين. ماذا يحدث إذا قمت بإعادة ربط التزامات تم دفعها بالفعل، وأعدت كتابة السجل التاريخي، ثم قمت بالكتابة فوقه بالقوة باستخدام &lt;code>git push --force&lt;/code>؟&lt;/p>
&lt;p>سوف يتفرع DAG الموجود في أجهزة المطورين الآخرين بشكل جذري عن DAG الموجود في المستودع البعيد (Remote). عندما يقوم المطورون الآخرون بتشغيل &lt;code>git pull&lt;/code>، سيحاول Git دمج مجموعات من الالتزامات ذات تواريخ مختلفة بالقوة، مما يؤدي إلى حدوث عدد هائل من التعارضات والتزامات مكررة (التزامات لها نفس التغييرات ولكن بقيم تجزئة مختلفة)، وستسود الفوضى في المستودع.&lt;/p>
&lt;p>القاعدة الذهبية هي إجراء إعادة الربط (Rebase) &lt;strong>&amp;ldquo;فقط على الفروع المحلية التي لم تشاركها بعد مع أي شخص&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-حل-التعارضات-conflicts-و-git-rebase---continue">7. حل التعارضات (Conflicts) و git rebase &amp;ndash;continue
&lt;/h1>&lt;p>عندما يقوم عدة أشخاص بتعديل نفس الجزء من نفس الملف، يحدث تعارض (Conflict). تختلف عملية حل التعارضات بين &lt;code>merge&lt;/code> و &lt;code>rebase&lt;/code>.&lt;/p>
&lt;h2 id="71-حل-التعارضات-في-merge">7.1 حل التعارضات في Merge
&lt;/h2>&lt;p>في حالة &lt;code>git merge&lt;/code>، يحدث حل التعارضات &lt;strong>مرة واحدة فقط&lt;/strong>. قبل إنشاء التزام الدمج النهائي مباشرة، تقوم بتصحيح جميع أجزاء التعارض دفعة واحدة.&lt;/p>
&lt;h2 id="72-حل-التعارضات-في-rebase">7.2 حل التعارضات في Rebase
&lt;/h2>&lt;p>في حالة &lt;code>git rebase&lt;/code>، نظراً لطبيعته في إعادة تطبيق الالتزامات واحداً تلو الآخر، &lt;strong>من الممكن حدوث تعارض في كل التزام&lt;/strong>.&lt;/p>
&lt;p>عندما يحدث تعارض أثناء إعادة الربط، يقوم Git بإيقاف العملية مؤقتاً. تسلسل الحل هو كما يلي:&lt;/p>
&lt;ol>
&lt;li>افتح محرر نصوص أو بيئة تطوير متكاملة (IDE مثل VS Code)، وقم بتصحيح علامات التعارض (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code> و &lt;code>======&lt;/code> و &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>) يدوياً.&lt;/li>
&lt;li>أضف الملفات المصححة إلى الفهرس (Index):
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git add &amp;lt;الملف المعدل&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>لا تقم بإنشاء التزام، بل استأنف عملية إعادة الربط:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>إذا كنت ترغب في إلغاء عملية إعادة الربط نفسها والعودة إلى الحالة الأصلية، فقم بتنفيذ الأمر التالي:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(※ إذا لم تكن هناك حاجة لحل التعارض وأردت تخطي هذا الالتزام بالكامل، استخدم &lt;code>git rebase --skip&lt;/code>)&lt;/p>
&lt;hr>
&lt;h1 id="8-الاستخدام-الصحيح-في-العمل-الفعلي-تطبيق-سير-العمل">8. الاستخدام الصحيح في العمل الفعلي (تطبيق سير العمل)
&lt;/h1>&lt;p>إذن، كيف يجب أن نميز بين استخدام &lt;code>merge&lt;/code> و &lt;code>rebase&lt;/code> في بيئة التطوير الفعلية؟ إليك النهج الأكثر قياسية وأماناً.&lt;/p>
&lt;h2 id="81-السيناريو-1-ترتيب-سجل-العمل-المحلي-استخدام-rebase">8.1 [السيناريو 1] ترتيب سجل العمل المحلي (استخدام Rebase)
&lt;/h2>&lt;p>لنفترض أنك تعمل على فرع ميزة (feature)، وتراكمت لديك الكثير من الالتزامات الصغيرة (مثل &amp;ldquo;تصحيح خطأ مطبعي&amp;rdquo; أو &amp;ldquo;حفظ مؤقت&amp;rdquo;). قبل فتح طلب سحب (Pull Request - PR)، استخدم إعادة الربط التفاعلية لترتيب هذه الالتزامات في وحدات ذات معنى.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># نفذ هذا أثناء تواجدك في فرع feature&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (سيتم فتح المحرر، استخدم squash و fixup لتنظيف السجل)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بذلك، يمكنك إنشاء سجل التزامات جميل ومفهوم يسهل على المراجعين (Reviewers) فهم نواياك.&lt;/p>
&lt;h2 id="82-السيناريو-2-مواكبة-أحدث-فرع-main-استخدام-rebase">8.2 [السيناريو 2] مواكبة أحدث فرع main (استخدام Rebase)
&lt;/h2>&lt;p>إذا طالت فترة التطوير، واستمر دمج تغييرات الآخرين في فرع &lt;code>main&lt;/code>، فإن فرع &lt;code>feature&lt;/code> الخاص بك سيصبح قديماً. في هذه الحالة، قم بإعادة ربط فرع &lt;code>feature&lt;/code> بأحدث &lt;code>main&lt;/code> لمواكبته.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># الحصول على أحدث المعلومات لـ main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># نقل فرع feature فوق أحدث main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>هذا يجعل السجل التاريخي مستقيماً، مما يمنع التعارضات عند الدمج لاحقاً. كما يمنع إنشاء التزامات دمج غير ضرورية (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-السيناريو-3-دمج-ميزة-مكتملة-استخدام-merge">8.3 [السيناريو 3] دمج ميزة مكتملة (استخدام Merge)
&lt;/h2>&lt;p>اكتمل التطوير في فرع &lt;code>feature&lt;/code>، وحان وقت دمجها في فرع &lt;code>main&lt;/code>. هنا نستخدم &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (وهذا يعادل اختيار &amp;ldquo;Create a merge commit&amp;rdquo; في Pull Request على منصات مثل GitHub).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;دمج ميزة: تنفيذ ميزة تسجيل دخول المستخدم&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>هذا يترك نقطة التقاء تاريخية (التزام دمج) على DAG لفرع &lt;code>main&lt;/code> تفيد بأنه &amp;ldquo;هنا تم دمج ميزة واحدة&amp;rdquo;. عند مراجعة السجل لاحقاً، سيكون من الأسهل تتبع الكود حسب الميزة (Feature).&lt;/p>
&lt;hr>
&lt;h1 id="9-الخلاصة-ملخص">9. الخلاصة (ملخص)
&lt;/h1>&lt;p>في استخدام Git، إن الأساليب المتطرفة مثل &amp;ldquo;إنجاز كل شيء باستخدام Merge&amp;rdquo; أو &amp;ldquo;جعل كل شيء مستقيماً باستخدام Rebase&amp;rdquo; لها مزاياها وعيوبها.&lt;/p>
&lt;p>أفضل ممارسة في العمل الفعلي هي النهج الهجين (Hybrid): &lt;strong>&amp;ldquo;ترتيب السجل الخاص المحلي بشكل جميل باستخدام rebase، والاحتفاظ بسياق السجل العام المتكامل باستخدام merge &amp;ndash;no-ff&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>محلياً (مساحة العمل الشخصية)&lt;/strong>: استخدم &lt;code>rebase&lt;/code> لإزالة الالتزامات غير الضرورية، ومواكبة أحدث خط رئيسي، والحفاظ على سجل خطي.&lt;/li>
&lt;li>&lt;strong>عالمياً (مساحة العمل المشتركة)&lt;/strong>: استخدم &lt;code>merge --no-ff&lt;/code> لتسجيل وجود فرع الميزة كالتزام دمج على DAG، مما يسهل عمليات التراجع (Revert) والتتبع.&lt;/li>
&lt;/ul>
&lt;p>من خلال فهم الخلفية المعمارية والرياضية مثل هيكل DAG وآليات دوال التجزئة، ترتقي أوامر Git من مجرد حفظ عن ظهر قلب إلى &amp;ldquo;تصميم مقصود للسجل التاريخي&amp;rdquo;. بالالتزام بالقاعدة الذهبية لإعادة الربط واختيار الأوامر المثلى وفقاً للموقف، يمكنك بناء سجل التزامات نظيف سهل القراءة والصيانة للفريق بأكمله.&lt;/p></description></item><item><title>أخطاء شائعة يقع فيها المبتدئون في Git وأوامر حلها (مثل حل النزاعات)</title><link>http://kenji.blog/ar/p/git-beginners-mistakes-and-solutions/</link><pubDate>Sat, 12 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/git-beginners-mistakes-and-solutions/</guid><description>&lt;img src="http://kenji.blog/p/git-beginners-mistakes-and-solutions/img/eyecatch.jpg" alt="Featured image of post أخطاء شائعة يقع فيها المبتدئون في Git وأوامر حلها (مثل حل النزاعات)" />&lt;h1 id="أخطاء-شائعة-يقع-فيها-المبتدئون-في-git-وأوامر-حلها-مثل-حل-النزاعات">أخطاء شائعة يقع فيها المبتدئون في Git وأوامر حلها (مثل حل النزاعات)
&lt;/h1>&lt;h2 id="1-مقدمة-لماذا-نرتكب-الأخطاء-في-git">1. مقدمة: لماذا نرتكب الأخطاء في Git؟
&lt;/h2>&lt;p>في تطوير البرمجيات، أصبح Git ضروريًا كالهواء والماء. ومع ذلك، بالنسبة للعديد من المبتدئين (وأحياناً حتى المحترفين)، يمكن أن يبدو Git كـ &amp;ldquo;صندوق سحري أسود مخيف&amp;rdquo;. تختفي الالتزامات (commits)، أو يتم دفع الكثير من التغييرات عن طريق الخطأ إلى الفرع الخاطئ، أو تمتلئ الشاشة برسائل أخطاء حول النزاعات (conflicts) لم تراها من قبل&amp;hellip; عندما تقع في &amp;ldquo;فخاخ Git&amp;rdquo; هذه، يتوقف تقدم العمل تمامًا، وقد ينتابك الخوف من تدمير الكود المصدري في أسوأ الحالات.&lt;/p>
&lt;p>لماذا يُعتبر Git بهذا الصعوبة ولماذا هو عرضة للتسبب في الأخطاء؟ السبب الأكبر هو &amp;ldquo;استخدام الأوامر السطحية عن طريق الحفظ دون فهم ما يحدث داخل Git&amp;rdquo;. يعتمد Git على فلسفة تصميم قوية كنظام للتحكم في الإصدارات الموزعة (DVCS)، لكن واجهته (CLI) ليست بديهية دائمًا.&lt;/p>
&lt;p>في هذه المقالة، سنصنف العديد من &amp;ldquo;الأخطاء الشائعة&amp;rdquo; التي يواجهها مبتدئو Git في بيئة العمل إلى حالات، وسنقدم الأوامر المحددة لحل كل منها. ولكنها لن تكون مجرد قائمة أوامر (ورقة غش). سنتعمق بشكل مكثف بأكثر من 10,000 حرف لشرح &amp;ldquo;لماذا يحدث هذا الخطأ&amp;rdquo; و&amp;quot;كيف تتحرك البيانات داخل Git عند تنفيذ هذا الأمر&amp;quot;، مع شرح هيكل مجلد &lt;code>.git&lt;/code>، والخلفية الرياضية لخوارزمية Diff التي تعمل خلف الكواليس، ورسومات توضيحية باستخدام Mermaid.&lt;/p>
&lt;p>عندما تنتهي من قراءة هذه المقالة، يجب أن تتحرر من شعور &amp;ldquo;الخوف من Git&amp;rdquo;، وبدلاً من ذلك ستتأكد أنه &amp;ldquo;لا يوجد شريك موثوق به مثل Git&amp;rdquo;. والآن، لنتعمق في عالم Git العميق.&lt;/p>
&lt;hr>
&lt;h2 id="2-أعماق-git-فهم-الهيكل-الداخلي-لمجلد-git">2. أعماق Git: فهم الهيكل الداخلي لمجلد &lt;code>.git&lt;/code>
&lt;/h2>&lt;p>الخطوة الأولى لتسهيل استكشاف العديد من الأخطاء وإصلاحها هي معرفة كيف يقوم Git بتخزين البيانات. المجلد المخفي &lt;code>.git&lt;/code> الموجود في الدليل الجذري لمشروعك هو قلب Git. لا يقوم Git بإدارة البيانات كمجرد تسجيل للاختلافات (patches) في الملفات بالتتابع، بل كـ &lt;strong>تدفق من اللقطات (snapshots)&lt;/strong>.&lt;/p>
&lt;h3 id="21-نموذج-الكائنات-blob-tree-commit">2.1 نموذج الكائنات: Blob، Tree، Commit
&lt;/h3>&lt;p>يستخدم Git بشكل رئيسي ثلاثة كائنات لتمثيل حالة المستودع. يتم تخزين هذه الكائنات في &lt;code>.git/objects&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Blob (Binary Large Object)&lt;/strong>
كائن يخزن محتوى الملف نفسه. لا يتضمن اسم الملف أو معلومات الأذونات هنا. يتم ضغط سلسلة البايتات الصافية بواسطة zlib، ويتم التعرف عليها بواسطة قيمة تجزئة SHA-1 (رقم سداسي عشري من 40 حرفًا).&lt;/li>
&lt;li>&lt;strong>Tree&lt;/strong>
كائن يمثل هيكل الدليل. يتضمن كائن Tree مؤشرات (قيم تجزئة SHA-1) لكائنات Tree أخرى (أدلة فرعية) وكائنات Blob (ملفات)، بالإضافة إلى أسماء الملفات وأذونات الوصول الخاصة بها. يلعب دورًا مشابهًا لدليل UNIX.&lt;/li>
&lt;li>&lt;strong>Commit&lt;/strong>
يحتفظ بمؤشر لكائن Tree من المستوى الأعلى للمستودع بأكمله في نقطة زمنية معينة، وبيانات وصفية (المؤلف، تاريخ ووقت الالتزام، رسالة الالتزام)، ومؤشر للالتزام السابق (الالتزام الأصل).&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
Commit1[&amp;#34;الالتزام (تجزئة: 9f8a)&amp;#34;] --&amp;gt; Tree1[&amp;#34;شجرة (تجزئة: 4b82)&amp;#34;]
Tree1 --&amp;gt; Blob1[&amp;#34;كتلة (تجزئة: 8d7e) : index.js&amp;#34;]
Tree1 --&amp;gt; Tree2[&amp;#34;شجرة (تجزئة: 3a2c) : src/&amp;#34;]
Tree2 --&amp;gt; Blob2[&amp;#34;كتلة (تجزئة: 5f1b) : app.js&amp;#34;]
&lt;/pre>
&lt;h3 id="22-حقيقة-head-والمراجع-refs">2.2 حقيقة HEAD والمراجع (Refs)
&lt;/h3>&lt;p>الكلمة &lt;code>HEAD&lt;/code> التي تراها كثيرًا عند العمل مع Git. هي &lt;strong>مرجع رمزي (Symbolic Reference)&lt;/strong> يشير إلى الفرع (أو الالتزام) الذي قمت بجلبه (checkout) حاليًا.
إذا فتحت الملف &lt;code>.git/HEAD&lt;/code> في محرر نصوص، فسترى سلسلة نصية مثل هذه.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ref: refs/heads/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>هذا يعني أن &amp;ldquo;الحالة الحالية هي عند نهاية الفرع &lt;code>main&lt;/code>&amp;rdquo;. وإذا فتحت &lt;code>.git/refs/heads/main&lt;/code>، ستجد قيمة تجزئة SHA-1 مكونة من 40 حرفًا مكتوبة هناك، وهذا يشير إلى أحدث كائن Commit.
فرع Git هو ببساطة مؤشر خفيف (ملف) يشير إلى التزام معين. مجرد معرفة هذه الحقيقة يزيل الخوف من &amp;ldquo;هل سيتم حذف كل الملفات إذا قمت بحذف فرع؟&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="3-فهم-git-من-خلال-الرياضيات-خوارزمية-diff-ودالة-التجزئة">3. فهم Git من خلال الرياضيات: خوارزمية Diff ودالة التجزئة
&lt;/h2>&lt;p>عندما يكتشف Git تعارضًا أو يعرض الاختلافات بين الملفات، تعمل خوارزميات متقدمة داخليًا.&lt;/p>
&lt;h3 id="31-خوارزمية-diff-لمايرز">3.1 خوارزمية Diff لمايرز
&lt;/h3>&lt;p>خوارزمية الكشف عن الفروق الافتراضية في Git هي خوارزمية ابتكرها Eugene W. Myers. عندما يكون لدينا ملفان نصيان $A$ و $B$، فإن مسألة العثور على &amp;ldquo;الحد الأدنى من خطوات التحرير (الإدراج والحذف)&amp;rdquo; لتحويل $A$ إلى $B$ يمكن نمذجتها كمسألة أقصر مسار في نظرية الرسوم البيانية.&lt;/p>
&lt;p>لنفترض أن طولي السلسلتين هما $N, M$ على التوالي، والمجموع هو $V = N + M$. في خوارزمية مايرز، نبحث عن مسافة التحرير (Edit Distance) $D$. يتم التعبير عن التعقيد الزمني لهذه الخوارزمية بالمعادلة التالية:&lt;/p>
$$ \mathcal{O}(V \cdot D) $$&lt;p>هنا، إذا كان الاختلاف بين الملفات صغيرًا (أي أن $D$ صغير)، فإن الخوارزمية تعمل بسرعة عالية جدًا $\mathcal{O}(V)$. ومع ذلك، إذا كانت الملفات مختلفة تمامًا، تصبح $D \approx V$، ويكون التعقيد في أسوأ الحالات هو $\mathcal{O}(V^2)$.&lt;/p>
&lt;h3 id="32-patience-diff-و-histogram-diff">3.2 Patience Diff و Histogram Diff
&lt;/h3>&lt;p>على الرغم من أن خوارزمية مايرز ممتازة، إلا أنها قد تولد اختلافات ليست بديهية (لا معنى لها) للبشر عند إعادة ترتيب تسلسل الدوال أو الفئات بشكل كبير. لحل هذه المشكلة، قام Git بتطبيق &lt;code>Patience Diff&lt;/code> و &lt;code>Histogram Diff&lt;/code>.&lt;/p>
&lt;p>يركز Patience Diff على &amp;ldquo;الأسطر الفريدة التي تظهر مرة واحدة فقط في كلا الملفين&amp;rdquo;، ويجد أطول تسلسل فرعي مشترك (LCS) بينها. إذا كان عدد العناصر الفريدة هو $U$، يمكن حساب LCS بالتعقيد التالي:&lt;/p>
$$ \mathcal{O}(U \log U) $$&lt;p>إذا شعرت بصعوبة في حل النزاعات، فإن إحدى الطرق هي استخدام &lt;code>git diff --histogram&lt;/code> أو تحديد هذه الخوارزمية في استراتيجية الدمج (&lt;code>git merge -s recursive -X histogram&lt;/code>).&lt;/p>
&lt;h3 id="33-sha-1-واحتمال-التصادم">3.3 SHA-1 واحتمال التصادم
&lt;/h3>&lt;p>يدير Git جميع الكائنات باستخدام قيم تجزئة SHA-1. حجم فضاء التجزئة هو $2^{160}$. عند تقريب احتمال تصادم التجزئة (حيث يكون للمحتوى المختلف نفس قيمة التجزئة) باستخدام مفارقة يوم الميلاد (Birthday Paradox)، فإن عدد الكائنات $k$ المطلوب للوصول إلى احتمال تصادم $p$ بنسبة 50٪ هو كالتالي:&lt;/p>
$$ k \approx \sqrt{2 \ln(2)} \cdot 2^{80} \approx 1.2 \times 2^{80} $$&lt;p>هذا رقم فلكي، واحتمال حدوث تصادم غير مقصود في تطوير البرمجيات العادي هو صفر عمليًا. لذلك، يعمل Git بثقة على أساس أن قيمة التجزئة هي &amp;ldquo;معرف فريد مطلق&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="4-دراسة-حالة-1-قمت-بالالتزام-في-فرع-خاطئ">4. دراسة حالة 1: قمت بالالتزام في فرع خاطئ!
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
دون أن تدرك أنك تعمل في فرع &lt;code>main&lt;/code>، كتبت الكود للميزة الجديدة بحماس، وقمت بتنفيذ &lt;code>git commit&lt;/code>. كان يجب أن تنشئ فرعًا باسم &lt;code>feature/login&lt;/code> وتعمل هناك!&lt;/p>
&lt;h3 id="الحل-git-reset-وإنشاء-فرع">الحل: &lt;code>git reset&lt;/code> وإنشاء فرع
&lt;/h3>&lt;p>في Git، الالتزام هو كائن مستقل، والفرع مجرد مؤشر. لذلك، يمكن حله فورًا من خلال عملية &amp;ldquo;إنشاء فرع جديد ثم إرجاع مؤشر الفرع الحالي&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. إنشاء فرع جديد يشير إلى الالتزام الحالي (الالتزام الذي قمت به عن طريق الخطأ)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git branch feature/login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. إرجاع مؤشر فرع main إلى الالتزام السابق (HEAD~1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># يسمح لك --keep بإعادة التعيين بأمان مع الاحتفاظ بالتغييرات غير الملتزم بها في دليل العمل.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --keep HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. التبديل إلى الفرع الصحيح&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="التوضيح-بالرسم-ماذا-حدث-بالداخل">التوضيح بالرسم: ماذا حدث بالداخل؟
&lt;/h3>&lt;p>دعونا نتصور حركة مؤشرات الفروع في هذه المرحلة باستخدام &lt;code>gitGraph&lt;/code> في Mermaid.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;الالتزام الأولي&amp;#34;
commit id: &amp;#34;إصلاح خطأ&amp;#34;
commit id: &amp;#34;التزام خاطئ&amp;#34; type: HIGHLIGHT
branch feature/login
checkout feature/login
checkout main
&lt;/pre>
&lt;p>في البداية، كان كل من &lt;code>main&lt;/code> و &lt;code>HEAD&lt;/code> يشيران إلى &amp;ldquo;التزام خاطئ&amp;rdquo;، ولكن باستخدام &lt;code>git branch feature/login&lt;/code>، يتم إنشاء مؤشر جديد هناك. بعد ذلك، عن طريق &lt;code>git reset&lt;/code>، يعود مؤشر &lt;code>main&lt;/code> فقط إلى موضع &amp;ldquo;إصلاح خطأ&amp;rdquo;. لم يتم حذف الكائنات نفسها على الإطلاق.&lt;/p>
&lt;hr>
&lt;h2 id="5-دراسة-حالة-2-أريد-التراجع-عن-التزام-تم-دفعه-بالفعل">5. دراسة حالة 2: أريد التراجع عن التزام تم دفعه بالفعل!
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
في منتصف الليل، قمت بالالتزام بكود مليء بالأخطاء، ودفعته إلى المستودع البعيد باستخدام &lt;code>git push origin main&lt;/code>. أدركت أن هناك خطأً فادحًا وشعرت بالذعر.&lt;/p>
&lt;h3 id="الحل-1-إبطال-التاريخ-باستخدام-git-revert-موصى-به-وآمن">الحل 1: إبطال التاريخ باستخدام &lt;code>git revert&lt;/code> (موصى به وآمن)
&lt;/h3>&lt;p>في تطوير الفريق، يُحظر تمامًا التلاعب بسجل الالتزامات التي تم دفعها بالفعل باستخدام &lt;code>git reset&lt;/code> وما شابه. لأن ذلك سيتعارض مع المستودعات المحلية للمطورين الآخرين. النهج الصحيح هو &lt;strong>&amp;ldquo;إنشاء التزام جديد عكسي يلغي تمامًا التغييرات في الالتزام الخاطئ&amp;rdquo;&lt;/strong>. هذا هو &lt;code>git revert&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># إنشاء التزام يلغي أحدث التزام&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git revert HEAD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>main 7f3a8b2&lt;span class="o">]&lt;/span> Revert &lt;span class="s2">&amp;#34;رسالة الالتزام الخاطئ&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">1&lt;/span> file changed, &lt;span class="m">1&lt;/span> insertion&lt;span class="o">(&lt;/span>+&lt;span class="o">)&lt;/span>, &lt;span class="m">10&lt;/span> deletions&lt;span class="o">(&lt;/span>-&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># الدفع إلى المستودع البعيد&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;الالتزام أ&amp;#34;
commit id: &amp;#34;الالتزام ب (خطأ)&amp;#34;
commit id: &amp;#34;تراجع عن الالتزام ب&amp;#34; type: REVERSE
&lt;/pre>
&lt;p>يستمر التاريخ في التقدم، وحالة الكود فقط هي التي تعود إلى ما كانت عليه.&lt;/p>
&lt;h3 id="الحل-2-التلاعب-بالتاريخ-باستخدام-git-push---force-with-lease">الحل 2: التلاعب بالتاريخ باستخدام &lt;code>git push --force-with-lease&lt;/code>
&lt;/h3>&lt;p>إذا قمت بالدفع للتو إلى فرع تستخدمه أنت فقط، فمن المقبول إعادة كتابة التاريخ.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># إعادة التعيين محليًا وتصحيح الالتزام&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --hard HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Correct implementation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># الكتابة فوق التاريخ البعيد إجباريا&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin feature/login --force-with-lease
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>--force-with-lease&lt;/code> هو دفع إجباري آمن لمنع حوادث الكتابة فوق عمل الآخرين عن طريق الخطأ.&lt;/p>
&lt;hr>
&lt;h2 id="6-دراسة-حالة-3-أريد-التبديل-إلى-فرع-آخر-في-منتصف-العمل-سحر-stash">6. دراسة حالة 3: أريد التبديل إلى فرع آخر في منتصف العمل (سحر Stash)
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
أثناء تنفيذ ميزة جديدة في الفرع &lt;code>feature/A&lt;/code>، كان الكود المصدري في حالة غير مكتملة ولم يتم تجميعه بعد (compile). فجأة، يطلب منك مديرك إصلاح خلل طارئ في بيئة الإنتاج على الفرع &lt;code>main&lt;/code> فورًا!&lt;/p>
&lt;h3 id="الحل-الإخفاء-باستخدام-git-stash">الحل: الإخفاء باستخدام &lt;code>git stash&lt;/code>
&lt;/h3>&lt;p>&lt;code>git stash&lt;/code> هو أمر يقوم بإخفاء التغييرات غير الملتزم بها مؤقتًا في منطقة التخزين المؤقت.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. إخفاء التغييرات قيد التقدم&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash push -m &lt;span class="s2">&amp;#34;WIP: feature A partially implemented&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. السماح بالتبديل إلى فرع main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ... (تنفيذ عمل إصلاح الخلل الطارئ، الالتزام، والدفع) ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. العودة إلى الفرع الأصلي بعد الانتهاء من العمل&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. استعادة التغييرات المخفاة&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash pop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>عند تنفيذ &lt;code>git stash&lt;/code>، يقوم Git بإنشاء كائني Commit خاصين داخليًا ويحفظهما في مرجع يسمى &lt;code>refs/stash&lt;/code>. بمعنى آخر، الاستخفاء (Stash) هو في النهاية &amp;ldquo;التزام مؤقت بلا اسم&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="7-دراسة-حالة-4-حالة-الرأس-المنفصل-detached-head-المرعبة">7. دراسة حالة 4: حالة &amp;ldquo;الرأس المنفصل&amp;rdquo; (Detached HEAD) المرعبة
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
أردت التحقق من الكود في نقطة زمنية معينة في الماضي فنفذت &lt;code>git checkout 9f8a7b6&lt;/code>. فظهرت رسالة &lt;code>You are in 'detached HEAD' state.&lt;/code>. قمت بالالتزام على هذه الحالة، لكن عندما قمت بالتبديل إلى فرع آخر اختفى الالتزام!&lt;/p>
&lt;h3 id="آلية-الرأس-المنفصل-detached-head">آلية الرأس المنفصل (Detached HEAD)
&lt;/h3>&lt;p>عادةً يشير &lt;code>HEAD&lt;/code> إلى فرع مثل &lt;code>refs/heads/main&lt;/code>. ومع ذلك، عند جلب (checkout) التزام معين مباشرةً، سيشير &lt;code>HEAD&lt;/code> مباشرة إلى كائن الالتزام. يسمى هذا &lt;strong>الرأس المنفصل (Detached HEAD)&lt;/strong>.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;الالتزام أ&amp;#34;] --&amp;gt; B[&amp;#34;الالتزام ب&amp;#34;]
B --&amp;gt; C[&amp;#34;الالتزام ج&amp;#34;]
C --&amp;gt; D[&amp;#34;الالتزام د&amp;#34;]
BranchMain[&amp;#34;فرع: main&amp;#34;] --&amp;gt; D
HEAD[&amp;#34;HEAD&amp;#34;] --&amp;gt; B
style HEAD fill:#f9f,stroke:#333,stroke-width:4px
&lt;/pre>
&lt;p>حتى لو قمت بالالتزام مرارًا وتكرارًا في هذه الحالة، فلن يقوم أي فرع بتتبع هذا الالتزام الجديد. بمجرد التبديل إلى فرع آخر، سيضيع الالتزام الجديد.&lt;/p>
&lt;h3 id="الحل-حفظه-كفرع-جديد">الحل: حفظه كفرع جديد
&lt;/h3>&lt;p>يمكن حل ذلك عن طريق إنشاء فرع جديد في مكانك الحالي.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># أنشئ فرعًا جديدًا في موقع HEAD الحالي وبدّل إليه&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout -b feature/recovered-work
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="8-دراسة-حالة-5-حل-النزاعات-أثناء-الدمج-وإعادة-التعيين">8. دراسة حالة 5: حل النزاعات أثناء الدمج وإعادة التعيين
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
عندما نفذت &lt;code>git merge&lt;/code> أو &lt;code>git rebase&lt;/code>، ظهرت رسالة &lt;code>CONFLICT (content)&lt;/code> وتم إيقاف العملية.&lt;/p>
&lt;h3 id="الفرق-بين-الدمج-merge-وإعادة-التعيين-rebase">الفرق بين الدمج (Merge) وإعادة التعيين (Rebase)
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>الدمج (Merge)&lt;/strong>
يقوم بدمج ثلاثي (3-way merge) باستخدام أحدث الالتزامات لفرعين مع الجد المشترك، وينشئ التزام دمج (merge commit).&lt;/li>
&lt;li>&lt;strong>إعادة التعيين (Rebase)&lt;/strong>
يحفظ التزامات الفرع الحالي مؤقتًا ويطبقها مرة أخرى على رأس الهدف. يصبح التاريخ خطًا مستقيمًا.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;م1&amp;#34;
commit id: &amp;#34;م2&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;ف1&amp;#34;
commit id: &amp;#34;ف2&amp;#34;
checkout main
commit id: &amp;#34;م3&amp;#34;
merge feature
&lt;/pre>
&lt;h3 id="كيفية-حل-النزاعات">كيفية حل النزاعات
&lt;/h3>&lt;p>يتم إدراج العلامات التالية في الملف الذي حدث فيه النزاع.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span> &lt;span class="nx">HEAD&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">=======&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="nx">feature&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">api&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>إجراءات الحل بسيطة للغاية.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>حذف العلامات وتصحيح الكود ليكون بالشكل الصحيح.&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>إضافة الملف الذي تم حله إلى منطقة التجهيز (staging).&lt;/strong>
أمر &lt;code>git add&lt;/code> له دور &amp;ldquo;إخبار Git بأن النزاع قد تم حله&amp;rdquo;.
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git add index.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>إكمال العملية.&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># في حالة الدمج&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Resolve merge conflict in index.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># في حالة إعادة التعيين&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>إذا شعرت بالذعر، يمكنك إلغاء العملية في أي وقت باستخدام &lt;code>$ git merge --abort&lt;/code> أو &lt;code>$ git rebase --abort&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="9-دراسة-حالة-6-سجل-الالتزامات-فوضوي-git-rebase--i">9. دراسة حالة 6: سجل الالتزامات فوضوي! &lt;code>git rebase -i&lt;/code>
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
حدثت الكثير من الالتزامات الصغيرة مثل &amp;ldquo;إصلاح خطأ مطبعي&amp;rdquo;، &amp;ldquo;تعديل إضافي&amp;rdquo;، &amp;ldquo;إضافة اختبار&amp;rdquo;. إذا قمت بالدمج إلى &lt;code>main&lt;/code> هكذا، سيكون السجل فوضويًا.&lt;/p>
&lt;h3 id="الحل-إعادة-التعيين-التفاعلي">الحل: إعادة التعيين التفاعلي
&lt;/h3>&lt;p>باستخدام &lt;code>git rebase -i&lt;/code> (تفاعلي)، يمكنك تغيير ترتيب الالتزامات السابقة، أو دمج التزامات متعددة في واحد (squash)، أو تعديل رسالة الالتزام.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># تنظيم آخر 3 التزامات&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase -i HEAD~3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>يفتح المحرر ويتم عرض ما يلي.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 إصلاح خطأ مطبعي
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 2b3c4d5 تعديل إضافي
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 3c4d5e6 إضافة اختبار
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>أعد كتابتها كالتالي.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 تنفيذ الميزة X
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 2b3c4d5 تعديل إضافي
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 3c4d5e6 إضافة اختبار
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>عندما تحفظ وتغلق، ستندمج هذه الالتزامات الثلاثة بشكل جميل في التزام واحد.&lt;/p>
&lt;hr>
&lt;h2 id="10-دراسة-حالة-7-لا-أعرف-متى-حدث-الخطأ-git-bisect">10. دراسة حالة 7: لا أعرف متى حدث الخطأ! &lt;code>git bisect&lt;/code>
&lt;/h2>&lt;p>&lt;strong>[الموقف]&lt;/strong>
الفرع الحالي &lt;code>main&lt;/code> يحتوي على خلل، لكن عند الإصدار قبل شهر كان يعمل بشكل جيد. أريد تحديد الالتزام الذي أدخل الخلل، ولكن هناك أكثر من 100 التزام ومن المستحيل القيام بذلك يدويًا!&lt;/p>
&lt;h3 id="الحل-تحديد-الخلل-باستخدام-البحث-الثنائي">الحل: تحديد الخلل باستخدام البحث الثنائي
&lt;/h3>&lt;p>يحتوي Git على أداة مدمجة للعثور على الالتزام الذي يحتوي على الخلل باستخدام البحث الثنائي (Binary Search) الرياضي. التعقيد هو $\mathcal{O}(\log N)$، لذلك حتى لو كان هناك 1000 التزام، يمكن تحديد الخلل بحوالي 10 اختبارات.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># بدء البحث&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># الالتزام الحالي يحتوي على خلل (bad)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># قبل شهر (على سبيل المثال التجزئة a1b2c3d) كان جيدًا (good)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good a1b2c3d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># سيقوم Git تلقائيًا بجلب التزام وسيط، لذا قم بتشغيل الاختبار&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># إذا نجح الاختبار:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># إذا فشل الاختبار:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بتكرار ذلك، سيخبرك Git بدقة &amp;ldquo;هذا هو أول التزام سيء&amp;rdquo;. عند الانتهاء، ارجع إلى الحالة الأصلية باستخدام &lt;code>$ git bisect reset&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="11-شبكة-الأمان-القصوى-git-reflog">11. شبكة الأمان القصوى: &lt;code>git reflog&lt;/code>
&lt;/h2>&lt;p>السر النهائي لأي &amp;ldquo;خطأ&amp;rdquo; في Git هو &lt;code>git reflog&lt;/code>. يسجل Git جميع عملياتك المحلية (تاريخ حركة HEAD) لفترة معينة. حتى إذا قمت بحذف فرع أو إعادة تعيين خاطئة، يمكنك العثور على التجزئة السابقة باستخدام &lt;code>git reflog&lt;/code>، واستردادها ببساطة عن طريق استخدام &lt;code>git reset --hard&lt;/code> إلى تلك النقطة.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git reflog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9f8a7b6 &lt;span class="o">(&lt;/span>HEAD -&amp;gt; main&lt;span class="o">)&lt;/span> HEAD@&lt;span class="o">{&lt;/span>0&lt;span class="o">}&lt;/span>: commit: Add new feature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1a2b3c4 HEAD@&lt;span class="o">{&lt;/span>1&lt;span class="o">}&lt;/span>: reset: moving to HEAD~1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="12-الخاتمة">12. الخاتمة
&lt;/h2>&lt;p>لقد شرحنا بالتفصيل الأخطاء التي يسهل على مبتدئي Git الوقوع فيها، والآليات الكامنة وراءها في Git، وكيفية حلها. الالتزام في الفرع الخطأ، التراجع عن الالتزامات المدفوعة، استخدام Stash، النجاة من Detached HEAD، وحل النزاعات. المهم في كل هذا هو &amp;ldquo;تخيل كيف يتلاعب Git بالكائنات والمؤشرات خلف الكواليس&amp;rdquo;.&lt;/p>
&lt;p>تُحسب الفروق بين الملفات بخوارزمية Diff صارمة يمكن التعبير عنها رياضيًا، وتُضمن سلامة التاريخ بواسطة دوال التجزئة المشفرة. إذا فهمت فلسفة التصميم الجميلة هذه، ستدرك أن Git ليس أبدًا &amp;ldquo;صندوقًا أسود غامضًا&amp;rdquo;، بل هو أقوى درع يحمي كود المصدر الخاص بك بقوة.&lt;/p>
&lt;p>في المرة القادمة التي تعتقد فيها &amp;ldquo;لقد أفسدت الأمر!&amp;quot;، لا تقم بإغلاق الطرفية (terminal) في حالة من الذعر، بل خذ نفسًا عميقًا واكتب &lt;code>git status&lt;/code>. سيقدم لك Git بالتأكيد تلميحات لاستعادة عملك.&lt;/p></description></item></channel></rss>