במאמר הקודם דיברנו על איך מבטלים דברים בגיט באופן מקומי עוד לפני שעשינו את ה-push – כלומר לבטל קומיטים. במאמר הזה אני אדון על דרכים לבטל דברים אחרי שעשינו את ה-push לשרת המרוחק.
יצירת קומיט ביטול
הדרך האלגנטית והפשוטה ביותר היא ליצור סוג של 'אנטי קומיט', כלומר קומיט שמבטל את הקומיט האחרון ולדחוף אותו. את זה קל מאוד לעשות באמצעות
git revert dd61ab31
המספר המוזר הזה הוא המזהה של הגיט בלוג. כפי שאנחנו זוכרים, לכל קומיט יש מספר משלו שניתן לראות אותו באמצעות לוג. צריך למצוא את הקומיט שאנחנו רוצים לבטל בלוג ואז לכתוב את המספר שלו (או את חמשת הספרות האחרונות שלו) אחרי ה-git revert. כמובן על אותו branch ושיהיה נקי ללא שינויים. מה שיקרה הוא שהגיט, יבורך שמו, יצור לנו קומיט על המחשב שמבטל את *כל* השינויים שנעשו באותו קומיט. את הקומיט הזה אנחנו יכולים לדחוף כמו כל קומיט רגיל ולהיות בטוחים שכל השינויים שנעשו בקומיט המבוטל יתבטלו. החסרון הוא שזה נרשם בלוג. באופן עקרוני, זה לא ממש חסרון. אין בושה להודות בכך שפישלתם ודחפתם קומיט לא טוב.
שינוי ההיסטוריה
כאן צריך להתנהל בזהירות מופלגת. אם מישהו אחר חולק איתכם את אותו ענף, זה עלול להגמר רע מאוד. למה? כי אם הוא עשה fetch בין הקומיט שאותו אתם רוצים לבטל לבין הביטול, יש מצב שהגרסה שלו לא תוכל להסתנכרן יותר מול הגרסה המרוחקת וזה ממש רע. אז אם אתם משנים את ההיסטוריה מול branch שמישהו אחר גם עובד עליו – כדאי להשתמש ב-revert. אבל אם אתם רוצים לשנות את ההיסטוריה של ה-branch ומוכנים להסתכן – זה הקוד:
git push -f origin last_known_good_commit:branch_name
אם איש הסיסטם שלכם או מי שקינפג את שרת הגיט מנע את אפשרות שכתוב ההיסטוריה באמצעות פלאג receive.denyNonFastForwards – אז אבוד לכם, זה לא יעבוד.
ביטול merges
אם אתם רוצים לבטל מיזוגים של ענפים שנדחפו לשרת המרוחק, גם את זה אפשר לעשות, באופן עקרוני, אם עשיתם את ה-merge ואז אתם רוצים לבטל, אין קל מזה. בואו ונניח שעשיתי merge של ענף מסוים לתוך ה-master שלי. אני מסתכל על הלוג של השרת המרוחק באמצעות git log ומוצא את הנקודה שבה עשיתי merge ודחפתי את ה-branch. בדרך כלל כתוב ליד זה Merge pull request. הנה דוגמה:
אני לוקח את ה-hash שלו, שאם אתם זוכרים מדובר במספר המזהה שלו. לא חייבים לקחת את כל הספרות הארוכות במספר אלא אפשר גם את החמש האחרונות. ואז כותב:
git checkout master git reset --hard 204070
ה-branch המקומי הוא בגרסה שממש קודמת ל-merge, ואז מייד כותבים
git push origin master --force
כדי לדחוף את השינוי. ה-merge בעצם מתבטל ואנחנו 'חוזרים' בזמן. שימו לב שהחזרה גם כוללת commit שנעשו אחרי ה-merge, כלומר – אם הכנסתם קוד אחרי שעשיתם את ה-merge הוא הולך לעזאזל. זו הסיבה שהשיטה הזו מעולה אבל רק אם אתם מתחרטים מייד אחרי ה-merge.
אם עבר זמן רב מה-merge שאותו אתם רוצים לבטל, זה יותר מסובך. הדבר הראשון שיש לעשות הוא להסתכל על ה-git log ולאתר את ה-hash של ה-push שבו נעשה ה-merge. למשל:
git revert -m 1 204070
כל הקוד של הענף הספציפי הזה יוסר בבושת פנים. חשוב לשים לב לספרה 1 שיש לאחר הפלאג m. למה? כי זה מציין את ההורה שאנחנו רוצים להסתכל עליו. במקרה הזה הוא הענף המקורי. בדרך כל זה 1.
ה-merge יוסר מהקוד, אך ייזכר לדראון עולם בהיסטוריה. אם נרצה מתישהו למזג את הענף שוב, נצטרך לעשות כמה פעולות כיוון שגיט בטוח שהענף הזה כבר מוזג. איזה פעולות? ראשית לבטל את הביטול. איך? מוצאים בלוג את הפעולה שבה ביטלנו את המיזוג ומוציאים את המספר שלה, מקלידים:
git revert 88edd6d
זה יבטל את הביטול ויכניס את כל השינויים שנעשו עד הביטול לענף המקורי שלנו. עכשיו צריכים להכניס את השינויים האחרים שנוספו לענף הזה באמצעות פקודה merge רגילה כמו:
git merge jk/post-checkout
באופן אישי, אם עשיתי unmerge, נדיר שאני מכניס שוב את הענף המקולל, אבל אם מסיבה מסוימת אתם רוצים לעשות את זה- הראיתי שזה אפשרי.
ביטול ה-merge גם מההיסטוריה
אם מסיבה מסוימת אתם לא רוצים שביטול ה-merge יופיע בהיסטוריה אפשר לעשות את זה. כמובן שרק ובתנאי שרק אתם משתמשים בענף הספציפי הזה ולא אחרים. למה? כי אתם משנים את ההיסטוריה ואם הענף הזה נמצא אצל אחרים, והם יעשו פעולות, יש מצב שהם לא יצליחו כי ההיסטוריה המקומית שלהם לא תהיה תואמת להיסטוריה של הענף בשרת. אז ככה שזו לא פרקטיקה מומלצת. מה עושים? מאתרים בלוג את המספר של ה-merge ואז מריצים את הקוד הבא (123456 זה המספר של ה-merge לצורך העניין) :
git rebase -i 123456
וזהו! ה-merge נעלם מההיסטוריה.
2 תגובות
היי,
תודה על הפוסט הברור!
יש לי פרויקט שרק אני עובדת עליו.
ביטלתי 2 commitים אחרי push ב revert, ואח"כ לפי מה שכתבת כאן בשינוי היסטוריה נמחקו לי 2 הקומיטים.
אבל את הפרויקט הזה רציתי להעלות לגיט אחר. וכשעשיתי git push –mirror לגיט השני – קיבלתי גם את 2 הקומיטים המבוטלים + 2 קומיטים של הביטול.
איך אפשר למחוק את ארבעתם לגמרי?
תודה רבה!
טלי
הסתדרתי.. 🙂
git reset –hard HEAD~1
git push -f