• בלוג
  • היום למדתי: איך להתעלם מקומיטים ב git blame

היום למדתי: איך להתעלם מקומיטים ב git blame

28/06/2021

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

אבל-

לפעמים יש קומיטים שמשנים שורות בלי שהשינוי הזה באמת יהיה קשור לתוכן של אותה שורה. הדוגמה הקלאסית היא שינויים שמשנים טאבים לרווחים, אבל אפשר לחשוב גם על אישהו Refactoring שעשה Mass Rename לשם של פונקציה. במצב כזה הפעלה של git blame תציג לי את הקומיט העדכני ביותר, למרות שזה לא הקומיט אותו אני מחפש.

דוגמה קצרה תעזור להמחיש את העניין. נניח שיש לי קובץ Python עם התוכן הבא:

def twice(x):
    return x * 2


if __name__ == "__main__":
    x = int(input("Pick a number: "))
    print(f"{x} x 2 = {twice(x)}")

ואני רוצה לדעת למה הודעת ההדפסה מציגה דווקא את הסימן x כדי לסמן מכפלה. פקודת git blame היתה יכולה לעזור לי:

$ git blame a.py
7391c20d (ynonp 2021-06-27 14:39:12 +0300 1) def twice(x):
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 2)     return x * 2
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 3) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 4) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 5) if __name__ == "__main__":
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 6)     x = int(input("Pick a number: "))
7391c20d (ynonp 2021-06-27 14:39:12 +0300 7)     print(f"{x} x 2 = {twice(x)}")
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 8) 

והנה אני רואה שהקומיט האחרון שהשפיע על השורה שמעניינת אותי הוא קומיט מספר 7391c20d. בואו נראה מה קרה בקומיט הזה:

$ git show 7391c20d

commit 7391c20df9f8eec472494602ffa727542151c553 (HEAD -> main)
Author: ynonp <ynonperek@gmail.com>
Date:   Sun Jun 27 14:39:12 2021 +0300

    Renamed function

diff --git a/a.py b/a.py
index 6e899d5..a883ec7 100644
--- a/a.py
+++ b/a.py
@@ -1,8 +1,8 @@
-def double(x):
+def twice(x):
     return x * 2


 if __name__ == "__main__":
     x = int(input("Pick a number: "))
-    print(f"{x} x 2 = {double(x)}")
+    print(f"{x} x 2 = {twice(x)}")

נו זה לא עזר במיוחד - מסתבר שהשינוי האחרון על השורה היה בסך הכל שינוי שם של הפונקציה. בשביל להמשיך לקומיטים ישנים יותר ב blame אני משתמש במתג ignore-rev:

$ git blame --ignore-rev 7391c20d a.py
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 1) def twice(x):
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 2)     return x * 2
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 3) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 4) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 5) if __name__ == "__main__":
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 6)     x = int(input("Pick a number: "))
373b123e (ynonp 2021-06-27 14:38:12 +0300 7)     print(f"{x} x 2 = {twice(x)}")
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 8) 

ועכשיו אני מקבל את הקומיט הקודם שהשפיע על השורה, ודרכו אפשר לראות את הודעת הקומיט ולהיזכר:

$ git show 373b123e

commit 373b123e76b02fe6972de79daf52839f5b08664a
Author: ynonp <ynonperek@gmail.com>
Date:   Sun Jun 27 14:38:12 2021 +0300

    Changed multiplication symbol

    Turns out we've received multiple complaints about clients
    not understanding that a * is actually a multiplication sign,
    so product asked to replace the * with an "x"

diff --git a/a.py b/a.py
index 9cc6552..6e899d5 100644
--- a/a.py
+++ b/a.py
@@ -4,5 +4,5 @@ def double(x):

 if __name__ == "__main__":
     x = int(input("Pick a number: "))
-    print(f"{x} * 2 = {double(x)}")
+    print(f"{x} x 2 = {double(x)}")

עכשיו בדוגמה שלנו הקומיט ששינה את שם הפונקציה בהחלט יכול להועיל בקונטקסט אחר, אבל יש מקרים (כמו שינוי טאבים לרווחים) שהקומיט אף פעם לא יועיל - ומה שצריך זה פשוט להתעלם ממנו בכל blame שאי פעם נריץ.

בשביל זה אנחנו יוצרים קובץ בתיקיית הפרויקט (אפשר לבחור לו כל שם, אני קראתי לו .git-blame-ignore-revs), ורושמים לתוכו את המזהה ממנו רוצים להתעלם:

$ echo 7391c20df9f8eec472494602ffa727542151c553 >> .git-blame-ignore-revs 

(שימו לב לרשום לקובץ את מזהה הקומיט המלא ללא קיצורים)

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

$ git config blame.ignoreRevsFile .git-blame-ignore-revs

ו-הפתעה. עכשיו כשנפעיל git blame הקומיט הבעייתי לא יופיע ונגיע ישר לקומיט שמעניין אותנו:

$ git blame a.py

cae63ff (ynonp 2021-06-27 14:36:40 +0300 1) def twice(x):
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 2)     return x * 2
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 3) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 4) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 5) if __name__ == "__main__":
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 6)     x = int(input("Pick a number: "))
373b123e (ynonp 2021-06-27 14:38:12 +0300 7)     print(f"{x} x 2 = {twice(x)}")
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 8) 

ואגב אחרון, אם עשיתם את כל ההוראות לפי הפוסט ועכשיו דווקא מתחשק לכם לוותר על הדילוג ולראות את git blame האמיתי תוכלו תמיד להפעיל:

$ git blame --ignore-revs-file "" a.py

7391c20d (ynonp 2021-06-27 14:39:12 +0300 1) def twice(x):
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 2)     return x * 2
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 3) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 4) 
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 5) if __name__ == "__main__":
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 6)     x = int(input("Pick a number: "))
7391c20d (ynonp 2021-06-27 14:39:12 +0300 7)     print(f"{x} x 2 = {twice(x)}")
^cae63ff (ynonp 2021-06-27 14:36:40 +0300 8)