الفروع والجراحة

إزالة ملف من السجل

git rm يمنع ظهور ملف في الالتزامات الجديدة. وهو لا يفعل شيئًا لتلك التي صُنعت أصلًا: الكائن ما زال في قاعدة بيانات الكائنات، وما زال في كل استنساخ، وما زال على بعد git show واحد.

وهذا يهمّ في حالتين — حين يكون الملف بيانات اعتماد، وحين يكون حجمه 400 ميغابايت.

⌘K ← إزالة ملف من السجل، أو انقر بالزر الأيمن على الملف — في شجرة المشروع، أو في قائمة ملفات التزام، أو في محرّر الالتزام. والالتزام الذي حذف ملفًا هو عادةً المكان الذي يدرك فيه المرء أنه ما زال في السجل، لذا فالمخرج موجود في تلك القائمة أيضًا.

العثور على المسار

طريقتان للدخول، لأنهما تجيبان عن سؤالين مختلفين.

اكتبه — نسبةً إلى جذر المستودع، بلا شرطة مائلة في البداية — حين تعرف أصلًا ما جئت لإزالته.

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

القائمة نفسها تجيب عن السبب الآخر الذي يأتي به الناس إلى هنا — لماذا حجم هذا الاستنساخ غيغابايتان — لأنها مرتّبة حسب البايتات التي تشغلها كائنات كل مسار فعلًا. واختيار صفّ يقيسه فورًا.

كل مسار التُزم به يومًا، من الأثقل إلى الأخف، مع تعليم المحذوف منها

قِس قبل أن توافق

اضغط قِس (أو اختر صفًا). لم يُكتب شيء بعد. وتحصل على:

الالتزامات المعاد كتابتها كل التزام ابتداءً من أول واحد حمل الملف
الفروع / الوسوم المراجع التي ستتحرّك
ما تشغله كائناته البايتات التي تشغلها تلك النسخ فعلًا
أول التزام حيث تبدأ إعادة الكتابة — وكل ما بعده يحصل على بصمة جديدة

القياس: الالتزامات المعاد كتابتها، والمراجع المتأثرة، والبايتات المشغولة، والتحذير بتدوير السرّ على أي حال

إن كان العدد صفرًا، فالمسار خاطئ. وذلك عادةً خطأ إملائي أو بادئة مجلد، لا غياب الملف.

ما الذي تفعله إعادة الكتابة فعلًا

ينسخ Gitcito كل فرع ووسم إلى refs/gitcito/pre-purge/<timestamp>/…، ثم يشغّل:

git filter-branch --force \
  --index-filter 'git rm --cached --ignore-unmatch -- <path>' \
  --prune-empty --tag-name-filter cat -- --branches --tags

يعيد --index-filter كتابة الفهرس مباشرة بدل سحب كل التزام، وهذا هو الفرق بين دقائق وساعات. واستخدام --branches --tags بدل --all مقصود: --all كان سيشمل مراجع النسخة الاحتياطية، فتأكل إعادة الكتابة شبكة أمانها.

تُسقَط الالتزامات التي لم تحمل سوى الملف المُزال (--prune-empty). وتُعاد إشارة الوسوم إلى التزاماتها المعاد كتابتها.

النسخة الاحتياطية، ولماذا لا تعود المساحة بعد

الإزالة قابلة للتراجع، وثمن ذلك أن المساحة على القرص لا تُستردّ حتى تقول أنت ذلك. فما دامت النسخة الاحتياطية موجودة، تبقى الالتزامات القديمة قابلة للوصول، ولن يجمعها git.

الإجراء الأثر
استعادة يعود كل فرع ووسم إلى التزامه السابق للإزالة؛ ويعود الملف معها
إسقاط النسخة الاحتياطية يحذف مراجع النسخة الاحتياطية، ويُنهي صلاحية سجل المراجع، ويشغّل git gc --prune=now — تعود المساحة، وتصبح الإزالة نهائية

خطوتان لا واحدة، لأن الأولى هي النصف القابل للاسترجاع والثانية ليست كذلك.

دوّر السرّ على أي حال

إن سبق أن أُرسلت بيانات اعتماد، فإعادة كتابة سجلّك لا تلغي تسريبها. قد يكون أحدهم قد جلبها؛ وخوادم الاستضافة تحتفظ بالكائنات غير المُشار إليها؛ وقد يكون سجل تكامل مستمر قد طبعها. إعادة الكتابة توقف انتشارها أكثر — لكنها لا تلغي الانكشاف.

دوّر المفتاح. ثم أزِل الملف، حتى لا يجده أول من يستنسخ بعدك.

ما الذي لن يفعله

  • لن يرسل. إعادة الكتابة محلية. ونشر النتيجة يعني إرسالًا قسريًا لكل فرع متأثر، وعلى كل شخص آخر أن يعيد الاستنساخ أو يعيد الضبط الصلب — وحارس الإرسال القسري هو حيث يعيش ذلك القرار.
  • يرفض العمل على شجرة عمل متسخة أو في منتصف دمج/إعادة أساس. إعادة الكتابة تحرّك HEAD مرارًا، وفعل ذلك حول عمل غير ملتزَم به هو الطريق إلى ضياعه.
  • يعيد الكتابة بحسب المسار، لا بحسب المحتوى. إزالة سرّ لُصق داخل ملف مصدري، بدل أن يكون في ملف خاص به، تحتاج مرشّح محتوى — وذلك مجال git filter-repo --replace-text، ولا يغلّفه Gitcito.
  • filter-branch بطيء على السجلات الضخمة جدًا. لكنه ما يُشحن مع git في كل مكان، ولهذا هو ما يستخدمه Gitcito. في مستودع فيه عشرات الآلاف من الالتزامات، يكون git filter-repo في الطرفية هو الأداة الأسرع.
  • استنساخات الآخرين ليست مستودعك. سيحتفظون بالسجل القديم إلى أن يعيدوا الاستنساخ.

حسّن هذه الترجمة على GitHub