האם git revert שווה את הזמן שלכם?

09/09/2018
git

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

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

1. איך זה עובד

הפקודה git revert מזהה איזה שינויים קומיט מסוים יצר ובונה אוטומטית קומיט חדש שמבטל את אותם השינויים.

ניצור מאגר קטן עם הקוד הבא:

$ git init
$ echo hello > chat.txt
$ git add chat.txt
$ git commit -m 'initial commit'

$ echo sup >> chat.txt 
$ echo yo dog >> chat.txt
$ git commit -am 'second message'

$ echo I can see you >> chat.txt
$ git commit -am 'third message'

עכשיו הקובץ chat.txt נראה כך:

$ cat chat.txt 
hello
sup
yo dog
I can see you

והלוג נראה כך:

$ git log --oneline
81dfc8f (HEAD -> master) third message
5e396c4 second message
b0d16a0 initial commit

נפעיל את הפקודה revert כדי לבטל את הקומיט האחרון:

$ git revert HEAD

ועכשיו הלוג נראה כך:

8a40fda (HEAD -> master) Revert "third message"
81dfc8f third message
5e396c4 second message
b0d16a0 initial commit

הפקודה יצרה קומיט חדש שמספרו 8a40fda שמבטל את הקומיט הקודם לו. נתבונן בשינוי ונגלה:

$ git log -1 -p
commit 8a40fda2553aef0b4fb3038bfbfb456a7ba5f0c0 (HEAD -> master)
Author: ynonp <ynonperek@gmail.com>
Date:   Sat Sep 8 15:00:47 2018 +0300

    Revert "third message"

    This reverts commit 81dfc8fae479b2498fd76d5ec71e5f164ba2dbd5.

diff --git a/chat.txt b/chat.txt
index 7febbba..895aad3 100644
--- a/chat.txt
+++ b/chat.txt
@@ -1,4 +1,3 @@
 hello
 sup
 yo dog
-I can see you

ואכן קומיט זה מחק את השורה האחרונה בקובץ.

2. למה זה יותר טוב מ Rebase

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

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

3. מתי זה לא רעיון טוב

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

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

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

$ git log --oneline
81dfc8f (HEAD -> master) third message
5e396c4 second message
b0d16a0 initial commit

$ git revert 5e396c4
error: could not revert 5e396c4... second message
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

$ cat chat.txt
hello
<<<<<<< HEAD
sup
yo dog
I can see you
=======
>>>>>>> parent of 5e396c4... second message

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