ענפים מתפצלים

ההתנהגות של git pull השתנתה בגירסה 2.27 של גיט, ואז שוב בגירסה 2.33, במטרה להפוך את הפקודה לקצת פחות הרפתקנית.

הפקודה git pull היא למעשה שילוב של שתי פקודות: היא קודם כל מושכת את כל הקומיטים מהענף המרוחק (fetch), ואחר כך היא משלבת אותם בענף שלנו - ברירת המחדל באמצעות merge.

הבעיה בשימוש ב merge כשמושכים קומיטים מענף מרוחק היא שלא כולם רוצים דווקא את ההתנהגות הזאת:

  1. אם היה ריבייס בענף המרוחק, merge עלול לפספס את זה ולהכניס קומיטים כפולים.

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

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

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

$ git config pull.rebase false

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

$ git config pull.rebase true

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

$ git config --global pull.ff only

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

$ git log --oneline --graph
* 036277f (HEAD -> main) d4
* a82b7c8 d3
* b69d6c1 d2
* bb4f039 d1
* 244c3c2 c1
* 8a9cf3b first commit

והענף המרוחק נראה ככה:

$ git log --oneline --graph origin/main
* 6bf42ea (origin/main) c4
* 589e8f4 c3
* 955b0f3 c2
* 244c3c2 c1
* 8a9cf3b first commit

ההסתעפות היא בקומיט c1, כאשר הענף המקומי המשיך ממנו ל d1 והענף המרוחק המשיך ממנו ל c2.

ניסיון עכשיו להפעיל pull נותן את השגיאה:

$ git pull

hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.

נבחר את ברירת המחדל האהובה עליי:

$ git config pull.ff only

ועכשיו אני מנסה שוב להפעיל pull:

$ git pull

fatal: Not possible to fast-forward, aborting.

עדיין כישלון אבל לפחות הפעם פחות חופר. עכשיו אני יודע שאי אפשר לעשות fast-forward ונדרשת החלטה - האם אני רוצה לעשות merge רגיל או rebase. בשביל להחליט לבצע merge רגיל אני מפעיל:

$ git pull --ff

או בשביל להחליט שאני מעדיף rebase אני יכול להפעיל:

$ git pull --rebase

הבחירה בין שתי האופציות כבר תלויה במקרה הספציפי ובאיך אני רוצה שהשינויים שלי ישתלבו עם השינוי המרוחק.

הנה במאגר שלי התוצאה אחרי הפעלת merge:

$ git pull --ff
$ git log --oneline --graph

*   78831a1 (HEAD -> main) Merge branch 'main' of https://github.com/ynonp/divergent-branches
|\
| * 6bf42ea (origin/main) c4
| * 589e8f4 c3
| * 955b0f3 c2
* | 036277f d4
* | a82b7c8 d3
* | b69d6c1 d2
* | bb4f039 d1
|/
* 244c3c2 c1
* 8a9cf3b first commit

או אחרי הפעלת rebase:

$ git pull --rebase
$ git log --oneline --graph

* f0a0b30 (HEAD -> main) d4
* 07cd430 d3
* e95dcad d2
* 3f03f93 d1
* 6bf42ea (origin/main) c4
* 589e8f4 c3
* 955b0f3 c2
* 244c3c2 c1
* 8a9cf3b first commit