ריבייס מהיר יותר עם rerere

29/09/2018

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

1. איך הגענו לכאן

ניקח לדוגמא אתר שמורכב מ-3 הקבצים הבאים בענף master. הקובץ demo.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
  </head>
  <body>

  </body>
</html>

הקובץ demo.css:

body {
  background: orange;
}

h1 {
  color: white;
  font-size: normal;
}

והקובץ demo.js:

document.body.textContent = 'Hello World';

קל לראות שיש פה בעיה וקובץ ה JavaScript לא נטען מקובץ ה HTML אבל זאת לא הנקודה עכשיו. נפתח ענף חדש בשם dev כדי להוסיף קוד:

$ git checkout -b dev

ועכשיו נתקדם עם שני הענפים. ב master נתקן את הבעיה ונוסיף הפניה ל JavaScript כך שקובץ ה demo.html של master יראה כך:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <script src='demo.js'></script>
  </head>
  <body>

  </body>
</html>

ובאותו זמן בענף dev נעדכן את הקוד של demo.html לגירסא הזו:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <link rel='stylesheet' href='demo.css' />
  </head>
  <body>

    <script src='demo.js'></script>
  </body>
</html>

נמשיך ונבצע קומיט ב dev לגירסא שלו ועכשיו יש לנו שני ענפים עם קונפליקט בקובץ demo.html.

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

2. מהו Control Merge ואיך הוא עוזר לנו לשמור שלא התרחקנו יותר מדי

טכניקה אחת שעוזרת להשאיר ענף לא-יותר-מדי רחוק מ master נקראת Control Merge. הרעיון בשיטה הוא שכל כמה זמן נעשה מיזוג מ master לענף שלנו וכך כל הזמן יהיו אצלנו העדכונים החדשים מ master.

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

3. מה הבעיה עם Control Rebase

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

מה שאנחנו רוצים זה לקבל אפקט דומה ל Control Merge אבל בלי תוצאת הלוואי של קומיטים מיותרים במאגר ובלי לשכתב קומיטים שאולי כבר שיתפנו עם אחרים.

4. איך rerere עוזר לנו לצמצם Control Merges

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

  1. בכל שבוע ניצור Control Merge ונבטל אותו.

  2. אם יהיו קונפליקטים בתהליך המיזוג נקליט איזה קונפליקטים היו ואיך פתרנו אותם עם rerere.

  3. כך בשבוע הבא לא נצטרך לפתור את אותם קונפליקטים.

5. דוגמא: שימוש ב git rerere כדי לצמצם את הקונפליקטים

בשביל להשתמש ב rerere עלינו תחילה לוודא שמנגנון ההקלטה דולק באמצעות הגדרת הערך המתאים בקונפיגורציה:

$ git config --global rerere.enabled true

כעת נוכל להשתמש ב Control Merge כדי לשלב בין קובץ ה HTML בשני הענפים:

$ git merge master
Auto-merging demo.html
CONFLICT (content): Merge conflict in demo.html
Recorded preimage for 'demo.html'
Automatic merge failed; fix conflicts and then commit the result.

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

$ git commit -a -m 'fixed conflicts'
Recorded resolution for 'demo.html'.
[dev c5476c9] fixed conflicts

עכשיו אפשר לבטל את ה Control Merge שלנו:

$ git reset --hard HEAD^

נוסיף שינויים לפרויקט בקבצי ה CSS ו JS ואחרי שבוע נבצע Control Merge נוסף:

$ git merge master
Auto-merging demo.html
CONFLICT (content): Merge conflict in demo.html
Resolved 'demo.html' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.

גיט באופן אוטומטי זכר את התיקון והפעיל אותו על אותו קונפליקט. למעשה ה rerere מחליף את ה merge commit שהיה שמור ומלכלך לנו את ההיסטוריה. מכאן ועד עולם בכל פעם שגיט ייתקל בקונפליקט הזה הוא ידע איך לתקן אותו ולכן כשיגיע הרגע למזג את master האמיתי יהיו לנו הרבה פחות קונפליקטים להתעסק איתם.