פורסם ב זה רק קוד, כלים לחיים קלים

Git me baby one more time

קוד: git_hooks
זמן קריאה: 10 דקות

היי לכולן!

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

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

במשפט אחד – git hooks הם סקריפטים שמורצים כשאתן עושות פעולות גיט. אתן יכולות לכתוב סקריפטים כאלה שירוצו אוטומטית לפני/אחרי כמעט כל פעולת גיט. לדוגמא – הסקריפט pre_commit רץ כשאתן עושות git commit (ולפני ה-commit עצמו).

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

NO COMMIT

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

בתהליך פיתוח המון פעמים אנחנו שמות בקוד כל מיני "ממלאי מקום" – יכול להיות שאנחנו כותבות סיסמא hardcoded בקוד (ואומרות לעצמנו שאחר כך נזכור לשים אותה במשתנה סביבה), או שהוספנו מלא הדפסות debug מיותרות או שרשמנו לעצמנו הערות וכו'…

כל התוספות האלה הן סבירות כל עוד הקוד נמצא על המחשב שלכן, אבל יכולות לגרום לדליפה של מידע ארגוני רגיש (או שלל בעיות אחרות) כשהן נדחפות למאגר הקוד הכללי של החברה.
כאן נכנס למשחק ה-NO COMMIT.

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

הנה הסקריפט שלי, הוא סופר פשוט – 

הוא רץ על כל השינויים שעשיתן בקוד, מחפש את המחרוזת NO_COMMIT#, ואם הוא מוצא אותה חוזר עם exit 1 – קוד יציאה שאומר לגיט שהבדיקה נכשלה והוא צריך לעצור את פעולת ה-commit.

NO MERGE

ה-git hook השני הוא מאוד דומה, אבל מסתבך קצת לקראת הסוף.
הרעיון מאחורי ה-NO_MERGE הוא למנוע הכנסת קוד ל-master שלא אמור להיות שם.
בניגוד ל-NO_COMMIT שאמור לעזור לתפוס קטעי קוד מיותרים שהוכנסו בתהליך הפיתוח הלוקאלי, ה-NO_MERGE עוזר לתפוס קטעי קוד שהם הכרחיים לפיתוח אבל לא אמורים להיכנס לפרודקשן.

דמיינו לכן את המצב הבא –
יש לכן קוד שמשתמש בספריה של החברה, ואתן מכניסות פיצ'ר שדורש גם שינוי בקוד וגם שינוי בספריה.
בזמן הפיתוח יש כל מיני טריקים תלויי-שפה שאפשר להשתמש בהם כדי להשתמש בגרסה שונה של הספריה, במקום בספרייה האמתית, אז אתן מכניסות את הטריק המתאים, וממשיכות במרץ בפיתוח.
שבועיים לאחר מכן, כשאתן באות לדחוף את הקוד ל-master, אתן כבר ממש לא זוכרות שאי פעם הכנסתן את השורה הזו, ואופס – הכנסתן באג/ פרצת אבטחה/ פצצת זמן מתקתקת לפרודקשן.

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

הסקריפט שלו נראה כך – 

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

אוקי, קול, עד לפה טוב ויפה – אז למה טענתי שה-git hook הזה מסתבך בסוף?

ובכן, אחרי שכתבתי את ה-git hook הזה, הרצתי אותו כמה פעמים, ושמתי לב לתופעה מטרידה – כשהסקריפט מכשיל את ה-merge, הוא עוצר את הפעולה באמצע, אחרי שלב ה-commit, ולפני שלב ה-merge. כלומר, אני נשארת במצב ביניים מוזר, שכדי לצאת ממנו אני חייבת לעשות פעולה אקטיבית (למשל git merge –abort), וזה, אין מה לעשות, מעצבן, לא כיף להשתמש בכלים שמייצרים לך עוד עבודה.

חקרתי ובדקתי, וניסיתי למצוא דרך לעקוף את הנושא, ולא מצאתי אחת כזו, אז בסוף החלטתי ללכת על פתרון יצירתי (כמובן שאשמח לשמוע אם יש לכן פתרון טוב יותר).
כתבתי פונקציית מעטפת שדורסת את git, ובמקרה של ניסיון merge כושל מריצה merge –abort.
הנה הפונקציה – 

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

כלומר, אם אתן כמוני, משתמשות רק ב-rebase בעבודה השוטפת שלכן, ועושות merge רק לקוד שאתן יודעות שיעבור חלק, אז אין שום בעיה, ואתן יכולות להוסיף את הפונקציה העוטפת.
אם merge הוא חלק מהעבודה שלכן, הפונקציה הזו תמחק את ה-merge conflicts, ותמנע מכן לסדר אותם, ולכן אתן לא יכולות להשתמש בה.
כך או כך, אתן עדיין יכולות להשתמש ב-NO_MERGE, ופשוט להמשיך אחרי העצירה כמו שאתן רגילות.

נקודות חשובות למשתמשות ב-git hooks!

  • כדי שגיט יוכל להריץ את הסקריפטים שכתבתן אתן צריכות להפוך אותם ל-executable בעזרת chmod +x.
  • אתן יכולות לכתוב את הסקריפטים האלה בכל שפה, לא רק ב-bash, אז אל תפחדו להתנסות.
  • והנה טריק מצוין לחיסכון בזמן ועבודה – באופן דיפולטי ה-git hooks מאוחסנים בתיקייה הראשית של הקוד. כלומר, אם יש לכן על המחשב כמה תיקיות של כמה פרויקטים נפרדים, אז בכל אחת מהן יהיו git hooks שונים.
    אם אתן רוצות להשתמש באותם git hooks בכמה פרויקטים, יש כמה שיטות לעשות את זה, אבל הטובה ביותר בעיניי היא לקנפג תיקייה דיפולטית חדשה עם הפקודה הבאה – 
git config core.hooksPath ~/my_git_hooks/

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

חלק מהאתרים יפנו אתכן לשים את הסקריפטים שלכן בתיקיית git_template/hooks שזו שיטה שיש איתה שתי בעיות – 

  1. הסקריפטים בתיקיית ה-templates לא יועתקו לתיקיות פרויקטים קיימות.
  2. אם אתן עושות שינויים בתיקיית ה-templates הם לא מתעדכנים בתיקיות פרויקטים קיימות.

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


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

תגובה אחת בנושא “Git me baby one more time

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

השאר תגובה