Git LFS (Large File Storage) یک افزونه‌ی رسمی برای گیت است که فایل‌های حجیم را از جریان معمول مخزن بیرون می‌کشد و در جای دیگری ذخیره می‌کند. بدون LFS، یک فایل ویدئویی چند صد مگابایتی در هر کامیت، تاریخچه‌ی مخزن را به‌سرعت متورم می‌کند و clone را برای همه‌ی اعضای تیم کند می‌سازد. LFS با جایگزین کردن فایل واقعی با یک اشاره‌گر متنی کوچک، این مشکل را در ریشه حل می‌کند. این نوشته مسیر کامل راه‌اندازی، مهاجرت، عیب‌یابی و بهینه‌سازی LFS را در GitHub پوشش می‌دهد. تمرکز اصلی روی تصمیم‌هایی است که در پروژه‌های واقعی بیشترین اثر را دارند.

اولین باری که یک مخزن گیت را با چند فایل ویدئویی clone کردم و دیدم حجم clone از یک گیگابایت گذشته، فهمیدم گیت برای این نوع داده طراحی نشده است. گیت روی مدل «هر بایت در تاریخچه برای همیشه می‌ماند» بنا شده و این مدل برای کد عالی است، اما برای فایل‌های حجیم یک فاجعه‌ی تدریجی می‌سازد.

چرا گیت برای فایل‌های حجیم ساخته نشده است

گیت یک سیستم کنترل نسخه‌ی توزیع‌شده است. هر clone، در اصل یک کپی کامل از تاریخچه است. این طراحی برای فایل‌های متنی و کد که تغییراتشان کوچک است، کارآمد است، چون گیت می‌تواند تفاوت‌ها را با الگوریتم‌های delta فشرده کند. اما برای فایل‌های باینری — مثل ویدئو، تصویر، فایل‌های طراحی و دیتاست‌های بزرگ — این الگوریتم‌ها کار نمی‌کنند. هر کامیت یک نسخه‌ی کامل جدید از فایل باینری را در تاریخچه ذخیره می‌کند و حجم مخزن به‌صورت خطی رشد می‌کند.

مشکل بزرگ‌تر این است که این حجم برای همیشه باقی می‌ماند. حتی اگر فایل را حذف کنید، نسخه‌های قبلی در تاریخچه‌ی commit‌ها باقی می‌مانند. یک فایل ۱۰۰ مگابایتی که ۲۰ بار تغییر کند، ۲ گیگابایت به حجم مخزن اضافه کرده است. این رشد در clone اولیه، هر بار دانلود می‌شود.

در عمل، سه پیامد مستقیم دارد: زمان clone طولانی، فضای دیسک مصرفی بالا و فشار روی سهمیه‌ی مخزن در سرویس‌های میزبانی. GitHub برای مخازن بیش از یک گیگابایت هشدار می‌دهد و برای مخازن چند گیگابایتی محدودیت اعمال می‌کند. آشنایی با اصول پایه‌ی گیت در این مسیر ضروری است؛ اگر تازه‌کار هستید، آموزش Git از صفر نقطه‌ی شروع مناسبی است.

گیت برای کد طراحی شده، نه برای داده‌ی حجیم؛ استفاده از آن برای فایل‌های باینری بزرگ، یک بدهی پنهان می‌سازد که دیر یا زود سر باز می‌کند.

LFS چگونه کار می‌کند؛ اشاره‌گرها و ذخیره‌سازی جداگانه

LFS با یک ایده‌ی ساده کار می‌کند: فایل واقعی را از گیت دور نگه می‌دارد و در مخزن فقط یک «اشاره‌گر» (pointer) متنی کوچک قرار می‌دهد. این اشاره‌گر چیزی شبیه این است:

version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345

وقتی گیت این فایل را می‌بیند، به‌جای محتوای واقعی، همین چند خط را در تاریخچه ذخیره می‌کند. فایل واقعی در یک سرور جداگانه به نام LFS server ذخیره می‌شود. GitHub برای هر مخزن یک LFS server مخصوص فراهم می‌کند که با سهمیه‌ی حساب شما کار می‌کند.

در زمان clone، اگر LFS نصب باشد و مخزن به‌درستی پیکربندی شده باشد، گیت ابتدا اشاره‌گرها را می‌آورد و سپس فایل‌های واقعی را از سرور LFS دانلود می‌کند. اگر LFS نصب نباشد، فقط اشاره‌گرها دانلود می‌شوند و فایل‌های واقعی به‌صورت متن ساده در می‌آیند. این رفتار در نگاه اول عجیب به نظر می‌رسد، اما در عمل اجازه می‌دهد مخزن بدون LFS هم قابل استفاده باشد — هرچند فایل‌ها ناقص خواهند بود.

فایل .gitattributes نقش کلیدی در این مکانیزم دارد. این فایل مشخص می‌کند کدام الگوهای فایل باید توسط LFS مدیریت شوند:

*.psd filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

عبارت -text به گیت می‌گوید این فایل‌ها باینری هستند و نباید تبدیل خط پایان انجام شود. حذف این عبارت می‌تواند به خرابی فایل منجر شود.

نصب و راه‌اندازی LFS در مخزن

پیش از هر کار، LFS باید روی سیستم نصب شود:

# macOS
brew install git-lfs

# Ubuntu/Debian
sudo apt install git-lfs

# Windows
# از سایت git-lfs.com دانلود و نصب کنید

# فعال‌سازی برای کاربر جاری
git lfs install

دستور git lfs install فقط یک بار در هر سیستم اجرا می‌شود و هوک‌های لازم را در پیکربندی گیت نصب می‌کند. بعد از آن، در هر مخزن جدید می‌توانید LFS را فعال کنید:

git lfs track "*.mp4"
git lfs track "*.psd"
git add .gitattributes
git commit -m "Configure Git LFS tracking"

ترتیب اجرا مهم است. فایل .gitattributes باید پیش از اضافه کردن فایل‌های حجیم commit شود؛ در غیر این صورت، فایل‌ها به‌صورت معمولی وارد تاریخچه می‌شوند و بعداً باید مهاجرت انجام دهید.

الگوهای ردیابی و انتخاب فایل‌های مناسب

انتخاب اینکه چه چیزی را با LFS ردیابی کنید، یک تصمیم مهندسی است. قاعده‌ی کلی این است که هر فایل باینری بزرگ‌تر از یک مگابایت که در طول زمان تغییر می‌کند، کاندید مناسبی است. اما نه همه‌ی فایل‌های باینری.

نوع فایلLFS مناسب است؟دلیل
ویدئو، صدا، تصویر خامبلهحجم بالا، تغییر مکرر
فایل‌های طراحی (PSD, AI)بلهحجم بالا، نسخه‌بندی مفید
دیتاست‌های MLبلهحجم بالا، به‌ندرت تغییر می‌کند
فایل‌های PDFبستگی دارداگر حجم بالا باشد بله، وگرنه نه
minified JS/CSSخیرخروجی build است، نباید در مخزن باشد
تصاویر سایت کوچکخیرحجم کم، delta compression کافی است

در پروژه‌های وب، تصاویر معمولاً نیازی به LFS ندارند. بهینه‌سازی آن‌ها با تکنیک‌های معمول کافی است؛ موضوعی که در کدام برای سرعت سایت بهتر است، WebP یا JPEG؟ به آن پرداخته شده. LFS برای حجم‌هایی است که این تکنیک‌ها جواب نمی‌دهند.

مهاجرت مخزن موجود به LFS

اگر مخزنی دارید که از قبل فایل‌های حجیم در آن commit شده‌اند، فقط اضافه کردن .gitattributes کافی نیست. تاریخچه‌ی قبلی همچنان فایل‌های واقعی را در خود دارد. برای پاک‌سازی تاریخچه باید از git lfs migrate استفاده کنید:

# مهاجرت همه‌ی فایل‌های منطبق با الگو
git lfs migrate import --include="*.mp4,*.psd" --everything

# بررسی وضعیت پس از مهاجرت
git lfs migrate info --everything

# پاک‌سازی و فشرده‌سازی مخزن
git reflog expire --expire=now --all
git gc --prune=now --aggressive

مهاجرت تاریخچه یک عملیات بازنویسی (rewrite) است و تمام SHA commit‌ها را تغییر می‌دهد. اگر مخزن روی ریموت مشترک است، همه‌ی اعضای تیم باید clone جدید انجام دهند. این فرآیند شبیه به همان منطقی است که در چگونه تعارض گیت را بدون از دست دادن کدها حل کنیم؟ توضیح داده شده، منتهی در مقیاسی بزرگ‌تر و حساس‌تر.

پیش از مهاجرت، یک نسخه‌ی پشتیبان از مخزن تهیه کنید. اگر مهاجرت نیمه‌کاره بماند یا خطایی رخ دهد، بازگشت به عقب بدون پشتیبان تقریباً غیرممکن است.

سهمیه‌ها و هزینه‌ها در GitHub

GitHub در حساب‌های رایگان سهمیه‌ای برای LFS تعیین می‌کند: یک گیگابایت فضای ذخیره‌سازی و یک گیگابایت پهنای باند ماهانه. در حساب‌های پرداختی این سهمیه‌ها افزایش می‌یابد و امکان خرید بسته‌های اضافی وجود دارد. اگر از سهمیه عبور کنید، دانلود فایل‌ها مسدود می‌شود و ظاهر مخزن همچنان سالم است، اما فایل‌های واقعی دانلود نمی‌شوند.

این موضوع در تیم‌های بزرگ می‌تواند به یک مشکل جدی تبدیل شود. یک پروژه‌ی طراحی با فایل‌های PSD متعدد، به‌سرعت سهمیه را پر می‌کند. پیگیری مصرف از طریق تنظیمات حساب یا با دستور زیر انجام می‌شود:

git lfs ls-files
git lfs status
git lfs env

در پروژه‌هایی که حجم LFS بالاست، جایگزین‌هایی مثل GitLab، Bitbucket یا سرور اختصاصی LFS باید بررسی شوند. مقایسه‌ی کلی این سرویس‌ها در GitHub یا GitLab؛ کدام برای توسعه‌دهندگان بهتر است؟ آمده است.

LFS در جریان CI/CD و GitHub Actions

در GitHub Actions، به‌طور پیش‌فرض LFS در مرحله‌ی checkout فعال نیست. برای دانلود فایل‌های LFS در workflow، باید پارامتر lfs را در اکشن checkout تنظیم کنید:

- uses: actions/checkout@v4
  with:
    lfs: true

بدون این تنظیم، workflow فقط اشاره‌گرها را می‌بیند و اگر مرحله‌ای به محتوای واقعی فایل نیاز داشته باشد، با خطا متوقف می‌شود. این موضوع در پروژه‌هایی که build به فایل‌های حجیم وابسته است، یکی از رایج‌ترین علت‌های شکست CI است. آشنایی با اصول خودکارسازی در GitHub Actions راهنمای خودکارسازی گردش کار به پیشگیری از این خطاها کمک می‌کند.

نکته‌ی مهم دیگر، مصرف پهنای باند LFS در CI است. هر اجرای workflow، فایل‌های LFS را دانلود می‌کند و این مصرف از سهمیه‌ی حساب کم می‌شود. در پروژه‌های پرترافیک، این می‌تواند به‌سرعت سهمیه را تمام کند. راه‌حل‌های عملی شامل cache کردن فایل‌های LFS بین اجراها یا انتقال آن‌ها به یک فضای ذخیره‌سازی خارجی است.

جایگزین‌ها و محدودیت‌های LFS

LFS تنها راه مدیریت فایل‌های حجیم نیست و محدودیت‌های مشخصی دارد. در برخی سناریوها، جایگزین‌ها منطقی‌تر هستند:

  • ذخیره‌سازی خارجی: فایل‌های حجیم را در S3، Google Cloud Storage یا فضای ابری مشابه نگه دارید و لینک آن‌ها را در مخزن بگذارید. حجم مخزن گیت کوچک می‌ماند.
  • DVC (Data Version Control): برای دیتاست‌های یادگیری ماشین، ابزارهایی مثل DVC امکانات نسخه‌بندی پیشرفته‌تری از LFS ارائه می‌دهند.
  • Git Annex: جایگزین قدیمی‌تر LFS با انعطاف بیشتر، اما پیچیدگی بالاتر.
  • Submodules: برای پروژه‌هایی که فایل‌های حجیم به‌صورت مستقل نگهداری می‌شوند.

انتخاب بین این گزینه‌ها به ماهیت پروژه بستگی دارد. برای پروژه‌های وب و وردپرسی که با مخزن گیت کار می‌کنید، LFS معمولاً کافی است. برای پروژه‌های داده‌محور، ابزارهای تخصصی‌تر انتخاب بهتری هستند.

LFS یک ابزار است، نه یک راه‌حل جادویی؛ انتخاب اشتباه الگوی ردیابی می‌تواند حجم را بیشتر از قبل کند.

خطاهای رایج و روش عیب‌یابی

  • خطای Git LFS is not installed: LFS روی سیستم نصب نیست یا فعال نشده. اجرای git lfs install مشکل را حل می‌کند.
  • خطای this exceeds GitHub''s file size limit: فایل از حد مجاز بزرگ‌تر است و LFS به‌درستی ردیابی نکرده. باید الگو را در .gitattributes اضافه و مهاجرت انجام دهید.
  • فایل‌های اشاره‌گر به‌جای محتوای واقعی: LFS نصب نیست یا سهمیه تمام شده. با git lfs pull یا git lfs fetch می‌توانید فایل‌ها را جداگانه دانلود کنید.
  • خطای batch response: This repository is over its data quota: سهمیه‌ی LFS تمام شده. باید بسته‌ی اضافی خریداری کنید یا سرویس را عوض کنید.
  • کند شدن push: فایل‌های حجیم در حال آپلود به LFS server هستند. این طبیعی است و به پهنای باند بستگی دارد.
  • حذف فایل از LFS: با git lfs migrate export می‌توانید فایل‌ها را از LFS خارج کنید، اما این عملیات تاریخچه را بازنویسی می‌کند.

برای عیب‌یابی دقیق‌تر، دستورات زیر مفید هستند:

git lfs env          # اطلاعات محیط LFS
git lfs ls-files     # فایل‌های تحت مدیریت LFS
git lfs status       # وضعیت فایل‌ها
git lfs fsck         # بررسی سلامت اشیاء LFS

بسیاری از این خطاها با همان منطقی که در رفع خطاهای رایج Git راهنمای کاربردی توضیح داده شده، قابل تشخیص هستند؛ چراکه LFS یک لایه‌ی اضافه روی همان ساختار گیت است.

پرسش‌های پرتکرار درباره Git LFS

آیا LFS برای پروژه‌های کوچک هم لازم است؟ اگر فایل‌های باینری حجیم ندارید، LFS فقط پیچیدگی اضافه می‌کند. برای پروژه‌های کوچک، گیت معمولی کافی است.

آیا می‌توانم فقط بخشی از فایل‌های LFS را دانلود کنم؟ بله، با git lfs pull --include="*.mp4" می‌توانید الگوی مشخصی را هدف بگیرید. این در پروژه‌های بزرگ صرفه‌جویی قابل‌توجهی در پهنای باند می‌کند.

تفاوت LFS و submodule چیست؟ LFS فایل‌ها را در همان مخزن نگه می‌دارد اما محتوای واقعی را جدا ذخیره می‌کند. Submodule یک مخزن مستقل را به‌عنوان زیرشاخه اضافه می‌کند. این دو برای سناریوهای متفاوتی مناسب‌اند.

آیا LFS از فایل‌های بیش از ۲ گیگابایت پشتیبانی می‌کند؟ محدودیت LFS به خود پروتکل مربوط نیست، بلکه به سهمیه و سیاست سرویس میزبانی بستگی دارد. GitHub برای فایل‌های بیش از ۲ گیگابایت محدودیت اعمال می‌کند.

آیا می‌توانم LFS را غیرفعال کنم؟ بله، با git lfs uninstall در سطح سیستم یا حذف الگوها از .gitattributes. اما فایل‌های قبلی در تاریخچه باقی می‌مانند تا زمانی که مهاجرت معکوس انجام دهید.

چرا بعد از clone، فایل‌های LFS خالی هستند؟ چون LFS نصب نیست یا git lfs pull اجرا نشده. پس از نصب LFS، دستور git lfs pull فایل‌ها را دانلود می‌کند.

آیا LFS روی GitHub Enterprise کار می‌کند؟ بله، اما سهمیه‌ها و پیکربندی ممکن است متفاوت باشد. برای اطلاعات دقیق، مستندات رسمی نصب‌شده را بررسی کنید.

لایه‌ی مهندسی و تصمیم‌های معماری

در سطح معماری، LFS یک سیستم ذخیره‌سازی توزیع‌شده با سازگاری نهایی (eventual consistency) است. اشاره‌گرها در تاریخچه‌ی گیت ذخیره می‌شوند و محتوای واقعی در سرور LFS. این تفکیک، مسئله‌ی اتمیک بودن و سازگاری را به دو زیرسیستم مستقل تقسیم می‌کند.

در پروژه‌های بزرگ، انتخاب الگوهای ردیابی یک تصمیم معماری است. اگر الگوها بیش از حد گسترده باشند، فایل‌های کوچکی که نیازی به LFS ندارند هم وارد این جریان می‌شوند و هزینه‌ی سهمیه را بالا می‌برند. اگر الگوها باریک باشند، فایل‌های حجیم ممکن است از قلم بیفتند و به تاریخچه‌ی گیت نفوذ کنند. تعادل بهینه معمولاً با ترکیب تحلیل مصرف واقعی و پیش‌بینی الگوهای آینده به دست می‌آید.

از منظر پروتکل، LFS از HTTP API استفاده می‌کند و همین موضوع آن را با پروکسی‌ها، فایروال‌ها و ابزارهای میانی سازگار می‌کند. اما در محیط‌های با محدودیت شبکه، باید مطمئن شوید که دامنه‌ی LFS server در فهرست مجاز قرار دارد. این موضوع در سازمان‌هایی که از پروکسی اجباری استفاده می‌کنند، یکی از رایج‌ترین علت‌های شکست است.

در تیم‌های توزیع‌شده، LFS یک نقطه‌ی مشترک وابستگی ایجاد می‌کند. اگر سرور LFS از دسترس خارج شود، همه‌ی اعضای تیم که به فایل‌های حجیم نیاز دارند، مسدود می‌شوند. برای کاهش این ریسک، برخی سازمان‌ها یک mirror محلی از LFS نگه می‌دارند و در زمان قطعی سرور اصلی، از آن استفاده می‌کنند. این معماری مشابه همان چیزی است که در GitHub فراتر از میزبانی کد برای همکاری تیمی توضیح داده شده است. 🔗

یک نکته‌ی ظریف در سطح سیستم: LFS از SHA-256 برای شناسایی محتوا استفاده می‌کند، در حالی که گیت سنتی از SHA-1. این تفاوت در مهاجرت بین سیستم‌ها و در پروژه‌هایی که با ابزارهای جانبی کار می‌کنند، می‌تواند به ناسازگاری منجر شود. در پروژه‌های حساس، این نکته را در طراحی اولیه لحاظ کنید. 🧩

بستن بحث

Git LFS یک راه‌حل بالغ برای مسئله‌ی فایل‌های حجیم در گیت است. تصمیم درست این است که پیش از افزودن اولین فایل باینری به مخزن، الگوهای LFS را تنظیم کنید و اجازه ندهید تاریخچه آلوده شود. مهاجرت بعدی همیشه ممکن است، اما هزینه‌ی آن به‌مراتب بیشتر از پیشگیری است.

اگر روی پروژه‌ای با حجم بالای فایل‌های باینری کار می‌کنید، LFS یکی از ابزارهایی است که باید در جعبه‌ابزار داشته باشید. ترکیب آن با یک سیاست روشن برای انتخاب الگوها و مدیریت سهمیه، مخزن را برای بلندمدت سالم نگه می‌دارد.

اگر تجربه‌ای از مهاجرت یک مخزن بزرگ به LFS دارید یا با محدودیت سهمیه در GitHub دست‌وپنجه نرم کرده‌اید، برایم جالب است بدانید کدام بخش بیشترین چالش را داشت. تجربه‌تان را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل جایگزینی برای مدیریت فایل‌های حجیم پیدا کرده‌اید.