הפרדה לתתי פרויקט באמצעות git submodules


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

הסיבות לכך יכולות להיות:
1. ניסיון לשמירה על single responsibility principle (כן, הוא לא נכון רק לפונקציות ומחלקות)
2. עובדים על הפרויקט כבר יותר מדי אנשים וביצוע פעולות גיט מרובות לאותו פרויקט על ידי הרבה אנשים יכול ליצור לא מעט צרות וקונפליקטים.
3. לפעמים, בתוך באותה חברה נרצה שמספר צוותים יעבדו על (או ישתמשו ב) אותו פרויקט.
4. במקרים מסוימים אפילו נרצה לקחת חלק קטן מהקוד שלנו (לדוגמה פעולות אוטומציה נפוצות) ולהוציא ל open source.

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

הערת צד: אינני הולך לדבר על מימוש ההפרדה עצמה, הפוסט מניח שהקורא יודע לפתוח פרויקט, להזיז קבצים אל הפרויקט החדש ולדחוף אותו ל source control.

איך מפרידים את הפרויקט לתתי פרויקטים?

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

אם מראש בתחילת הפרויקט לא נלקח בחשבון שהפרויקט הולך להיות מופרד אז סביר להניח שקיימות תלויות משותפות בין הפרויקטים, כלומר: אם יש לי פרויקט AB שברצוני להפריד לפרויקט A ופרויקט B, סביר להניח שאמצא שימוש (יבוא מחלקות) של A ב B ושל B ב A, ובמקרה שיכול להיות אפילו קצת יותר מעצבן - יתווסף לנו C אשר משומש גם על ידי A וגם B.

זה יכול להראות בערך ככה:


נוכל לראות בדוגמה שפונקציה hello_c נקראת גם מ A וגם מ B. זו כמובן דוגמה פשוטה למקרה של תלויות בין פרויקטים.
במקרה כזה, הדבר הראשון שנרצה לעשות זה לפרום את הפרויקט שלנו ולהצליח להפריד לפרויקטים (או לפחות לתיקיות בתור התחלה) 

כך בערך אמור להראות הקוד לאחר ההפרדה:

בדוגמה למעלה אנחנו רואים שלוש מחלקות (במקרה שלנו לאחר החלוקה לשלושה פרויקטים שונים.
במקרה שלנו, המוטיבציה העיקרית שלנו להפרדת הפרויקטים הייתה להוציא את ModuleB החוצה לשימוש של צוותים נוספים בתוך הארגון מבלי שיצטרכו להתעסק גם בקוד של ModuleA אך ModuleC נקלע לנו בדרך והיינו צריכים להפריד גם אותו (משום שגם A וגם B צורכים אותו).

מה זה git submodules ואיך זה יכול לעזור לנו?

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

עתה, באמצעות יכולת שהכלי git מספק לנו בשם git submodules נוכל לצרוך את הפרויקט שהפרדנו מתוך פרויקט מסוים.

באמצעות submodules נוכל להגדיר מתוך הפרויקט שלנו שפרויקט מסוים נצרך על ידו באמצעות הפקודה git submodule add.
מהרגע שביצענו חיבור בין הפרויקטים נוכל להשאר מעודכנים מתוך פרויקט A בכל מה שנדחף בפרויקט B, נוכל לראות את פרויקט B מתוך פרויקט A, לערוך משם את הקוד ולבצע ממנו פעולות לgit ובנוסף נוכל לעבוד על פרויקט B באופן נפרד לחלוטין.

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

מתי נרצה להשתמש ב git submodules ומתי ב package manager של השפה?

submodules הן לא האופציה היחידה לנהל תלויות בין פרויקטים. כמעט בכל שפה (לפחות שאני מכיר) יש לנו כלי מובנה בשפה לניהול התלויות שלנו וליצירת חבילות מהפרויקט שלנו - pip, npm, nuget, maven וכו׳. אתם וודאי מכירים לפחות אחד מהרשימה.

הכלים הנ״ל מצוינים וב90 אחוז מהמקרים הייתי ממליץ להשתמש בהם בעיקר משום שהם מסודרים יותר ומאפשרים פחות מקום לטעויות. הרי ברוב המקרים כשאנחנו צורכים ספריה מסוימת אנחנו לא באמת צריכים את הקוד שלה בתוך הפרויקט שלנו אלא רק יכולת שימוש בה. בנוסף ניהול הגרסאות באמצעות הכלים האלו הוא מאוד פשוט - אם אני רוצה לצרוך חבילה מסוימת בגרסה מסוימת אני יכול פשוט להזין בקובץ ההגדרות של הפרויקט שלי באיזה גרסה אני צריך את החבילה הזו (או פשוט להגיד latest) בעוד שכשעובדים עם submodules זה טיפה יותר מסובך משום שאנחנו מדברים על ישויות של git כמו commit id ו tag.

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

לסיכום, כשאני עומד בפני ההחלטה האם להשתמש בפרויקט שהפרדתי כ-submodule השיקול העיקרי שלי הוא - כמה המשתמשים של תת הפרויקט המדובר צריכים אותו בתוך עץ הפרויקט שלהם? אם מאוד חשוב להם לראות אותו, ולשנות אותו תוך כדי העבודה שלהם כנראה אשתמש ב submodules. אחרת, כנראה שאתאמץ טיפה יותר ואבנה תהליך שיוצר חבילה מתת הפרויקט, בודק אותה ומפרסם אותה לשימוש הכלל (מה שנקרא בשפת העם - CI).

איך משתמשים ב git submodules?

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

לשם כך - נכנס באמצעות git bash לתיקייה הראשית של הפרויקט שלנו (זו שמכילה את הקובץ 'git.')

ונרשום את הפקודה

git submodule add {http link to git repo}
i.e.
git submodule add https://github.com/TomerCohen95/ModuleB.git

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


בנוסף, נוכל לראות שהתווסף לפרויקט שלנו קובץ בשם 'gitmodules.' שבתוכו מתואר הקישור בין הפרויקט שלנו לבין תת הפרויקט שצרפנו.


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


כעת הפרויקט  שלנו Multimodule, מעודכן כמקושר לפרויקט ModuleB.

ועכשיו נשארה רק שאלה אחת שצריך לענות עליה - אם מישהו דחף שינוי ל ModuleB, איך מעדכנים את הפרויקט הראשי בשינוי?
אז התשובה היא מאוד פשוטה, מהתיקייה הראשית של הפרויקט נריץ הפקודה:
git submodule update --remote

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

כמובן שקיימים ל git submodules עוד פיצ׳רים רבים ולא את כולם כיסינו כאן. לצורך העמקה בנושא אני ממליץ להתחיל במדריך הבא.


לסיכום

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

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

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


נתראה בפוסט הבא :)





תגובות

פוסטים פופולריים מהבלוג הזה

קודמתי לדרגת סיניור במיקרוסופט - מה למדתי בדרך

מהם קבצי DLL ואיך להשתמש בהם?

מדריך C# | שימוש ב LINQ