تبدیل فایل سازگار با سیستم کنترل نسخه

وقتی یک تیم توسعه مستندات، دارایی‌های طراحی یا فایل‌های داده‌ای را در کنار کد منبع ذخیره می‌کند، انتخاب فرمت فایل می‌تواند استفاده از سیستم کنترل نسخه را تسهیل یا مختل کند. یک تبدیل نادرست می‌تواند اندازه مخزن را افزایش دهد، خروجی diff را مبهم سازد و ساخت‌های خودکار را ناپایدار کند. این مقاله به بررسی ملاحظات فنی می‌پردازد که به شما امکان می‌دهد فایل‌ها را بدون قربانی کردن تاریخچه تمیز و قابلیت بازتولید که Git فراهم می‌کند تبدیل کنید. راهنما بر پایهٔ جریان‌های کاری واقعی استوار است و فرض می‌کند که برای تبدیل سریع و با حفظ حریم خصوصی از یک مبدل مبتنی بر ابر مانند convertise.app استفاده می‌کنید.


چرا تبدیل‌های متداول با Git در تضادند

Git برای ردیابی تغییرات متن ساده خط به خط عالی است. با این حال، بلوک‌های باینری به عنوان لحظه‌نگاره‌های مبهم ذخیره می‌شوند؛ هر تغییر باعث آپلود مجدد کل فایل می‌شود و مخزن را بزرگ می‌کند. علاوه بر این، بسیاری از خطوط لوله تبدیل خروجی غیرقابل پیش‌بینی تولید می‌کنند—زمان‌سازها، GUIDها یا فراداده‌های توکار در هر اجرا متفاوت هستند، که منجر به هشدارهای مثبت کذب در git diff و دشوارتر شدن حل تعارضات می‌شود. ترکیب باینری‌های بزرگ و عدم پیش‌بینی‌پذیری به سرعت مزایای داشتن یک منبع حقیقت واحد را از بین می‌برد.

یک گردش کار تبدیل سازگار با کنترل نسخه به سه مسألهٔ اصلی می‌پردازد:

  1. پُرشد حجم – جلوگیری از ذخیره مگابایت‌های دارایی‌های تولیدی در مخزن.
  2. شفافیت Diff – نگه داشتن خروجی در قالبی که Git بتواند تفاوت‌های معنادار را نشان دهد.
  3. قابلیت بازتولید – تضمین این که همان منبع همیشه خروجی یکسانی تولید کند، به‌طوری‌که خطوط لوله CI تعیین‌پذیر بمانند.

انتخاب فرمت‌های آماده برای تبدیل در مراحل اولیه

موثرترین روش کاهش مشکلات، انتخاب فرمت هدفی است که با قوت‌های Git هم‌راستا باشد. در ادامه رایج‌ترین جفت‌سازی‌های منبع‑به‑هدف و دلایل اهمیت آن‌ها آورده شده است:

  • Markdown → HTML / PDF – Markdown متن ساده است؛ HTML نیز مبتنی بر متن است، بنابراین مقایسهٔ diff کار می‌کند. هنگامی که PDF لازم است، آن را از یک خط لولهٔ LaTeX تعیین‌پذیر که زمان‌سازها را حذف می‌کند، تولید کنید.
  • SVG → PNG – SVG یک گرافیک برداری‌ و مقایسه‌پذیر است. برای توزیع نهایی فقط به PNG تبدیل کنید؛ SVG را در مخزن برای تاریخچه نگه دارید.
  • CSV → Parquet – CSV را برای بازبینی انسانی ذخیره کنید؛ با یک مرحلهٔ خودکار Parquet را برای تجزیه و تحلیل تولید کنید. فایل‌های Parquet باینری هستند، بنابراین باید در یک سطل data‑lake ذخیره شوند، نه در مخزن.
  • منبع طراحی (Figma، Sketch) → PNG / PDF – فایل‌های منبع اصلی را نگه دارید (اغلب باینری‌اند اما در یک پروژهٔ کنترل‌شدهٔ نسخه‌بندی می‌شوند). فقط هنگام انتشار، خروجی‌ها را استخراج کنید و آن‌ها را در یک مخزن artifacts جداگانه ذخیره کنید.

زمانی که تبدیل ناگزیر به باینری تولید می‌کند (مثلاً یک PDF کامپایل‌شده)، منبع (LaTeX، Markdown، SVG) را در Git ذخیره کنید و باینری را به‌عنوان یک artifact مشتق‌شده در نظر بگیرید. این جداسازی هم مشکل حجم و هم مشکل diff را حل می‌کند.


تبدیل تعیین‌پذیر: حذف تغییرپذیری پنهان

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

  1. حذف زمان‌سازها – اکثر مبدل‌ها تاریخ جاری را جاسازی می‌کنند که در هر اجرا تغییر می‌کند. با یک اسکریپت پس‌پردازش (exiftool -AllDates= …) آن‌ها را پاک کنید.
  2. نرمال‌سازی ترتیب فراداده‌ها – برخی ابزارها ورودی‌های دیکشنری را به ترتیب غیرقابل پیش‌بینی می‌نویسند. اگر مبدل گزینهٔ ترتیب ثابت دارد، از آن استفاده کنید؛ یا خروجی را از طریق یک سریالایزر ثابت (مثلاً jq -S برای JSON، xsltproc برای XML) بگذرانید.
  3. ثابت کردن تنظیمات فشرده‌سازی – الگوریتم فشرده‌سازی بدون‑زیان و تعیین‌پذیر (مثلاً zlib با بذر ثابت) را انتخاب کنید. از تنظیماتی که بذرهای تصادفی را شامل می‌شوند، پرهیز کنید.
  4. کنترل پایان خطوط – سراسراً از LF (\n) استفاده کنید؛ پایان خطوط ویندوزی (\r\n) diff را خراب می‌کند.
  5. استفاده از محیط قابل بازتولید – تبدیل را داخل یک کانتينر Docker اجرا کنید که تمام نسخه‌های کتابخانه را قفل کرده باشد. این کار اختلاف «روی ماشین من کار می‌کند» را از بین می‌برد.

با این‌که خط لولهٔ تبدیل را شبیه یک تابع خالص می‌کنید، artefact تولیدی در هر بار اجرا بر روی منبع یکسان، همان هش را دارد و امکان git diff --binary قابل اطمینان و کش‌گذاری ساده در CI را فراهم می‌کند.


ادغام تبدیل در گردش کار Git

دو الگوی رایج برای ادغام مراحل تبدیل وجود دارد:

1. هوک Pre‑commit برای تولید

یک هوک pre‑commit می‌تواند مبدل را بر روی فایل‌های staged قبل از کمیت اجرا کند. هوک artefact مشتق‌شده را به ایندکس می‌نویسد و تضمین می‌کند مخزن همیشه آخرین تبدیل را داشته باشد. مثال به‌زبان Bash:

#!/usr/bin/env bash
# Pre‑commit hook: generate PDFs from Markdown
files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.md$')
for f in $files; do
  out=${f%.md}.pdf
  curl -X POST -F "file=@$f" https://api.convertise.app/convert -F "target=pdf" -o "$out"
  # Strip timestamps to keep the file deterministic
  exiftool -AllDates= "$out" -overwrite_original
  git add "$out"
done

این هوک تبدیل را اتوماتیک می‌کند و تضمین می‌کند هر کمیت شامل باینری سازگار باشد.

2. Artefacts فقط در CI

وقتی باینری‌ها بزرگ باشند، بهتر است آن‌ها را روی سرور CI تولید کرده و به یک مخزن artefact (مثلاً GitHub Packages، Artifactory) ارسال کنید. منبع در Git می‌ماند و نسخه‌های نهایی از مخزن artefact کشیده می‌شوند. این الگو باعث جلوگیری از افزونگی مخزن می‌شود، در حالی که همچنان دارایی‌های آماده‑به‑استفاده را به مصرف‌کنندگان downstream می‌رساند.


مدیریت باینری‌های بزرگ با Git LFS

اگر مجبورید دارایی‌های بزرگ—تصاویر با وضوح بالا، PDFهای کامپایل‌شده برای یک کتاب، یا پیش‌نمایش مدل‌های 3‑بعدی—را نسخه‌بندی کنید، Git LFS (Large File Storage) راه‌حل استاندارد است. نکات کلیدی برای موفقیت:

  • فقط باینری‌های ضروری را ردیابی کنید. فایل‌های منبع آماده برای تبدیل را در مخزن اصلی نگه دارید؛ LFS باید خروجی نهایی را ذخیره کند.
  • یک قرارداد نام‌گذاری اعمال کنید (*.pdf.lfs، *.png.lfs) تا توسعه‌دهندگان بداند کدام فایل‌ها تحت مدیریت LFS هستند.
  • یک محدودیت حجم در .gitattributes تعیین کنید (مثلاً *.pdf filter=lfs diff=lfs merge=lfs -text) تا از کمیت مستقیم فایل‌های بزرگ جلوگیری شود.

در ترکیب با تبدیل تعیین‌پذیر، Git LFS فقط یک نسخهٔ هر خروجی را در هر نسخه ذخیره می‌کند و خروجی‌های یکسان در شاخه‌های مختلف همان شیء LFS را به‌اشتراک می‌گذارند و پهنای باند را صرفه‌جویی می‌کند.


خودکارسازی با هوک‌های Pre‑commit و Pre‑push

علاوه بر هوک تولید پایه، می‌توانید مراحل اعتبارسنجی برای شناسایی رگرسیون‌ها زودتر اضافه کنید:

  • تأیید checksum – پس از تبدیل، یک هش SHA‑256 محاسبه کنید و با مقداری که در فایل .checksums ذخیره شده مقایسه کنید. اگر اختلافی وجود داشته باشد، تبدیل تعیین‌پذیر نیست.
  • اعتبارسنجی Schema – برای فایل‌های داده‌ای (CSV → Parquet)، از یک JSON Schema یا تعریف Avro استفاده کنید تا اطمینان حاصل شود خروجی نوع ستون‌های مورد انتظار را رعایت می‌کند.
  • بررسی دسترس‌پذیری – یک ابزار a11y خودکار را بر روی PDF یا HTML تولیدی اجرا کنید تا تأیید شود alt‑text و ساختار سرفصل‌ها حفظ شده‌اند.

این چک‌ها به‌صورت محلی اجرا می‌شوند و بازخورد فوری قبل از رسیدن هر کد به مخزن مرکزی فراهم می‌کنند.


حفظ Metadata و منشا (Provenance)

حتی وقتی باینری غیرقابل مقایسه باشد، می‌توانید اطلاعات حیاتی منشا را در یک فایل side‑car ذخیره کنید. یک manifest JSON را در کنار هر دارایی تولیدی نگه دارید:

{
  "source": "docs/chapter1.md",
  "converter": "convertise.app",
  "timestamp": "2026-05-24T12:34:56Z",
  "options": {
    "pdfVersion": "1.7",
    "embedFonts": true
  },
  "hash": "a3f5c2..."
}

manifest به‌صورت متن ساده versioned می‌شود و می‌تواند توسط خطوط لوله CI برای تأیید اینکه باینری با منبع اعلام‌شده مطابقت دارد، استفاده شود.


تست صحت تبدیل

یک گردش کار مقاوم شامل تست‌های رگرسیون است که باینری‌های تازه تولیدشده را با یک baseline معتبر مقایسه می‌کند. چون diff باینری پر سر و صدا است، از ترکیبی استفاده کنید:

  • مقایسه پیکسلی تصویر با آستانهٔ تحمل (compare -metric RMSE).
  • مقایسه ساختاری PDF با diff-pdf --output-diff برای برجسته‌سازی تفاوت‌های بصری.
  • بررسی استخراج متن — OCR روی PDF اجرا کنید و متن استخراج‌شده را با منبع مقایسه کنید.

این چک‌ها را در یک شغل GitHub Actions خودکار کنید که اگر هر انحرافی از آستانهٔ مجاز فراتر رفت، PR را شکست می‌دهد.


مطالعهٔ موردی کوچک: سایت مستندات فنی

یک تیم نرم‌افزاری وب‌سایت مستندات عمومی خود را با Hugo می‌سازد. اسناد منبع در Markdown نگه داشته می‌شود؛ سایت همچنین کتابچه PDF قابل بارگیری ارائه می‌دهد. جریان کاری اولیه PDFها را مستقیماً در مخزن ذخیره می‌کرد. با گذشت زمان، مخزن به ۱٫۵ گیگابایت رسید و توسعه‌دهندگان از تعارضات merge در PDFها شکایت کردند.

مراحل راه‌حل:

  1. فقط فایل‌های .md را در مخزن نگه دارید.
  2. هوک pre‑commit اضافه کنید که convertise.app را برای تولید PDF از هر فایل Markdown فراخوانی کند، زمان‌سازها را حذف کند و یک فایل .md5 حاوی هش SHA‑256 بنویسد.
  3. Git LFS را طوری پیکربندی کنید که PDFها (*.pdf filter=lfs) را ذخیره کند.
  4. یک کار CI تنظیم کنید که همان تبدیل را اجرا، هش را با .md5 متعهد شده مقایسه و PDFها را به یک سطل S3 منتشر کند.
  5. وب‌سایت در زمان ساخت PDFها را از S3 می‌گیرد.

نتیجه: حجم مخزن ۷۸ ٪ کاهش یافت، diffها دوباره معنا داشتند و تولید PDF کاملاً بازتولیدپذیر شد، به‌طوری‌که «drift PDF» بین شاخه‌ها منقضی شد.


خلاصهٔ بهترین روش‌ها

  • فرمت‌های دوست‌دار منبع (Markdown، SVG، CSV) را در Git نگه دارید؛ باینری‌ها را به‌عنوان artefact مشتق‌شده در نظر بگیرید.
  • تبدیل‌ها را تعیین‌پذیر کنید با حذف زمان‌سازها، ثابت‌سازی فشرده‌سازی و استفاده از محیط‌های کانتینری.
  • تولید را خودکار کنید با هوک‌های pre‑commit برای دارایی‌های کوچک یا خطوط لوله CI برای دارایی‌های بزرگ.
  • Git LFS را فقط برای باینری‌های ضروری به کار بگیرید و زیر مجموعهٔ نام‌گذاری واضحی اعمال کنید.
  • منشا را در فایل‌های side‑car JSON ثبت کنید تا قابلیت حسابرسی بدون افزایش حجم مخزن حفظ شود.
  • به‌صورت منظم اعتبارسنجی کنید با checksum، schema و تست‌های رگرسیون بصری.

با هماهنگ کردن انتخاب‌های تبدیل با قوت‌های کنترل نسخه، تیم‌ها می‌توانند مخازن خود را کم حجم، تاریخچهٔ واضح و همچنان توانایی ارائهٔ دارایی‌های باینری با کیفیت بالا حفظ کنند. این رویکرد برای پروژه‌های کد‑محور و سایت‌های مستندات سنگین محتوا به‌یک‌سان کار می‌کند و به‌راحتی با مبدل‌های ابری‑محور و حفظ‑حریم‌خصوصی مانند convertise.app که تبدیل‌های معتبر و در‑وقت‑طلب را فراهم می‌آورند، یکپارچه می‌شود.