היום למדתי: גיט ריבייס ו Merge Commits

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

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

1. נקודת ההתחלה

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

לוג טיפוסי של כזה מאגר יכול להיראות כך:

* 2fa580d (main) m12
* e026ae8 m11
* df2c82f m10
| * 8056178 (HEAD -> feature) d6
| * 778c404 d5
| *   a6fff44 Merge branch 'main' into feature
| |\
| |/
|/|
* | 80586ce hotfix1
| * a0d9a73 d4
| * 61e4ba0 d3
| * 6ae739d d2
| * 4a0a511 d1
|/
* 09ac820 c3
* ede6816 c2
* 8197d89 c1

ענף הפיתוח הוא feature והוא יוצא מ c3. באמצע היה לנו קומיט עם הטקסט hotfix1 ב main ואותו גם לקחנו לענף הפיתוח במיזוג שקרה אחרי d4, ואחרי אותו מיזוג הענף הראשי התקדם עם m10 ו m11, וענף הפיתוח התקדם עם d5 ו d6.

2. הצרות מתחילות

עכשיו אנחנו שולחים Pull Request ומי שעובר עליה לא מרוצה מכל הקומיטים. הם מבקשים למזג את d1, d2 ו d3 לקומיט יחיד ולשנות את הטקסט של d5. בשביל זה אני מפעיל פקודת ריבייס אינטרקטיבי:

$ git switch feature
$ git rebase -i --onto 09ac820 09ac820

אני בוחר את הפקודות הנכונות בריבייס ומוודא בלוג שהכל תקין:

$ git log --oneline --graph
* a877c74 (HEAD -> feature) d6
* b3247e7 fixed text for d5
* c574ede hotfix1
* ddb80ac d4
* 24b7b77 d1
* 09ac820 c3
* ede6816 c2
* 8197d89 c1

וממזג את התוצאה ל main:

$ git switch main
$ git merge feature

אבל עכשיו הלוג שלי שבור:

*   9b5f10a (HEAD -> main) Merge branch 'feature'
|\
| * a877c74 (feature) d6
| * b3247e7 fixed text for d5
| * c574ede hotfix1
| * ddb80ac d4
| * 24b7b77 d1
* | 2fa580d m12
* | e026ae8 m11
* | df2c82f m10
* | 80586ce hotfix1
|/
* 09ac820 c3
* ede6816 c2
* 8197d89 c1

הבעיה היא ש hotfix מופיע גם בענף הראשי וגם בענף ה feature בתור קומיט כפול, במקום לראות merge מסודר מ main ל feature.

3. תיקון פשוט עם rebase-merges

מתג הפלא לריבייס שמתקן את הבלאגן נקרא --rebase-merges או בקיצור -r. כשאני מעביר אותו מסך הריבייס האינטרקטיבי כולל גם את ה Merge Commits. זה נראה ככה:

$ git rebase --rebase-merges -i --onto 09ac820 09ac820

והתוצאה:

label onto

# Branch main
reset onto
pick 80586ce hotfix1 # empty
label main

reset onto
pick 4a0a511 d1 # empty
pick 6ae739d d2 # empty
pick 61e4ba0 d3 # empty
pick a0d9a73 d4 # empty
merge -C a6fff44 main # Merge branch 'main' into feature
pick 778c404 d5 # empty
pick 8056178 d6 # empty

הבלוק העליון מגדיר שאנחנו רוצים לקחת את הקומיט hotfix1 ולקרוא לו main. בבלוק התחתון פקודת ה merge אומרת שאנחנו צריכים ליצור merge commit עם הקומיט hotfix1.

כשאני בוחר את ההוראות הנכונות בריבייס הפעם אני מקבל את רשימת ההוראות:

label onto

# Branch main
reset onto
pick 80586ce hotfix1 # empty
label main

reset onto
pick 4a0a511 d1 # empty
s 6ae739d d2 # empty
s 61e4ba0 d3 # empty
pick a0d9a73 d4 # empty
merge -C a6fff44 main # Merge branch 'main' into feature
r 778c404 d5 # empty
pick 8056178 d6 # empty

ואחרי מיזוג ל main נקבל:

*   e4c1e0c (HEAD -> main) Merge branch 'feature'
|\
| * bcbb528 (feature) d6
| * 4ef376c fixed text for d5
| *   ed81791 Merge branch 'main' into feature
| |\
| * | 35db291 d4
| * | 7296128 d1
* | | 2fa580d m12
* | | e026ae8 m11
* | | df2c82f m10
| |/
|/|
* | 80586ce hotfix1
|/
* 09ac820 c3
* ede6816 c2
* 8197d89 c1

וזו בדיוק ההיסטוריה הנכונה פרט לקומיטים שערכנו עם הריבייס.