שיעור פרטי: סדר בראש וריענון על קונספטים של git – עם ציורים!

הנה קומיט ראשון בריפו אין בו כלום:

image

שיניתי ועשיתי קומיט:

image

עכשיו יצרתי בראנץ’ וקראתי לו אביעד (git branch aviad):

עשיתי checkout לבראנץ’ בעזרת git checkout aviad, כלומר הוא activated.
עכשיו כל פעם שאני אעשה commit אני אמשוך אותו יחד איתי

(שאלה מהכיתה):

ו-main עדיין נמצא על 1, כן?

חניך מהכתה מעיר:

הmain הוא כמו עוד aviad לצורך העניין, ואם הוא נוצר עם הרפו אז הוא דבוק ל־1 ועולה איתו כל קומיט.

ים ממשיך – לא יצרנו את main, אבל בואו נניח ש¯main נוצר אוטומטית כשאנחנו מקימים ריפו:

image

עשיתי קומיט:

image

עכשיו יצרתי את הענף “אביעד” בעזרת git branch aviad ועשיתי אליו checkout, עם git checkout aviad.

חניך:

דבר ראשון שטעיתי לחשוב והאיורים סידרו לי זה שההתקדמות היא עם העץ, ולא חזרה לשורש שלו.
אם אתה יוצר עכשיו את aviad אז ל2 יחוברו גם אביעד וגם main, נכון?

איור:

image

עכשיו נעשה git commit.

החניך שואל:

עושים commit למי?

אני עונה:

מה הכוונה “למי”? שינינו קובץ, עושים קומיט.
קומיט זו בקשה לשמור תמונה של המצב הנוכחי.

החניך:

אה אוקי
לא משנה אם אני עושה את זה או הmain עושה את זה ?

אני עונה:

ה¯main לא עושה כלום. הוא סך הכל חץ שמצביע לעיגול.
אותו דבר aviad, הם לא שותפים לתהליך.

נבצע את ה־commit:

עכשיו נעשה git checkout main, ואז ונעשה git branch ode.
זה יצור ענף חדש ששמו ode:

עכשיו נפעיל את ode עם git checkout ode:

ונעשה commit:

עכשיו אנחנו עדיין עם אודה activated, נעשה עוד קומיט.

החניך:

ממנה הפעם

ים:

מה זה ממנה? אין ממנה ממני ממנו.

החניך:

מי עושה את הקומיט שלוקח את התמונה של הכל

ים:

הכל באותו מחשב. אנחנו על מחשב אחד. כרגע הכל במחשב שלי.

כאמור, נעשה עוד קומיט:

עשינו עוד קומיט, זה המצב כרגע

החניך:

עכשיו שנינו סגורים,
אין אקטיבים (שזו צרה בפני עצמה)

ים:

לא, אודה עדיין activated. למה חשבת שהיא כבר לא?

החניך:

אמממ. מה בעצם סוגר אותה? מה עשינו שסגר את אביעד?

ים:

שום דבר. אביעד לא סגור, חלילה.

חניכה מצטרפת להסביר:

אודה יכל באותה מידה להיות סתם monthview או branch_1, זה סתם שם

חניך:

למה אביעד לא התקדם עם כל קומיט, אחרי הפיצול?

ים:

כי הענף אביעד הפסיק להיות activated, עשינו checkout ל־main.
אני חש שאתה מתגעגע לאביעד. נחזור לאביעד: git checkout aviad.

עכשיו נעשה commit שוב:

החניך:

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

ים:

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

[ממשיכים לדסקס בגדול]

ים:

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

חניך:

נגיד הייתי עושה עכשיו checkout main. הייתי חוזר לנקודה בזמן של main.

ים:

צ’קאווט זו דרך לעבור לנקודה או לשם של בראנץ’ כלשהו.
אם היית עושה checkout main בדיאגרמה האחרונה כל הקבצים היו משתנים למה ששמרת בנקודה 2.

אתה יכול ליצור בראנץ’ מכל נקודה שהיא,
וכל עוד הוא activated הוא יתקדם יחד איתך.

חניך:

כאילו אני פשוט עושה checkout לנקודה שאני רוצה, ומייצר ממנה בראנץ.

ים:

יותר מדויק: מייצר בראנץ’ שמצביע אליה.
בוא ניתן עוד קומיט מאודה. איך נעשה את זה?

החניך:

נעשה checkout ל־ode, ואז נעשה לה commit?

ים:

לא נעשה לה, ode הוא לא משהו, הוא label שמוצמד לנקודה.
אנחנו לא עושים commit על ode.

כשאנחנו עושים git checkout ode זה מתורגם ל"תעבור לקומיט מספר 5", אודה זה השם שבו אנחנו משתמשים כדי להפנות לקומיט 5

חניך:

אה. וואו. אוקיי.
הייתי צריך את המשפט הזה

ים:

השמות ode ו־aviad הם רק מצביעים שיודעים להתקדם בעץ, הם לא מייצגים כלום בפני עצמם.
כלומר, הם הבראנצ’ים, והם מייצגים בראנצ’ים. אבל זהו.

נתקדם:

git checkout ode
git commit

זמן לקצת סדר.

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

ונשנה את השמות של ה־branch־ים. נדמיין שהבראנץ’ הראשון הוא לא aviad אלא profile-page ו¯ode היא main-page:

נניח שבבראנץ’ main-page עשינו קומיטים שקשורים לעיצוב הדף הראשי.
בבראנץ’ profile-page עשינו קומיטים שקשורים לדף הפרופיל.

אלו שני פיצ’רים שונים שנניח עבדתי עליהם במקביל.

חניך:

כלומר מהבראנץ main, שהוא commit מספר 2, הייתה התחלה של עבודה על שני דברים שונים.
כל אחד התפתח בכיוון שלו. כלומר הבסיס של כל אחד מהם היה זהה אבל זה התפתח שונה.

ים:

כן. 3, 4 נגזרו מ¯2.
שניהם שינו משהו ב¯2, אבל האבא שלהם הוא 2.
כל אחד שינה בכיוון אחר.
זה הוסיף דף ראשי, זה הוסיף פרופיל.

חניך:

כאילו 3 ו4 הם תמונות מצב בדרך של התתפתחות של 5. כן.

המשך הסבר:

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

חניך:

ל־2? כלומר לאבא שלהם?

ים:

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

חניך:

נכון, הוא תמונה גם בעצמו, אז אי אפשר לשנות אותו.

בכ"מ עשינו git checkout main:

עכשיו אנחנו עושים git merge profile-page:

יצרנו תמונה חדשה שמשולבת מ¯2 ומ¯6.

חניך:

ואז נגיד אתה עושה checkout ל־main-page…

ים:

בלי צ’קאווט. אני רק רוצה למרג’ג’.
אני עומד על הענף שאני רוצה שימשיך הלאה ומבקש לשאוב אלי את הענף למירג’וג’

נעשה git merge main-page:

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

[will it blend.gif]

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

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

נתחיל בהמשגה: תדמיין אתר שמולו אתה פועל וממנו אתה מוריד ומעלה את הקוד. זה remote.
כל עוד יש אחד כזה, אתה אפילו לא צריך להיות מודע לקיומו.

חניך:

אבל מה זה אומר שכולם עובדים מולו ישירות?
שיש נקודה וכולם יוצאים ממנה, ומעדכנים אליה הולך ושוב?

ים:

כולם מדברים מולו. תדמיין ענן. זה remote.
אז בוא נניח ש¯http://github.com/yam/calendar הוא רימוט.
אתה יכול לעשות fork, ואז יהיה http://github.com/moshik/calendar.

וזה אומר שיהיה 2 ריפוים בלתי תלויים.
אני יכול לעשות קומיטים ובראנצ’ים ומרג’ים ובלאגן,
וזה לא ישוקף לך ב־repo של moshik.

חניך:

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

ים:

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

אז אחרי שעשית fork מ¯Yam, יש 2 ריפויים.
הריפו הראשון נקרא Yam, ואני יכול לעבוד עליו כמה שבא לי ולשנות ולעשות בראנצ’ים ובלאגן.
והריפו השני נקרא moshik שהוא שלך, שנגזר מנקודת הזמן שבה עשית fork לפרויקט שלי.
אתה יכול לעשות בשלב הזה מה שבא לך על הפרויקט שלך, הפרויקט שלך לא יושפע משלי ושלי לא יושפע משלך.

חניך:

זה duplicate אבל without dependencies.

ים:

כן, ה¯duplication הוא של נקודה מסוימת בזמן ומאז מתנתקים.

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

נניח, אודה ואביעד שניהם יכולים להיות ה¯owners של הריפו שנקרא aviad,
ושניהם יכולים לעבוד עליו ביחד.

עכשיו יש את הריפו aviad שאביעד ואודה מנהלים.
נניח שאנחנו במצב שממש אחרי ה¯fork שאביעד עשה לריפו yam.

עכשיו אביעד ואודה עשו clone, כל אחד למחשב שלו, של הריפו aviad.
נקרא לריפו avocado כדי שיהיה פחות מבלבל:

חניך:

איך שונה ה clone מה fork?
את הפורק אני מבין…

ים:

ה¯clone זו פעולה שאתה עושה כדי להוריד את הקבצים מ¯remote כלשהו אל המחשב שלך.

חניך:

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

ים:

כן, אבל אתה תשאף מתישהו לעדכן את ה¯remote כי זה ה¯source of truth של כולם.

חניך:

אוקיי, אז ה־clone מעתיק אליי את הקבצים לפי תמונה שנמצא בה הרימוט שלי, במצב הנתון שלו.

ים:

הנה סיפור שאולי יבהיר את זה: מושיק מפתח מערכת לקנייה אוטומטית של אבוקדו מהשוק.
הוא עובד עליה במרץ עם קהילה קטנה של מפתחים מ¯2012–2017
ב¯2017 מושיק מחליט לשנות את הלייסנס של הפרויקט, ושכל מי שירצה להשתמש במשהו חדש שמוסיפים מעתה והלאה, יצטרך לשלם כסף
רעידת אדמה בעולם האופן סורס וכל הקהילה שעבדה יחד עם מושיק כעוסים ועצובים.
חמגשית, הרי היא גיבורת הסיפור האמיתית, מחליטה לפתוח פרויקט Open Source משלה לקנייה אוטומטית של בננות בשוק.
היא עושה fork מהנקודה בה מושיק החליט שהפרויקט הוא קנייני, ומתחילה ציר פיתוח נפרד.
בזמן שמושיק קרא לפרויקט שלו Mega Bananas Extreme, חמגשית קוראת לפרויקט שלה Best Bananas Ultra.
כל הקהילה שעבדה עם מושיק עד עכשיו מצאו תחליף. שמחים ומבסוטים הם עושים clone לפרויקט שחמגשית יצרה בעזרת ה¯fork וממשיכים לפתח משם.
… זה ברור?

חניך:

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

ים:

נכון. נניח שיש 20 מפתחים שחמגשית גייסה, כולם יעשו clone לפרויקט החדש,
כלומר ל¯fork של חמגשית שיצא מה¯repo של מושיקו.

חניך:

כן, נכון.

ים:

עוד סיבה לפיצול כזה עם fork, יכולה להיות ניהול הרשאות. נניח ואני שליט אכזר שמנהל 40 חניכים, ולא בא לי שיהיה לי המון בראנצ’ים מוזרים בכל מקום והפרויקט יקבל צורה מאוד מבולגנת.
אז אני אגביל את הגישה לכתיבת הפרויקט רק אליי, ואגיד לכולם לעשות fork. בשלב הזה כולם יוכלו לעשות דברים על ה¯sandbox שלהם, ואז רק כשהם ישלחו משהו שנראה טוב אני אהיה מוכן שזה ישתלב עם ה¯repo שלי.

חניך:

אחלה.

ים:

סבבה. קצת נסחפתי לשלב הבא, אבל בוא רגע נדמיין שיש רק source of truth אחד.
בוא נדמיין שיש רק את אביעד ואת אודה שמנסים לנהל פרויקט יחד.
שניהם סטודנטים נמרצים שמנסים להקים אתר על הריפו שלהם מתוך repo שלי שעשיתם לו fork.
נחזור לתמונה:

אביעד נניח יעבוד על ה¯profile page, ויוסיף קומיטים להנאתו:

חשוב להדגיש:
זה המצב במחשב שלו. זה לא מתעדכן בשרת המרוחק, ב¯remote, עד שאביעד לא מבקש מפורשות.

אודה עובדת על הדף הראשי:

ביחד העץ שלכם נראה דומה, אבל כולל דברים אחרים לגמרי:

עכשיו אודה עושה push לשרת.
היא עושה git push avocado main-page:

ואז גם לאביעד מתחשק אז הוא עושה git push avocado profile-page.

החניך:

היא לא צריכה קודם אצלה לעשות merge עם המיין? כאילו בעץ שלה עצמה? לפני שהיתי חוזרת חזרה לריפו?

ים:

לא.
הענף שלה יצא מ¯main והיא דוחפת את הענף שלה שלא מפריע ל¯state של אף אחד אחר.
זה לא שהיא תהרוס את המבנה של העץ, או לא תואמת משהו שקרה בעץ של הפרויקט הגדול.

החניך:

אוקיי.

ים:

מה שכן, יכול להיות ש¯git יסרב לבקשה הזו של אביעד, במקרים מסוימים.
הוא יגיד “אביעד היקר, היו שינויים מאז שמשכת דברים פעם אחרונה, נא עדכן”.
ואז אביעד יצטרך לבצע pull, מה שיביא אליו את הענף של ode.
זה יראה ככה, אחרי שהוא יעשה git pull avocado:

הפקודה git pull avocado מביאה את כל הענפים ב־remote שנקרא avocado.
אפשר גם git pull avocado main, זה יביא ספציפית את main.

החניך:

כשאביעד מתחבר לענף של אודה -
הוא לוקח את הענף שלה מהרפו שהמעודכן שנוצר אחרי שהיא התחברה אליו,
או לוקח את הענף מהקלון שלה?

ים:

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

החניך:

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

יאללה. נניח שאביעד עשה git push avocado:

(בשלב הזה נהייתי רעב ויצאתי להפסקת פרסומות)

עכשיו דמיינו שאודה עושה pull, ואז המצב בכל המחשבים ובשרת avocado זהה:

אבל לאודה נמאס מהבלאגן שאביעד עשה בריפו אז היא ממרג’ג’ת הכל ל־main:

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

  1. להריץ את הפקודה git remote add upstream https://github.com/yam/calendar, שתוסיף remote בשם upstream שכתובתו היא https://github.com/yam/calendar (ענן שמאל בתמונה, זה שמצויר אליו חץ למטה)
  2. אחרי זה, לכתוב git push upstream main שידחוף את כל הקומיטים ש¯main מצביע עליהם לענן המרוחק, upstream.

החניך:

אז upstream הוא שם שנתנו לקישור שמקשר בין שני הרפו האלה?

ים:

לא. זה השם שנתנו לריפו השמאלי.

החניך:

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

החניכה:

לא, כל אחד על פורק משלו.

החניך:

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

ים:

מדייק אותך קצת: כל אחד יוצר פורק שבעצם יוצר ריפו חדש, ואז מריץ במחשב שלו פקודת clone שמורידה את הקבצים שבריפו למחשב שלו ומגדירה את ה¯remote ששמו origin להיות אותו ריפו חדש.

החניך:

בשורה git remote add upstream https://github.com/yam/calendar היא מתחברת לענן השמאלי

ים:

בשורה הזו היא מגדירה remote חדש ששמו upstream,
והקישור אליו הוא הקישור לריפו הראשון, הענן השמאלי, ממנו עשינו fork בהתחלה.

החניך:

הענן הראשון אין לו שם?
זה נראה לי החור היחיד.

ים:

הענן שממנו אביעד ואודה עשו clone הוא avocado. הם לא באים עם שם.
אפשר להגדיר אותו במחשב הפרטי שלך באמצעות פקודת ה¯remote,
או כשאתה עושה clone – שם הוא מקבל את השם הדיפולטי origin.

החניך:

ה־add שהיא עושה, היא אומרת לו upstream…

ים:

זה שם שהיא נותנת לו.

החניך:

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

החניכה:

תחשוב על זה ככה:
git remote add <local nickname for repo> <location/url of the repo>

לילה טוב :slight_smile:

16 לייקים

:avocado::avocado::avocado::avocado::avocado::avocado::avocado::avocado::avocado:

ברדיט Explain me like im 5! התגובה הזאת מקבלת הרבה Awards!

מעולה !!₪ תודה
:avocado::avocado::avocado::avocado::avocado::avocado::avocado:

2 לייקים

הסבר מעולה, תודה רבה!

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

לייק 1