أريد مشاركة الوصفات لحل بعض المشاكل التي تنشأ أحيانًا عند العمل مع git ، والتي "ليست واضحة تمامًا".
في البداية اعتقدت أن تتراكم المزيد من هذه الوصفات ، ولكن كل شيء له وقته. أعتقد أنه إذا كان هناك أي فائدة ، فمن الممكن شيئًا فشيئًا ...
لذا ...
فروع الاندماج القديمة مع ألم بسيط
الديباجة. هناك فرع master
( master
) ، يلتزم بنشاط الميزات والإصلاحات الجديدة ؛ هناك فرع متوازي feature
، أبحر فيه المطورون لفترة من الوقت في السكينة الخاصة بهم ، ثم اكتشفوا فجأة أنهم لم يكونوا مع السيد لمدة شهر الآن ، وكان الدمج "على الجبهة" (وجها لوجه) غير بسيط.
(نعم ، لا يتعلق الأمر بعالم مثالي حيث كل شيء صحيح ، والجريمة غائبة ، والأطفال مطيعون دائمًا وحتى يعبرون الطريق بصرامة مع والدتهم ، وينظرون بحذر).
الغرض: أن تغضب. في الوقت نفسه ، كان دمجًا "خالصًا" ، بدون ميزات. على سبيل المثال لذلك في المستودع العام في الرسم البياني للفرع ، يتم ربط خيطين في نقطة واحدة برسالة "الفرع المدمج" الرئيسي "في الميزة". والصداع كله "هذا واحد" حول كم من الوقت والجهد الذي استغرقه ، وعدد النزاعات التي تم حلها ، وكم الشعر المتبقي من دون داع.
المؤامرة. حقيقة أنه في البوابة يمكنك تعديل آخر التزام بالمفتاح - --amend
يعرفه --amend
. الحيلة هي أن هذا "الالتزام الأخير" يمكن أن يكون موجودًا في أي مكان ويحتوي على أي شيء. على سبيل المثال ، لا يمكن أن يكون مجرد "الالتزام الأخير بالفرع الخطي" ، حيث نسوا تصحيح الخطأ المطبعي ، ولكن أيضًا التزام الدمج من الدمج المعتاد أو "الأخطبوط". --amend
فقط --amend
التغييرات المقترحة و "تضمين" الالتزام المتغير في الشجرة ، كما لو أنه ظهر بالفعل نتيجة دمج صادق وحل النزاعات. في جوهرها ، git merge
و git commit --amend
يسمح git commit --amend
بفصل "مكان git commit --amend
تمامًا ("هذا الالتزام في الشجرة سيكون هنا") ومحتويات الالتزام نفسه.
الفكرة الأساسية لارتباط معقد مع تاريخ نظيف بسيطة: أولاً ، "إنشاء مكان" عن طريق إنشاء التزام بالدمج النظيف (بغض النظر عن المحتويات) ، ثم إعادة كتابته باستخدام - --amend
، مما يجعل المحتوى "صحيحًا".
"حبة مكان". من السهل القيام بذلك عن طريق وضع استراتيجية اندماج لن تطرح أسئلة غير ضرورية حول حل النزاع.
git checkout feature git merge master -s ours
أوه نعم. كان من الضروري قبل الدمج إنشاء فرع "نسخ احتياطي" من رأس الميزة. بعد كل شيء ، لم يتم دمج أي شيء حقًا ... ولكن فليكن هذه هي الفقرة الثانية ، وليس الفقرة 0. بشكل عام ، ننتقل إلى الميزة غير المدمجة ، والآن ندمج بصدق. بأي شكل من الأشكال ، على الرغم من أي "الاختراق القذرة". طريقي الشخصي هو النظر إلى الفرع الرئيسي من لحظة الدمج الأخير وتقييم عمليات ارتكاب المشاكل المحتملة (على سبيل المثال: تصحيح خطأ مطبعي في مكان واحد - وليس مشكلة واحدة. على نطاق واسع (العديد من الملفات) قاموا بإعادة تسمية كيان - مشكلة واحدة ، وما إلى ذلك). من ارتكاب المشكلات ، ننشئ فروعًا جديدة (أقوم ببراعة - master1 ، master2 ، master3 ، وما إلى ذلك). ثم نقوم بدمج فرع تلو الآخر ، وننتقل من النزاعات القديمة إلى الجديدة والصحيحة (التي عادة ما تكون بديهية مع هذا النهج). اقتراح طرق أخرى (أنا لست ساحرًا ؛ أنا أتعلم فقط ؛ سأكون سعيدًا بالتعليقات البناءة!). في النهاية ، إنفاق (ربما) بضع ساعات على العمليات الروتينية البحتة (والتي يمكن أن يعهد بها إلى صغار ، لأنه ببساطة لا توجد تعارضات معقدة مع هذا النهج) ، نحصل على الحالة النهائية للرمز: تم نقل جميع الابتكارات / إصلاحات المعالج إلى فرع الميزة بنجاح ، وجميع الاختبارات ذات الصلة مشى على هذا الرمز ، إلخ. يجب الالتزام برمز ناجح.
إعادة كتابة "قصة نجاح". يجري على الالتزام ، حيث "يتم كل شيء" ، قم بتشغيل ما يلي:
git tag mp git checkout mp git reset feature git checkout feature git tag -d mp
(I decipher: باستخدام علامة (mp - دمج نقطة) ، ننتقل إلى حالة HEAD المنفصلة ، ومن هناك يتم إعادة تعيينها إلى رأس فرعنا ، حيث في البداية تم إنشاء "حصة خارج" من خلال عملية دمج احتيالية. لم تعد العلامة مطلوبة ، لذلك نحذفها). نحن الآن على التزام دمج "محض" الأصلي ؛ في نفس الوقت في نسخة العمل لدينا الملفات "الصحيحة" ، حيث يوجد كل ما تحتاجه. الآن تحتاج إلى إضافة جميع الملفات التي تم تغييرها إلى الفهرس ، وإلقاء نظرة خاصة على الملفات غير المرحلية (سيكون هناك جميع الملفات الجديدة التي ظهرت في الفرع الرئيسي). نضيف كل ما نحتاجه من هناك أيضًا.
أخيرًا ، عندما يكون كل شيء جاهزًا ، ندخل التزامنا الصحيح في المكان المحجوز:
git commit --amend
مرحى! كل شيء يعمل! يمكنك دفع فرع عرضيًا إلى مستودع عام ، ولن يعرف أحد أنك قضيت نصف يوم من العمل في هذا الدمج.
التحديث: طريقة أكثر إيجازا
بعد ثلاثة أشهر من هذا المنشور ، مقالة " كيف ولماذا سرقة الأشجار في بوابة " من قبل capslocky
استنادًا إلى دوافعها ، من الممكن تحقيق نفس الهدف بالضبط بطريقة أقصر وبدون آليات مساعدة: لا حاجة إلى "نشر مساحة" ، والنظر في الملفات غير المرحلية بعد إعادة التعيين وإجراء التعديل ؛ يمكنك إنشاء التزام دمج مباشر مع المحتوى المطلوب في خطوة واحدة.
نبدأ على الفور بالاندماج باستخدام أي من الطرق المتاحة (كما في الفقرة 2 أعلاه). لا تزال القصة المتوسطة والمتسللة غير ذات صلة. وبعد ذلك ، بدلاً من المطالبة 3 ، مع استبدال التزام الدمج ، نقوم بعمل دمج مصطنع ، كما هو الحال في المقالة:
git tag mp git checkout feature git merge --ff $(git commit-tree mp^{tree} -m "merged branch 'master' into 'feature'" -p feature -p master) git tag -d mp
كل السحر هنا يتم في خطوة واحدة بواسطة الأمر الثالث (بوابة الالتزام - شجرة).
حدد جزءًا من الملف ، مع الاحتفاظ بالسجل
الديباجة: تم ترميز الملف ، وأخيرًا تم ترميزه حتى أن الاستوديو المرئي بدأ يتباطأ ، ويهضمه (ناهيك عن JetBrains). (نعم ، لقد عدنا إلى عالم "غير كامل". كما هو الحال دائمًا).
العقول الذكية فكرت وفكرت وخصت العديد من الكيانات التي يمكن تقديمها في ملف منفصل. لكن! إذا أخذته للتو ، قم بنسخ ولصق جزء من الملف ولصقه في ملف آخر - سيكون هذا ملفًا جديدًا تمامًا من وجهة نظر بوابة. في حالة وجود أي مشاكل ، سيشير البحث في السجل بشكل لا لبس فيه فقط إلى "أين يوجد هذا الشخص المعاق؟" من شارك الملف. وقد يكون من الضروري العثور على المصدر الأصلي ليس "للقمع" على الإطلاق ، ولكن بشكل بنّاء - لمعرفة سبب تغيير هذا الخط ؛ ما علة إصلاحه (أو لم يتم إصلاح أي). أريد أن يكون الملف جديدًا ، ولكن في نفس الوقت لا يزال سجل التغييرات بالكامل!
المؤامرة. مع بعض تأثيرات الحواف المزعجة قليلاً ، يمكن القيام بذلك. من أجل التعريف ، هناك ملف file.txt
، أرغب في تسليط الضوء على جزء منه في file2.txt
. (وما زلت تحتفظ بالقصة ، نعم). تشغيل هذا المقتطف:
f=file.txt; f1=file1.txt; f2=file2.txt cp $f $f2 git add $f2 git mv $f $f1 git commit -m"split $f step 1, converted to $f1 and $f2"
نتيجة لذلك ، نحصل على ملفات file1.txt
و file2.txt
. كلاهما لهما نفس القصة بالضبط (حقيقي ؛ مثل الملف الأصلي). نعم ، كان يجب إعادة تسمية file.txt
الأصلي ؛ هذا هو تأثير حافة "مزعج قليلاً". لسوء الحظ ، لم أجد طريقة لحفظ القصة ، ولكن من أجل عدم إعادة تسمية الملف المصدر (إذا كان بإمكان أي شخص ، أخبرني!). ومع ذلك ، فإن git سوف تتحمل كل شيء. الآن لا أحد يزعج إعادة تسمية الملف مرة أخرى في التزام منفصل:
git mv $f1 $f git commit -m"split finish, rename $f1 to $f"
الآن file2.txt
سيعرض المذهب نفس تاريخ سطر الملف الأصلي. الشيء الرئيسي هو عدم دمج هاتين الجريمتين معًا (وإلا سيختفي كل السحر ؛ لقد جربته!). ولكن في الوقت نفسه ، لا أحد يزعج تحرير الملفات مباشرة في عملية الفصل ؛ ليس من الضروري القيام بذلك في وقت لاحق مع عمليات منفصلة. ونعم ، يمكنك تحديد العديد من الملفات دفعة واحدة!
النقطة الرئيسية في الوصفة: إعادة تسمية الملف المصدر إلى آخر في نفس الالتزام ، حيث يتم (وربما تحريره) نسخة (نسخ). ودع هذا الالتزام يعيش في المستقبل (لن يخطئ أبدًا في إعادة التسمية العكسية).
تحديث: زوج من الوصفات من Lissov
فصل جزء مستودع التاريخ
أنت في أحدث إصدار من المستودع الأولي. المهمة هي فصل مجلد واحد. (لقد رأيت خيارات للعديد من المجلدات ، ولكن من الأسهل والأكثر قابلية للفهم إما طي كل شيء أولاً في مجلد واحد ، أو تكرار ما يلي عدة مرات.)
هام! يجب أن تتم جميع الحركات باستخدام git mv
، وإلا فقد يفقد التاريخ التاريخ.
نقوم بتنفيذ:
git filter-branch --prune-empty --subdirectory-filter "{directory}" [branch]
{الدليل} هو المجلد الذي سيتم فصله. ونتيجة لذلك ، نحصل على المجلد جنبًا إلى جنب مع السجل الكامل للالتزامات فقط ، أي في كل التزام ، يتم عرض الملفات من هذا المجلد فقط. وبطبيعة الحال ، سيكون جزءًا من عمليات التنفيذ فارغًا ، ويزيلهم -prune-blank.
الآن قم بتغيير الأصل:
git remote set-url origin {another_repository_url}` git checkout move_from_Repo_1
إذا كان المستودع الثاني نظيفًا ، يمكنك الذهاب مباشرة لإتقان. حسنًا ، ادفع:
git push -u move_from_Repo_1
مقتطف كامل (للنسخ واللصق بسهولة):
directory="directory_to_extract"; newurl="another_repository_url" git filter-branch --prune-empty --subdirectory-filter "$directory" git remote set-url origin "$newurl" git checkout move_from_Repo_1 git push -u move_from_Repo_1
ادمج مستودعين معًا
لنفترض أنك فعلت شيئًا أعلى بمرتين وحصلت على move_from_Repo_1
move_from_Repo_2
في كل من move_from_Repo_1
و move_from_Repo_2
، وفي كل ملف قمت بنقل الملفات باستخدام git mv
إلى المكان الذي يجب أن تكون فيه بعد الدمج. الآن يبقى للسيطرة على:
br1="move_from_Repo_1"; br2="move_from_Repo_2" git checkout master git merge origin/$br1 --allow-unrelated-histories git merge origin/$br2 --allow-unrelated-histories git push
الحيلة بأكملها في --السماح - لا علاقة لها - التاريخ. ونتيجة لذلك ، نحصل على مستودع واحد يحتوي على سجل كامل لجميع التغييرات.