למה Git LFS נחוץ: המאגר גדל, הבנייה מואטת
Git שומר כל גרסה של קובץ כאובייקט נפרד, ולכן היסטוריית הקומיטים גדלה יחד עם מספר העריכות. כל עוד במאגר יש רק קוד מקור, זה כמעט לא מורגש — דחיסת דלתא מכסה את ההבדל בין גרסאות סמוכות.
ברגע שנכנסים לשם ארטיפקטים בינאריים — מדיה, פריסות, מודלים, מסדי נתונים לבדיקות, הפצות בנויות — כל עריכה מוסיפה להיסטוריה עותק מלא וחדש של הקובץ, ולא את ההבדל.
ואז נכנסת מפולת. שכפול המאגר נמשך עשרות דקות, כי הלקוח מוריד את כל גרסאות הקובץ, כולל כאלה שכבר מזמן לא נחוצות. סוכן הבנייה מבלה יותר זמן בחימום עותק העבודה מאשר בבנייה עצמה, משימות במקביל נתקלות במגבלות דיסק ורשת, והמטמון מתאפס כל הזמן כשמתמלא. ככל שהצוות פעיל יותר, כך ההיסטוריה מתנפחת יותר — וכל ריצה הבאה יקרה יותר.
במקביל נתקעת גם הפרסום: האירוח מסרב לקבל שינויים כשגודל האובייקטים הנשלחים חורג מהמגבלה, והעלאת ארטיפקטים נופלת בגלל זמן המתנה. לטפל בזה על ידי כתיבה מחדש של ההיסטוריה בדיעבד זה מסוכן — ענפים, הפניות לקומיטים וחתימות נשברים, וכל המפתחים צריכים לשכפל את הפרויקט מחדש. ובכל זאת קבצים גדולים במאגר נשארים פרקטיקה רגילה, כי «ככה זה התפתח היסטורית».
Git LFS משנה את מודל האחסון עצמו: אובייקטים גדולים עוברים לאחסון נפרד, ובמאגר נשארים מצביעים — הם שוקלים בתים בודדים ולא גדלים מגרסה לגרסה. נסקור איך זה נסגר בפועל: מבחירת מקום האחסון והעברת היסטוריה שכבר הצטברה ועד לכללים שמונעים מארטיפקטים בינאריים חדשים לחלוף על פני LFS.
מה משתנה אחרי הוצאת קבצים גדולים מהמאגר
קבצים גדולים — בניות בינאריות, מדיה, פריסות, משקלי מודלים — מחזיקים את היסטוריית המאגר כבני ערובה: כל עריכה מוסיפה עותק מלא חדש של האובייקט, והקודמים נשארים בהיסטוריה לנצח. ב-Git LFS התוכן מוחלף במצביע, והנתונים עצמם עוברים לאחסון אובייקטים נפרד. המאגר שוב שומר שינויים טקסטואליים, ולא ארטיפקטים.
ההשפעה המדידה הראשונה — זמן השכפול. מפתח ומכונת בנייה מושכים רק גרסאות עדכניות של קבצים גדולים, ולא את כל ההיסטוריה שלהם. עותק העבודה מפסיק להתנפח: במקום גיגה-בייטים של בינאריים — קילו-בייטים של מצביעים, ומעבר בין ענפים נמדד בשניות גם בערוץ צר.
השנייה — האצת הבנייה. שלבים שקודם חיכו להורדת ארטיפקטים מההיסטוריה מקבלים אותם לפי דרישה, והמטמון הביניים לא נבנה מחדש במלואו בכל קומיט. במקביל נעלם סוג שלם של כשלים: חריגה ממגבלת האחסון, ניתוק בזמן העלאת אובייקט גדול, נפילת משימה בגלל חוסר מקום.
יציבות הבנייה כאן היא לא עניין של מהירות, אלא של יכולת חיזוי: המשימה נופלת בגלל שגיאה בקוד, ולא בגלל התשתית. המחיר לכך הוא משמעת סביבה: לקוח LFS בתמונת מכונת הבנייה, כללי אחסון וגיבויים לאחסון האובייקטים. ההיסטוריה נשמרת במלואה — נסיגה לכל גרסה קודמת נשארת אפשרית.
אפשרויות אחסון לקבצים גדולים: השוואת גישות
הבחירה באופן אחסון הקבצים הגדולים קובעת כמה מהר המאגר משוכפל, כמה תופסת ההיסטוריה ומה יקרה אם שירות חיצוני לא יהיה זמין. להלן ארבע אפשרויות שבאמת בוחרים ביניהן בפרויקטים.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| אחסון רגיל במאגר | בינאריים קטנים שנחוצים לכל הצוות ומשתנים לעתים רחוקות | כל גרסה נשארת בהיסטוריה לנצח: השכפול גדל יחד עם מספר העריכות, ולא עם גודל הקובץ |
| אחסון מצביעים ייעודי | קבצים כבדים בזרימת עבודה רגילה: גרפיקה, מדיה, מערכי נתונים לבדיקות | דרושים שרת מוגדר וכלל ניקוי; בלעדיו נפח האחסון גדל ללא בקרה |
| אחסון קבצים חיצוני | נפחים גדולים, הפרדת הרשאות, אספקת נתונים ברשת | המאגר והבנייה תלויים בזמינות של גורם אחר ובערוץ רשת |
| אחסון מקומי | חומרי טיוטה שלא צריך להעלות לנגישות משותפת | אין יכולת שחזור: מהנדס אחר או סוכן בנייה לא יקבל את אותם נתונים |
בפועל, לרוב עובד שילוב: Git LFS מכסה את מה שנחוץ לכולם בזרימת הבנייה, אחסון חיצוני — מדיה וארטיפקטים גדולים, ואחסון מקומי — טיוטות. אנחנו בודקים אילו קבצים משתתפים בבנייה, אילו משתנים לעתים קרובות, ואילו נחוצים רק זמנית, ועל בסיס זה מפזרים אותם בין האחסונים.
איך מתבצעת ההטמעה: מאודיט ועד מסירת נהלים
הטמעת Git LFS היא לא התקנה של תוסף, אלא החלפה של סכימת האחסון לכל הארטיפקטים הכבדים במאגר. סדר העבודה בנוי כך שעד רגע המעבר אף רוויזיה לא הולכת לאיבוד והצוותים לא שוהים בחוסר מעש. להלן איך בנוי התהליך מאודיט ועד מסירת נהלים.
- אודיט של המאגר. מחשבים נפח והיסטוריה: אילו סיומות נותנות את עיקר המשקל, כמה רוויזיות מושפעות, מי וכמה פעמים מבצע קומיטים של קבצים גדולים. התוצר — רשימת נתיבים שאנחנו מעבירים לאחסון חיצוני, והערכת גודל אחרי ההעברה.
- תוכנית הגירה. קובעים אם לכתוב את ההיסטוריה מחדש במלואה או להעביר רק קומיטים חדשים, ומתאמים חלון שבו עריכות לענף הראשי מושהות. מקבעים סדר נסיגה אם הבדיקה לא תעבור.
- הגדרת אחסון. פורסים או מחברים אחסון אובייקטים, מגדירים כללי אחסון: תוחלת חיים של גרסאות ישנות, מגבלות גודל לאובייקט, הפרדת הרשאות לפי תפקידים. רושמים מסננים וכללים בקונפיגורציה של המאגר.
- העברת צוותים ובנייה. מעדכנים הגדרות אצל המפתחים ועל סוכני הבנייה, כך שקבצים כבדים יורדו לפי דרישה, ולא בכל שכפול. עבור מאגר של עשרות גיגה-בייטים זה מקצר את זמן קבלת עותק העבודה פי כמה.
- בדיקת שלמות. משווים סכומי ביקורת של אובייקטים לפני ואחרי ההגירה, בונים את הפרויקט מאפס על שכפול נקי, בודקים זמינות של רוויזיות ותגים ישנים. רק אחרי זה סוגרים את חלון ההגירה.
- מסירה לתפעול. מוסרים נהלים והוראות, מגדירים התראות על גידול באחסון, מעבירים הדרכה לצוות ומקבעים אחראי לכללים.
כללי מצביעים, הרשאות ותיעוד הפרויקט
ההטמעה נחשבת להסתיים לא כשקבצים גדולים מפסיקים להאט את השכפול, אלא כשלצוות יש סט ארטיפקטים שמאפשר לשנות כללים בלעדינו. להלן מה שהלקוח מקבל בסיום ומה שאנחנו עוברים עם המהנדסים שלו במסירה.
- קובץ כללי התאמה — אילו סיומות ונתיבים עוברים ל-LFS ואילו נשארים באינדקס הרגיל. קטגוריה חדשה של קבצים בינאריים נוספת בעריכת שורה אחת, ולא בבנייה מחדש של המאגר.
- קונפיגורציה של מצביעים ואחסון — כתובת אחסון האובייקטים, מצב נעילה לקבצים ש«לא ניתנים למיזוג», מגבלות על העברות מקבילות. אותה הגדרה בדיוק על מכונות העבודה ועל סוכני הבנייה, כדי שההתנהגות לא תתפצל.
- סכמת אחסון — היכן פיזית נמצאים האובייקטים, איך בנוי המטמון בסוכנים, אילו כללי ניקוי חלים: כמה רוויזיות מחזיקים, מה נמחק ראשון, איך משחזרים אובייקט אחרי מחיקה בטעות.
- מדיניות הרשאות — אילו חשבונות וטוקנים יכולים לקרוא ולכתוב אובייקטים, איך מוענקות הרשאות לקבלנים ולצוותים חיצוניים, היכן נשמרים סודות הבנייה ואיך הם מוחלפים.
- הוראות לצוות — איך נכון לשכפל מאגר עם קבצים גדולים, מה לעשות כשההעברה נפסקת, איך לפנות מקום בעותק העבודה, איך לשים נעילה על קובץ שאסור למזג אוטומטית.
- נוהל עבודה — סדר עריכת קובץ הכללים: מי מאשר העברת קטגוריית קבצים חדשה ל-LFS, איך בודקים שמצביע לא נשאר בלי האובייקט שלו, מי אחראי על גידול האחסון.
- בדיקות בבנייה — בקרה אוטומטית של «מצביע בלי אובייקט» ומגבלת גודל, כך שהבעיה תתגלה בשלב הבנייה, ולא בשכפול אצל מפתח חדש.
הסט הזה מספיק כדי שהצוות שלכם ישנה את הכללים: ירחיב את רשימת הקבצים במעקב, יחבר אנשים חדשים ומאגרים חיצוניים, בלי לחזור אלינו על כל הגדרה. תארו את המשימה שלכם — נחזור עם הערכה ותוכנית מסירה.
מקרה בוחן: איך פרקנו עומס מהמאגר והאצנו את הבנייה
המאגר של המוצר גדל לא בגלל הקוד: לצד קוד המקור היו פריסות ממשק, הקלטות וידאו לבדיקות ומערכי נתונים לבדיקת ייצוא. שכפול על מכונה נקייה ארך כ-40 דקות, עותק העבודה היה כבד פי ארבעה עשר מהקוד עצמו, ומתוך 30 דקות בנייה 12 הוקדשו להורדת ההיסטוריה. הייצוא נפל בערך בכל ניסיון חמישי — בדרך כלל בחריגה מזמן ההמתנה בעת קבלת קבצים מצורפים.
הפרדנו זרמים: הקוד נשאר בהיסטוריה הראשית, קבצים כבדים הועברו ל-Git LFS עם כללים ב-.gitattributes לפי מסכות סוגים, ומערכי בדיקה שמשתנים לעתים רחוקות הועברו לאחסון נפרד עם ניהול גרסאות לפי הפניה.
לתמונת הבונה הוספנו טעינה מוקדמת של אובייקטים נחוצים, כדי ששלבים לא ימשכו אותם אחד-אחד דרך הרשת, הגבלנו הרשאות כתיבה והפעלנו בדיקה לקבצים מעל הסף — קומיט כבר לא יכול להכביד על ההיסטוריה בלי לשים לב.
אחרי ההעברה השכפול התקצר ל-4 דקות, הבנייה ל-11, ועותק העבודה של המפתח קטן פי ארבעה עשר. נפילות בייצוא ברבעון — בודדות במקום כל ריצה חמישית, ושיעור המסירות הכושלות ירד מתחת לאחוז אחד.
המאגר עכשיו גדל בצפוי: מדיניות האחסון מגבילה את גודל אחסון השירות, ונסיגה לגרסה קודמת של נכסים נשארת זמינה.
במה Git LFS שונה מאחסון רגיל במאגר
באחסון רגיל, ארטיפקט בינארי — דימוי, מערך נתונים, וידאו — נכנס לקומיט לאותה בסיס אובייקטים כמו הקוד. דחיסת דלתא, ששומרת על היסטוריית קבצי טקסט קומפקטית, לא עובדת על בינאריים: כל גרסה נשמרת כעותק מלא. מחיקה מהענף הנוכחי לא מפנה דבר — האובייקט נשאר בהיסטוריה, והשכפול מושך את כל הרוויזיות שלו.
Git LFS משנה את הסכימה: למאגר נכנס מצביע טקסטואלי — סוג אובייקט, גיבוב וגודל — והבתים עצמם עוברים לאחסון נפרד. היסטוריית הקומיטים, הענפים והתגים נשארים במקום, אבל עץ הגרסאות שוקל כמו שורות המצביעים. ההבדל בין הגישות מתגלה לא בפקודות של המפתח, אלא במה בדיוק נמשך ברשת בזמן שכפול ובנייה.
שחזור עץ העבודה גם הוא שונה. שכפול רגיל מוריד את כל ההיסטוריה של קבצים גדולים, כולל גרסאות שנמחקו לפני הרבה גרסאות; מצב המצביעים מספק רק את האובייקטים הנחוצים לעץ הנוכחי.
המחיר — תלות ברשת: בלי גישה לאחסון, במקום הקבצים יישארו ממלאי מקום טקסטואליים, ואת המסננים צריך להגדיר לפני השכפול הראשון.
בעת יצירת ענפים, קבצים זהים עוברים דה-דופליקציה לפי גיבוב: עותק אחד בענף הגרסה ובענף העבודה נשמר פעם אחת, בעוד בסכימה רגילה האובייקט חי בכל הענפים ומאט את מעבר ההיסטוריה.
מגבלות האחסון לא נעלמות אלא עוברות: מופיע שרת נפרד, מכסות לפי נפח ותעבורה, מדיניות מחזור חיים משל עצמו וגיבוי שאינו תלוי במאגר.
בצינור הבנייה זה נותן טעינה סלקטיבית של נתיבים נחוצים במקום היסטוריה מלאה — הכנת הסביבה מואצת, אבל שלב טעינת האובייקטים מוגדר במפורש, אחרת הבנייה תקבל בשקט מצביעים במקום קבצים.
ספקות לגבי אחסון, הרשאות ושחזור קבצים
הטמעת LFS לעתים רחוקות מסתיימת בפקודת מעקב אחת. בהיסטוריה כבר נמצאים אובייקטים בינאריים כקבצים רגילים, ולכן נדרשת תוכנית העברה: בלעדיה שכפול חדש מושך ענפים ישנים במלואם.
קבוצת הספקות השנייה — תפעול: היכן פיזית נמצאים האובייקטים ומי יש לו גישה אליהם. בנפרד אנחנו בודקים את זמינות האחסון ברגע הבנייה: סוכן הבנייה או נעצר עם שגיאה מפורשת, או לוקח אובייקטים מהמטמון המקומי — זה נקבע מראש, ולא ברגע התקלה.
מה יקרה עם ענפים ישנים והיסטוריה?
ענפים ישנים מעבירים במעבר נפרד: אובייקטים מוצאים מההיסטוריה, ובמקומם נכנסים מצביעים מ-.gitattributes. ענפים לא נתמכים בדרך כלל סוגרים ומשאירים כארכיון — לכתוב אותם מחדש יקר יותר מהתועלת.
איך לתת גישה לקבלנים חיצוניים?
לקבלן מוציאים חשבון נפרד: קריאה בלבד ורק מרחב האובייקטים הנחוץ. הוא מוריד קבצים גדולים, אבל לא יכול לדרוס של אחרים, והגישה נשללת אחרי מסירת העבודה.
איך לשחזר קבצים גדולים מגיבוי?
עותק של המאגר חסר תועלת — יש בו מצביעים, לא הנתונים עצמם. לנוהל מוסיפים גיבוי של אחסון האובייקטים, ואת השחזור בודקים על שכפול בדיקה, כדי לא לגלות עותק חסר ברגע התקלה.
נדבר על המשימה: סכמת אחסון וסדר ההגירה
לדיון הלקוח מביא רשימת מאגרים והבנה אילו קבצים נכנסים לשם: בניות בינאריות, מודלים, מדיה, ייצוא ממערכות חיצוניות. מועילים כללי המעקב הנוכחיים, גודל עותק העבודה אצל המפתחים ותלונות על זמן שכפול או בנייה — מהם מיד רואים היכן צוואר הבקבוק ומה בדיוק אנחנו מאיצים.
מזה נבנית תוכנית עבודה: סכמת אחסון (היכן ממוקם אחסון LFS, אילו מסכות קבצים נכנסות למעקב, אילו מגבלות קובעים), סדר הטמעה לפי מאגרים והערכת לוחות זמנים. לא מתחילים בהגירה המונית — קודם פיילוט על מאגר אחד, כדי לבדוק שכפול, בנייה ונסיגה בצינור חי.
- מיפוי מלאי של מאגרים וקבצים שמועברים ל-LFS;
- סכמת אחסון: מיקום האחסון, כללי מעקב לפי מסכות, מגבלות גודל;
- סדר הטמעה: מאגר פיילוט, ואחר כך השאר בגלים;
- הגירת היסטוריה וניקוי אובייקטים גדולים שכבר נכנסו לקומיטים;
- עריכות בצינור הבנייה, מטמון התלויות וכללי הגישה לסוכנים;
- תפקידים והרשאות: מי קורא ומי כותב לאחסון, איך מונפקים מפתחות;
- תוכנית נסיגה, אם השכפול או הבנייה נפגעים אחרי המעבר.
נחזור עם הערכת לוחות זמנים והיקף העבודה: שלבים, מה אנחנו עושים, מה נדרש מצד הלקוח, קריטריוני קבלה — זמן שכפול, גודל עותק העבודה, משך הבנייה. תארו את המשימה — נחזור עם הערכה ותוכנית.







