تقنية

نهايات الأسطر في Git: ماذا يحفظ الفهرس وماذا يخرج إلى مجلد جديد؟

a.txt يبدأ CRLF في ملفنا؛ بعد إعادة التسوية يظهر LF في الفهرس، ثم CRLF أو LF في مجلدينا الجديدين.
مسار a.txt في تجربتنا؛ الألوان تشير إلى البايتات التي قرأناها.

text يضبط تسوية نهايات الأسطر، وeol يحدد نهايتها في نسخة العمل. [1]

الخلاصة السريعة

  • بقي الملفان في فهرس تجربتنا بنهاية CRLF بعد تغيير ملف السمات وحده.
  • بعد إعادة التسوية صار a.txt في الفهرس بنهاية LF، وبقي b.dat بنهاية CRLF.
  • لم تتغير بايتات ملفي العمل في تلك الخطوة؛ أعدنا قراءتها للتحقق.
  • أخرجنا مجلدين جديدين: اختلف a.txt بينهما، بينما بقي b.dat مطابقا.
  • سجلنا البايتات الفعلية؛ لم ننفذ التزاما أو اتصالا بمستودع بعيد.

ابدأ بملفين متساويين

أنشأنا ملفين سميناهما a.txt وb.dat. وضعنا في كل منهما الكلمتين alpha وbeta، وبعد كل كلمة البايتين 0D 0A. كان الحجم ثلاثة عشر بايتا لكل ملف. هذه بيانات تجربة صغيرة كتبناها، وليست ملفات أخذناها من مشروع قائم.

سنعرض النتائج باختصارين: CRLF للبايتين 0D 0A، وLF للبايت 0A. احتفظنا بالكلمتين نفسيهما في جميع القراءات. عندما يصبح الحجم أحد عشر بايتا بدلا من ثلاثة عشر، نستطيع تحديد البايتين اللذين غابا في نسختنا، بدلا من تخمين تغير الكلمات.

يحفظ البرنامج مجلداته في مكان تشغيله: git-line-case وcopy-crlf وcopy-lf. شغله ببايثون في مجلد تجربة لا يحتوي أيا من هذه الأسماء، مع توفر Git. يتوقف إنشاء الاسم الموجود بدلا من الكتابة فوقه. استخدمنا Python 3.12.14 وGit 2.53.0.windows.3.

نفذ السجل الكامل

حددنا الإعدادات داخل الاستدعاءات والبيئة الخاصة ببرنامجنا، وقدمنا ملف إعدادات فارغا. لم نعدل إعدادات المستخدم. يبدأ السجل بقاعدتي -text، ثم يغير قاعدة a.txt وحدها؛ لذلك يصلح b.dat للمقارنة في كل مرحلة.

import os, subprocess
from pathlib import Path
p=Path("git-line-case").resolve()
p.mkdir()
(p/"empty").write_bytes(b"")
env=os.environ|dict(GIT_CONFIG_NOSYSTEM="1",GIT_ATTR_NOSYSTEM="1",
    GIT_CONFIG_GLOBAL=str(p/"empty"))
def git(*args):
    return subprocess.check_output(["git","-c","core.autocrlf=false",
        "-c","core.safecrlf=false","-c","core.attributesFile="+str(p/"empty"),
        "-C",str(p),*args],env=env)
def attrs(rule):
    (p/".gitattributes").write_text(f"*.txt {rule}\n*.dat -text\n",encoding="ascii")
def read(label):
    print(label,[git("show",":"+n).hex() for n in names])
names=["a.txt","b.dat"]
git("init","--quiet")
for n in names: (p/n).write_bytes(b"alpha\r\nbeta\r\n")
attrs("-text");git("add",".");read("initial")
attrs("text eol=crlf");git("add",".gitattributes");read("attrs")
git("add","--renormalize",".");read("index")
print("work",[(p/n).read_bytes().hex() for n in names])
for eol in ["crlf","lf"]:
    attrs("text eol="+eol);git("add",".gitattributes")
    out=p.parent/("copy-"+eol);out.mkdir()
    git("checkout-index","--all","--prefix="+out.as_posix()+"/")
    print(eol,[(out/n).read_bytes().hex() for n in names])
[1] [2] [3] [4]

كل سطر مطبوع يحتوي اسما للمرحلة، ثم قيمتين: الأولى a.txt والثانية b.dat. يسجل البرنامج قيمة الفهرس بواسطة git show، ويقرأ ملف العمل مباشرة في سطر work. ترتيب القيم ثابت، فلا نخلط اسم الملف باسم المرحلة. [4]

اقرأ ما تغير في الفهرس

في initial جاءت القيمتان 616c7068610d0a626574610d0a. وبعد حفظ قاعدة text eol=crlf وإضافة ملف السمات فقط، أعاد attrs القيمتين نفسيهما. هذه المقارنة هي ضابطنا الأول: تغيير الملف الذي يصف القاعدة لم يبدل بايتات النسختين المخزنتين في تلك اللحظة.

إعادة التسوية تعيد تطبيق التنظيف على الملفات المتتبعة في الفهرس. [2]

بعد أمر إعادة التسوية قرأنا a.txt فوجدنا 616c7068610a626574610a، بينما بقيت القيمة الثانية كما كانت. أما سطر work فأعاد قيمتي الثلاثة عشر بايتا. هكذا أعطانا الملف المسمى a.txt قيمتين مختلفتين عند القراءة من موضعين محددين، دون تغيير اسمه.

stagea.txtb.dat
initialCRLFCRLF
attrsCRLFCRLF
indexLFCRLF
workCRLFCRLF
crlfCRLFCRLF
lfLFCRLF

قارن مجلدي الإخراج

يسمح بادئ المسار بإخراج ملفات الفهرس إلى مجلد جديد. [3]

المجلد الأول في سجلنا احتوى ملفين بنهاية CRLF. غيرنا بعدها قيمة eol إلى lf، وأضفنا ملف السمات، ثم أخرجنا مجلدا ثانيا جديدا. في الثاني قرأنا أحد عشر بايتا من a.txt وثلاثة عشر من b.dat. لم نستعمل المجلد الأول مرة ثانية؛ بقيت نتيجته مستقلة يمكن مقارنتها بالثاني.

القراءات الست لملفينا: الفهرس في ثلاث مراحل وملفا العمل ومجلدا الإخراج؛ يبقى b.dat بنهاية CRLF في كل قراءة.
ست قراءات من البرنامج نفسه؛ لا نستبدل نتيجة مرحلة بنتيجة أخرى.

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

احتفظ بالموضع مع النتيجة

عند تدوين ملاحظة عن هذه التجربة، اكتب اسم المرحلة والملف والقيمة المقروءة معا. قولنا «صار a.txt بنهاية LF» دون الموضع يترك مواضع مختلفة أمام القارئ. السجل الذي أخرجناه يسمح بربط كل عبارة بسطر محدد وبايتات محددة.

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

المراجع

  1. Git: gitattributes
  2. Git: git-add
  3. Git: git-checkout-index
  4. Git: gitrevisions
أُعد هذا المثال محليا في13 أكتوبر2026.

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