הגדרת מאגר Git ואסטרטגיית ענפים (Git Flow)

תיקונים דחופים במהדורה, קונפליקטים במיזוגים וקוד שמפחיד לשחרר — תמונה מוכרת? אנו מגדירים Git Flow מקצה לקצה: מתכננים סכמת ענפים, כללי הגנה ותקנות שחרור, כדי שמהדורות יצאו בצורה צפויה. אישורים ומיזוגים ידניים פוחתים פי כמה.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

הסתעפות לפי Git Flow: מהיכן מתחילות תקלות בשחרורים

Git Flow נשען על הסכמה שלכל ענף יש תפקיד משלו: ענפים קבועים לפיתוח ולמצב ששוחרר, וענפים זמניים למשימות, לייצוב ולתיקונים דחופים. כל עוד כולם מקפידים על הכלל, תרשים הענפים נקרא במבט אחד, והיסטוריית הקומיטים עונה על השאלה מה בדיוק נכנס לשחרור. הסטייה הראשונה ממנו שוברת את שתי ההבטחות האלה.

תקלה טיפוסית מתחילה בכך שממזגים משימה לענף הלא נכון או מתקנים ישירות בענף היציב. מיד לאחר מכן מופיעים מיזוגים מענפים זרים והעברות של קומיטים בודדים, כדי "למתוח" את השחרור למצב הרצוי.

היסטוריית הקומיטים הופכת לשזירה שבה כבר אי אפשר לשחזר אילו שינויים שייכים לשחרור ואילו נכנסו אליו במקרה.

כך נולד שחרור לא יציב. לענף המשימה אין סימן מובהק למוכנות, ובזמן הבנייה נכנסים אליו כל מה שהספיקו למזג עד רגע הקפאת הגרסה — יחד עם עבודה שלא הושלמה ובדיקות שהושבתו זמנית. הרגרסיה צצה בפרודקשן, וחיפוש הסיבה מתחיל בפירוק ידני של ההסתעפויות במקום בקריאת ההיסטוריה.

סיפור נפרד — תיקון דחוף. לפי אסטרטגיית ההסתעפות הוא יוצא מהמצב ששוחרר וחוזר מיד לשני ענפים ארוכי־חיים. אם עושים אותו ישירות בענף השחרור, התיקון הולך לאיבוד במיזוג הבא, ואותו פגם חוזר כעבור שחרור־שניים.

להלן נבחן כיצד זה נסגר: אילו כללים מקובעים כדי שתרשים הענפים יישאר ניתן לבדיקה, והשחרור ייבנה רק משינויים מוכנים.

מה נותנת סכמת ענפים ומיזוגים מנוהלת

ענפים ארוכים שחיים במקביל שבועות הם מקור הכאב העיקרי: ככל שענף מתרחק מהקו הראשי זמן רב יותר, כך יותר תיקונים מצטלבים באותם קבצים. המיזוג מפסיק להיות פעולה של דקות והופך לפירוק קונפליקטים בזמן מיזוג, שבו מחצית מההחלטות יש לקבל מחדש. דווקא בשלב הזה השינויים הולכים לאיבוד לעתים קרובות ביותר.

סכמה מנוהלת של Git Flow מגדירה ענפים קצרים למשימה ונקודות מיזוג צפויות מראש: כללי המיזוג מתוארים מראש, בדיקות ובדיקות אוטומטיות עוברות לפני שהקוד נכנס לקו הראשי. זמן המיזוג מתקצר פי כמה — הפער מוגבל ונראה מראש, ולכן מערך שינויים טיפוסי מגיע לקו הראשי בלי פתרון קונפליקטים ידני.

בהמשך פועלת בקרת גרסאות: ההיסטוריה נקראת כרצף שחרורים ולא כמערך של קווים מקבילים. כל שינוי משויך למשימה ולשחרור, והחזרה לאחור — לנקודה מסוימת. מכאן שהשחרור צפוי: הרכב השחרור ידוע לפני הבנייה, ולא מתברר בדיעבד.

יציבות השחרור נמדדת בשיעור השחרורים שנדחו ובמחלקות כשל חוזרות. לאחר הטמעת הסכמה נעלמים כשלים בגלל תיקונים שלא מוזגו ותיקונים "נשכחים" בענף השחרור, ותיקונים חמים עוברים בנתיב קצר נפרד ואינם מתערבבים עם פיתוחים חדשים.

מודלים של הסתעפות: Git Flow וענפים קצרים

הבחירה בין מודל Git Flow מלא לענפים קצרים נקבעת לפי תדירות השחרורים ומספר הגרסאות הנתמכות. Git Flow מלא מחזיק ענפי פיתוח וענף ראשי קבועים, ענפי שחרור נפרדים וענפי תיקונים. הדבר נותן קו ייצוב מבודד: סביבת הבדיקות נבנית מענף השחרור בזמן שהצוות ממשיך לעבוד בענף הפיתוח.

המודל של ענפים קצרי־חיים בנוי אחרת: הענף חי בין כמה שעות ליומיים ומיד נמזג לענף הראשי, והשחרור יוצא ממנו עצמו. הסכמה פשוטה ומהירה יותר, אך נשענת על בדיקות אוטומטיות — בלי הרצה מהירה של בדיקות על כל מיזוג היא מתפרקת לפירוקי קונפליקטים ידניים.

מודל מתי מתאים מגבלות
Git Flow עם ענפי שחרור שחרורים מתוכננים, כמה גרסאות נתמכות, סביבת בדיקות נפרדת ענפים ארוכי־חיים מתרחקים, וגדל שיעור המיזוגים הידניים
ענפים קצרים, שחרור מהענף הראשי שחרורים תכופים, גרסה נתמכת אחת, בדיקות אוטומטיות בשלות נדרשים משמעת והרצה מהירה של בדיקות; בכיסוי חלש גדל הסיכון לתקלה
היברידי: ענפים קצרים בתוספת ענף שחרור לזמן הייצוב שחרורים תכופים עם בדיקה מחייבת בקו נפרד ענף השחרור חי זמן רב ושוב מתרחק מהענף הראשי

מגבלות המודל מתגלות בקנה מידה: ככל שיש יותר ענפי שחרור במקביל וככל שהמיזוגים נדירים יותר, כך מחיר הקונפליקטים גבוה יותר והרגרסיות נמצאות מאוחר יותר.

לכן אנחנו קובעים את המודל לפי קצב השחרורים: מתוכננים ונדירים — Git Flow מלא; תכופים עם גרסה אחת — ענפים קצרים עם ענף שחרור רק בזמן הייצוב.

סדר ההטמעה: מביקורת מאגר ועד מסירה

הטמעת Git Flow אינה יצירת ענף develop, אלא ארגון מחדש של האופן שבו הצוות משחרר שינויים. אנחנו מתחילים בספירת מלאי של המאגר ורק אחר כך מתכננים את הסכמה.

  1. ביקורת מאגר — מפת ענפים, תגים ומיזוגים על פני כמה שחרורים: ענפים תלויים, כללי גישה מתנגשים, מקומות שבהם הקוד נכנס לענף הראשי ישירות. בתום התהליך — רשימת צווארי בקבוק והערכת היקף העבודה.
  2. תכנון סכמת הענפים — לפי קצב השחרורים בוחרים מודל: ענפים ארוכים להכנת השחרור, קצרים למשימות, נפרדים לתיקונים דחופים בפרודקשן. קובעים שמות, סדר מיזוגים ומי שם את התג.
  3. תקנון — פורמט הודעות קומיט, דרישות לגודל השינוי, מדיניות ריוויו וסדר סנכרון ענפים ארוכים עם הענף הראשי, כדי שהפער לא יצטבר לקראת השחרור.
  4. הגדרת הגנה — בדיקות מחייבות לפני מיזוג, איסור שליחה ישירה לענפים מוגנים, דרישת בנייה מוצלחת וריוויו, הרשאות לשחרור. הכללים פועלים ברמת המאגר, לא על בסיס אמון.
  5. בדיקה על משימות אמיתיות — מריצים את הסכמה על משימות אמיתיות אך לא קריטיות: הכנת שחרור, תיקון דחוף, החזרה לאחור. רואים היכן התהליך מאט ומתקנים לפני המסירה.
  6. מסירה לצוות — תיעוד על ענפים, תבניות קומיט, הוראות למפתחים חדשים, פירוק תרחישים של «איך משחררים גרסה» ו«איך מחזירים לאחור».

קריטריון המוכנות — הצוות משחרר ומחזיר שחרור לאחור בלי עזרה חיצונית, והיסטוריית המאגר נקראת כלוג אספקות.

כללי הגנת ענפים, סכמות ותקנונים

כללי הגנה והרשאות תפקידים נשארים לרוב הסכמה בעל־פה: כל עוד בצוות שניים־שלושה מהנדסים, הסכמה מחזיקה מעמד בזכות משמעת. ברגע שמספר האנשים גדל או שמצטרף מבצע חיצוני, התיקון הדחוף הראשון עובר ישירות לענף הראשי — ואי אפשר יותר לבדוק מה בדיוק נכנס לשחרור. לכן אנחנו מתעדים את הגדרות ההגנה והתקנונים במאגר, ולא בהתכתבות.

מה נשאר אצל הלקוח לאחר ההגדרה:

  • כללי הגנת ענפים: כתיבה ישירה לענף הראשי ולענף השחרור סגורה, מיזוג אפשרי רק דרך בקשה עם ריוויו מחייב ובדיקות שעברו.
  • מטריצת הרשאות תפקידים: מי ממזג לשחרור, מי יכול להסיר חסימה במצב חריג, מי רק מציע שינויים — עם הפרדה לפי צוותים וקבלנים.
  • תקנון שחרור: מאיזה ענף נחתך ענף השחרור, אילו תגים שמים, ובאיזה סדר השינויים חוזרים לענף הראשי.
  • נוהל תיקונים דחופים: הענף נחתך מהתג של השחרור האחרון, ואחר כך חייב להתמזג חזרה לענף הראשי ולענף השחרור הנוכחי — אחרת התיקון הולך לאיבוד בשחרור הבא.
  • סכמת החזרה לאחור: כללים להצבת תגים וסדר החזרה לשחרור הקודם בלי עריכת קוד ידנית בשרת.
  • תיעוד הסתעפות: סכמת Git Flow עם תיאור מחזור החיים של כל ענף, כללי שמות ותוחלת החיים של הענף עד מחיקתו.
  • תבניות בקשות מיזוג וקריטריוני מוכנות: מה צריך להיות בתיאור, אילו בדיקות נחשבות מחייבות.

התקנונים מונחים במאגר לצד הקוד — מהנדס חדש קורא את הכללים, ולא מברר אותם בצ'אט.

מקרה בוחן: פחות קונפליקטים ותיקונים דחופים בשחרור

צוות מוצר של תשעה מהנדסים ניהל שתי קווי פיתוח במאגר אחד: תיקונים נכנסו ישירות לענף הראשי, והכנת השחרור חייתה בענף נפרד ארוך־חיים. עד רגע המיזוג הפער הגיע למאות קבצים, הקונפליקטים נפתרו ידנית בידי שני מהנדסים ואכלו עד יומיים. הגרסה שוחררה אחת לשבועיים, ובערך כל שחרור שלישי לווה בתיקון דחוף כבר בסביבה הפעילה.

מה גילה הניתוח

ניתוח ההיסטוריה ב-GitLab הראה שהבעיה לא הייתה בקפדנות של המהנדסים. לענפים לא היו כללי שמות ותוחלת חיים מקסימלית, הענף הראשי לא היה מוגן — פוש ישיר עבר בלי ריוויו, ובדיקות אוטומטיות הופעלו רק על ענף השחרור. בגלל זה פגמים צצו בשלב הכנת הגרסה, כשמחיר התיקון מקסימלי, וסיבת הקונפליקט אבדה בזרם התיקונים.

מה שינו

עברו לענפים קצרי־חיים לכל משימה עם תוחלת חיים של שניים־שלושה ימים והגנו על הענף הראשי: מיזוג רק דרך בקשת מיזוג, ריוויו מחייב של מהנדס שני, בדיקות ירוקות, היסטוריה ליניארית בלי נקודות מיזוג מיותרות.

לתיקונים דחופים הקימו ענף נפרד מהתג האחרון עם מיזוג חוזר מחייב לענף הראשי — התיקון כבר לא אבד בשחרור הבא.

כעבור חצי שנה הקונפליקטים בזמן מיזוג הצטמצמו לאחד־שניים לשחרור במקום שמונה עד עשרה, הכנת הגרסה אורכת כמה שעות במקום שלושה ימים, ותיקונים דחופים בסביבה הפעילה הפכו לבודדים במקום כל שחרור שלישי. לא היו כלל החזרות לאחור בגלל שינויים שאבדו במיזוג.

מה לעשות אם לצוות כבר יש סכמת ענפים משלו?

סכמת ענפים משל עצמך אינה סיבה לשבור אותה. אם במאגר כבר הצטברה היסטוריה, מוצבים תגי שחרור וחיים עשרה ענפי עבודה, אסור לכתוב הכול מחדש לפי Git Flow: הקשר בין קומיטים לארטיפקטים שנבנו ייקרע, והפריסה תיעצר בזמן פירוק הקונפליקטים. לכן אנחנו משנים כללים רק לשינויים חדשים — ענפים ישנים ממשיכים לחיות בשקט לפי ההיגיון הקודם.

נקודת המעבר נקבעת במפורש. התג היציב האחרון הופך לבסיס הענף הראשי, ממנו פותחים ענף פיתוח, ומכאן ואילך ענף עבודה חי לא יותר ממשימה אחת: ענף קצר — מיזוג צפוי מראש. ענפים ארוכים בזמן המיגרציה נסגרים לפושים ישירים, כדי שלא יגיעו אליהם שינויים בעקיפת בדיקות.

היסטוריה משותפת אנחנו לא כותבים מחדש: בלי ארגון מחדש ודחיסה של קומיטים שכבר מוזגו, רק מיזוגים חדשים מעל. כך נשמרת החזרה לתג הקודם ולא נשברות עותקים מקומיים אצל המהנדסים — לאחר העדכון הם לא צריכים לבנות מחדש ענפי עבודה. בנפרד אנחנו מסכימים על הקפאה: כל עוד המעבר נמשך, ענפים ארוכים לא נחתכים באמצע.

בודקים את המעבר לפי סימנים מדידים: הזמן מחיתוך המשימה ועד הכניסה לשחרור, מספר הקונפליקטים במיזוג, שיעור השינויים שעברו בדיקות לפני הכניסה לענף הראשי. אם הקונפליקטים לא גדלים והשחרור לא נדחה, הסכמה נקלטה — בהמשך אפשר להחמיר אותה: הגנת ענפים, בדיקות מחייבות, תגים לכל שחרור.

ספקות טכניים: ענפים, מיזוגים והחזרות לאחור

הספקות הנפוצים ביותר לפני מעבר ל-Git Flow אינם נוגעים לסכמת ההסתעפות, אלא למצבי קצה: ענפים ארוכי־חיים, עבודה שלא הושלמה עד מועד השחרור, הרשאות גישה והחזרה לאחור. כל אחד מהם נסגר בתקנון, ולא בגבורה של מהנדסים.

ענפים ארוכי־חיים מתרחקים עם הזמן מהקו הראשי: ככל שענף השחרור חי בנפרד זמן רב יותר, כך הקונפליקטים במיזוג גדולים יותר. אנחנו מגבילים את תוחלת החיים שלו לחלון השחרור ומסנכרנים עם הענף הראשי מדי יום — הקונפליקטים מפורקים למנות קטנות, ולא במאמץ אחד לפני השחרור.

עבודה שלא הושלמה אינה חוסמת את השחרור: שינויים לא גמורים נשארים בענפים קצרים נפרדים וממתינים למחזור הבא. לשחרור נכנס מה שמוכן, ולא מה שהספיק להבשיל עד המועד.

הרשאות גישה למאגר אנחנו בונים לפי הרשאות מינימליות: הענף הראשי וענפי השחרור מוגנים, שינויים נכנסים אליהם רק דרך מיזוג בדוק, ותיקונים ישירים סגורים. הרשאות ניתנות לפי תפקידים ומבוטלות בעת החלפת משימות.

החזרת שינויים לאחור היא פעולה שגרתית. השחרור מסומן בתג, ולכן החזרה למצב הקודם אורכת דקות ואינה כותבת מחדש את ההיסטוריה: קבוצת שינויים מסוימת מבוטלת, ושאר הענפים אינם נפגעים.

נדרש תיקון דחוף בעיצומו של השחרור — מה עושים?

התיקון יוצא בענף קצר מהקו הראשי, עובר בדיקה ונכנס לשחרור הקרוב. בענף השחרור הנוכחי לא נוגעים — אחרת ייכנסו אליו שינויים שלא נבדקו.

אפשר להחזיר שחרור לאחור תוך שמירה על חלק מהשינויים?

כן. השחרור מוחזר לאחור לפי התג בשלמותו, לאחר מכן השינויים הרצויים מועברים לענף נפרד ועוברים בדיקה רגילה. היסטוריית המאגר נשארת קריאה.

נדון בסכמת ההסתעפות ושחרור הגרסאות שלכם

כדי לחזור עם הערכה ותוכנית, ולא עם מילים כלליות, מספיק להביא לדיון את מה שכבר קיים במאגר שלכם. להלן מה שעוזר ביותר: לפי הנקודות האלה רואים גם את היקף העבודה וגם את צווארי הבקבוק שבגללם השחרורים מתקדמים לאט.

  • סכמת ההסתעפות הנוכחית: אילו ענפים חיים זמן רב, אילו נוצרים למשימה, והיכן מתבצעת העבודה על הגרסה. ממנה רואים היכן נוצרים מיזוגים כבדים ומדוע תיקונים הולכים לאיבוד.
  • סדר השחרור: מי ובאיזה רגע יוצר את ענף השחרור, איך נכנסים אליו תיקונים, ואיך השחרור חוזר לענף הראשי. זה החלק המרכזי במעבר ל-Git Flow.
  • מספור גרסאות ותגים: לפי איזה כלל מוקצים המספרים, היכן מוצב התג והאם הוא תואם למה שבאמת פועל בסביבה.
  • בדיקות לפני מיזוג: אילו בדיקות מחייבות, מי נותן אישור למיזוג, וכמה זמן בדרך כלל ממתין מחבר השינוי לתשובה.
  • תפקידים והרשאות גישה: מי ממזג לענפים מוגנים, מי מפרסם תגים, איך מונפקים ונשמרים מפתחות, והאם יש גישה משותפת.
  • בנייה ואחסון ארטיפקטים: במה נבנה התוצר, היכן הוא נשמר והאם אפשר לפי התג לקבל באורח חד־משמעי את אותה בנייה.
  • סדר החזרה לאחור: איך אתם חוזרים לשחרור הקודם וכמה זמן זה לוקח כיום.

לפי התשובות האלה אנחנו מתארים את היקף העבודה, סדר ההטמעה ונקודות שבהן צריך לשנות את הסכמה בלי לעצור שחרורים. תארו את המשימה — נחזור עם הערכה ותוכנית למאגר שלכם.