קרב: Git Rebase לעומת Merge
שאלה נצחית...
קרב מוות: Git Rebase מול (Squash) Merge!
האם עליי לבצע Rebase? או Squash Merge?
- האם זו העדפה אישית?
- תשובה: לא כשמעורבת צוות אחד או יותר! כל בחירה תשפיע על השימושיות של השנייה!
מדוע הנושא הזה מעורר להט דתי?
חלק מהמהנדסים משתמשים בידע של git (והטרמינל) כאות לרמת המיומנות היחסית שלהם. וכל פרקטיקה שקשורה לזהות/אגו שלנו יכולה להיות בלתי אפשרית לניתוח חסר פניות, שלא לדבר על שינוי.
גורמים נוספים כוללים ככל הנראה הטיית היכרות והטיית הישרדות (Survivorship Bias) שיכולות לטשטש עוד יותר את ההערכה וההנחות שלנו.
שאלת מפתח: מהי מטרתה של commit ב-git?
- האם אתה מבצע commit מוקדם ולעתים קרובות? תוך שימוש בגישת “נקודת ביקורת” או גיבוי?
- שבו הכל מתועד, אפילו התחלות כוזבות וניסויים? (למשל
git commit -am "Updated deps" && git push, חזור באופן קבוע) - אולי הודעות commit פחות חשובות לך מהקוד?
- שבו הכל מתועד, אפילו התחלות כוזבות וניסויים? (למשל
- או, האם ה-commit שלך הם יצירת אמנות מעוצבת ומסותתת בקפידה?
- אולי כל commit הוא יחידת עבודה עצמאית ואטומית? (למשל
git add package.json && git commit -m "Updated deps") - או, אתה פשוט לא יכול לסבול לוגים של commit “מבולגנים”?
- האם סקירות ה-PR שלך כוללות לעתים קרובות סקירה commit אחר commit?
- אולי כל commit הוא יחידת עבודה עצמאית ואטומית? (למשל
| 💡 אילו מודלים מנטליים אחרים מגדירים איך אתה רואה commits? אנא יידע אותי @justsml!
האם אתה חושב על git בצורה שמספקת את הערך הרב ביותר לך, לצוות שלך ולארגון שלך?
בהינתן שיש גישות שונות מאוד סביב אסטרטגיית commits, אין פלא שיש כל כך הרבה בלבול לגבי הדרך ה”נכונה” להשתמש ב-git.
תרחיש: יצירת תגית release מתוקנת
הבה נשווה את התהליך של יצירת תגית release תוך השמטת כמה commits אחרונים ב-main.
הדרך של Rebase
מודל מנטלי: “אני רוצה ליצור גרסה חלופית של היסטוריה קיימת. (למשל, עשיתי טעות לפני 16 merges, ואולי אצטרך שליטה מדויקת כדי לתקן אותה. בנוסף, עלול להיתקע בלולאה אינסופית לכאורה של קונפליקטים ו---continue.)”
- קבל את העדכני:
git checkout main&&git pull - צור ענף חדש:
git checkout -b release/hot-newness-and-stuff - התחל rebase אינטראקטיבי וכלול את ה-git ref למקום שאליו אתה רוצה לנסוע אחורה בזמן.
git rebase -i HEAD~6(הערה:HEAD~6הוא קיצור ‘git ref’ עבור6 commits ago) - הסר את ה-commit(s) הרצויים על ידי שינוי התחילית ל-
drop. שמור וסגור את העורך. - תקן קונפליקטים של מיזוג,
git add .&&git rebase --continue(אל תעשהgit commit). - חזור על הצעד הקודם עד להשלמה.
- תייג/דחוף באמצעות התהליך הנוכחי. דוגמה
git tag -a v1.2.3 -m 'Release v1.2.3'&&git push --tags
יתרונות
- 🔌 כוח מוחלט. אתה יכול לשנות היסטוריה.
חסרונות
- 😰 כוח מוחלט. אתה יכול לשנות היסטוריה. (טוב, גם יתרון וגם חסרון…)
- 🔂 אתה עלול להיכנס למעגל אינסופי לכאורה של קונפליקטים ו-
—-continue. (לפעמים אפילו עםgit rerere) - 🙀 שובר תכונות שיתוף פעולה מרכזיות: הערות PR שאבדו/יתומות. חצוף.
- 🖇️ קישורים קבועים יכולים להפוך ללא כל כך קבועים.
דרך המיזוג (Squash)
מודל מנטלי: “אני רוצה שחרור מותאם אישית, החל מנקודה נתונה, וכולל כל ענף/ים רצוי/ים.”
- קבל את העדכני ביותר:
git checkout main&&git pull - צור ענף חדש:
git checkout -b release/hot-newness-and-stuff - מזג ענפים ו/או קומיטים רצויים:
git merge --no-ff feature/hot-newness bug/fix-123(השתמש בדגל--no-ffהיכן שאפשר.) - תקן כל קונפליקט מיזוג (אם יתעורר.)
- תייג/דחוף באמצעות התהליך הנוכחי. דוגמה
git tag -a v1.2.3 -m 'Release v1.2.3'&&git push --tags
יתרונות
- 💪 פחות תהליך, פחות קונפליקטים בסך הכל, ומשתמש בידע קיים של פקודות git.
- 🚀 מאפשר לך לחשוב ברמת PR/ענף גבוהה יותר, תוך התעלמות מגרנולריות ברמת הקומיט (אלא אם יש צורך.)
- 🦺 לא הרסני. אתה יכול לחזור אחורה ו/או ליצור ענפים חדשים בכל עת.
- 🎥 משאיר קומיטים והודעות קיימים כפי שהם, מה שמוביל לפחות רעש ב-‘blame’.
חסרונות
- 🔏 קשה יותר לשנות הודעות קומיט.
- 🤐 קשה יותר להסתיר את העבודה שלך.
מסקנה
בסופו של דבר, תהליך פשוט יותר עם פחות סיכון צריך לנצח.
למרות שלמשתמשי Rebase אכן יש דרכים לפתור (או להימנע) מהבעיות שלהם, העובדה נשארת: בסופו של דבר תצטרך חגורה שחורה ב-git fu. (למשל, אפילו git push פשוט יכול להוסיף מורכבות: האם זה git push --force או git push --force-with-lease? למה להתעסק עם זה בכלל?)
יש סיבה נוספת לכך שrebasing ליצירת היסטוריה מתוקנת תמיד יהיה בעמדת נחיתות לעומת git merge .... git merge מאפשר ל-CLI של git להפעיל אלגוריתמים מתקדמים כדי להימנע מקונפליקטים על ידי ניתוח ה-HEAD של כל ענף.
זה יכול להיות חכם יותר כי כל מיזוג מתעניין רק במצב העדכני ביותר של כל ענף רצוי, בעוד rebasing חייב לשחזר (או להפיל) את היסטוריית הקומיטים ברצף שצוין. זה מגביל את יכולתו של git לבצע אופטימיזציה של המיזוג מכיוון שהוא משווה רק 2 קומיטים בכל פעם.
בסופו של דבר rebasing אומר שלפעמים תמצא את עצמך חווה מחדש קומיטים וקונפליקטים ישנים לא רלוונטיים - גם אם אתה יודע שהם הוסרו או נפתרו מאז.
סיכום
- 💃 תשובה: SQUASH MERGE את ה-PR שלך לתוך
main.- היסטוריית הענף שלך תהיה שם אם צריך, ו-
mainיישאר “נקי” יחסית.
- היסטוריית הענף שלך תהיה שם אם צריך, ו-
- 🔤 תמיד תעשה קומיט!
- בלמעלה מ-95% מהפרויקטים הארגוניים, “הלך הרוח של גיבוי” עדיף על “הלך הרוח של פיסול אמנותי”. ככל שעובר הזמן, משמעות הודעות הקומיט שלך תדעך, הרבה יותר מהר מהקוד שהלוגיקה והבדיקות שלו ישמרו על חשיבותן.