אם אתם חדשים או באמצע תהליך של למידת תכנות, סביר להניח ששמעתם את המונחים "Git" ו-"GitHub". אבל מה הם בדיוק ואיך הם עובדים? במאמר זה, נחקור את היסודות של Git ו-GitHub, כמו גם כמה פקודות נפוצות שכנראה שתצטרכו לדעת.
הקדמה
פעם, לפני ש-Git הפך לחלק בלתי נפרד מחיי המפתחים, שיתוף פעולה על פרויקט היה אתגר אמיתי. לכל מפתח היה עותק משלו של הפרויקט, וכל שינוי היה עובר ידנית בין חברי הצוות. בואו נראה דוגמה בין שני מתכנתים (יוסי וצביקה).
בהתחלה, לשניהם יש את אותה גרסה של הפרויקט ההתחלתי. לאחר מכן, כל אחד מקבל משימה לעבוד עליה. יוסי מסיים את העבודה שלו ושולח את הקבצים המעודכנים במייל לצביקה. צביקה, מצידו, צריך לפתוח את הקבצים, לשלב אותם ידנית בפרויקט שלו, ולוודא שהוא לא מוחק בטעות את העבודה שלו עצמו.
כדי להימנע מדריסת קוד, צביקה יוצר תיקייה חדשה בשם Project_v2_yossi_task1. הוא משלב את השינויים של יוסי עם הקוד שלו באופן ידני. אבל אז יוסי מבחין שיש טעיות בקוד שכתב ולכן יוסי שולח גרסה חדשה לצביקה, שצריך לשלב את כל השינויים שוב Project_v2_yossi_task1_final. התהליך הזה חוזר על עצמו, וכל פעם נוצרת תיקייה חדשה מה שיכול להיות ממש מוגזם:
/Project_v1
Initial version of the project, before any changes.
/Project_v2_yossi_task_complete
Yossi completed his task and updated the code.
/Project_v3_tzvika_added_features
Tzvika integrated Yossi's changes into the project.
/Project_v4_yossi_bug_fixes
Yossi fixed bugs identified after the integration.
/Project_v5_tzvika_integration_final
Tzvika added updates after the code review.
עכשיו תחשבו מה קורה בצוות של 5 מתכנתים – כל אחד שולח שינויים, כל אחד יוצר גרסה חדשה, ותוך זמן קצר יש בלגן של עשרות תיקיות. ניהול פרויקט בצורה כזו היה מתכון בטוח לכאב ראש ולעבודה כפולה. 🤯
מה זה Git?
Git היא מערכת לניהול גרסאות (Version Control System) שמביאה סדר לעבודה למתכנתים כמו יוסי וצביקה. במקום ליצור אין-ספור תיקיות ולשלב קוד בצורה ידנית, Git מאפשרת לעקוב אחר השינויים בקוד לאורך זמן, לעבוד בשיתוף פעולה עם מתכנתים אחרים בצורה מסודרת ובטוחה, ולחזור לגרסאות קודמות במקרה הצורך. כיום, Git היא הכלי המוביל לניהול גרסאות ומשמשת מתכנתים בכל רחבי העולם.
מה זה GitHub?
GitHub הוא אתר אינטרנט שנותן לנו לשמור ולנהל בצורה נוחה יותר את ניהול הגרסאות. זה מאפשר למתכנתים לאחסן את הקוד שלהם בענן ולהפוך הגרסאות שלהם לזמינות מכל מקום. Git ו-GitHub הם שני דברים שונים, ולמרות שהם עובדים יחד, הם לא אותו הדבר. GitHub בעצם לוקח את הכוח של Git והופך אותו לזמין מכל מקום עם תוספות כמו שיתוף פעולה, סקירות קוד, מעקב אחר בעיות, וכלים נוספים לניהול פרויקטים.
לצד GitHub, קיימים שירותים נוספים כמו GitLab ו-Bitbucket, שגם הם מציעים תכונות דומות ולעיתים אף ייחודיות, והבחירה ביניהם תלויה בצרכים של הצוות או הארגון.
השלבים המרכזיים בעבודה עם Git
Git פועלת בצורה מסודרת באמצעות תהליך מבוסס שלבים, שמאפשר מעקב וניהול מדויק של שינויים בקוד. הנה השלבים המרכזיים בעבודה עם Git:
Working Directory – מקום העבודה
זה המקום שבו מתבצעת כל העבודה שלכם על הקוד. לדוגמה, ב-VS Code, אתם עורכים קבצים, מוסיפים פונקציות חדשות או מתקנים באגים. מבחינת Git, ה-Working Directory הוא כל התיקייה שבה הקבצים של הפרויקט שלכם נמצאים.
חשוב לדעת שבשלב הזה, השינויים שלכם נשמרים במחשב (באופן זמני בדיסק המקומי), ו-Git מודע לכל הקבצים ב-Working Directory, אך לא בהכרח מנהל או עוקב אחריהם באופן פעיל. אם מדובר בקובץ חדש – הוא ייחשב כ-Untracked (לא מנוהל). אם מדובר בקובץ שכבר נמצא תחת ניהול של Git אבל השתנה – הוא ייחשב כ-Modified (שונה). אז איך אומרים ל-Git להתחיל לעקוב אחרי הקבצים? בואו נעבור לשלב השני (Staging Area).
Staging Area – אזור ההכנה
אחרי שסיימתם לעבוד על חלק מסוים בקוד, ואתם רוצים לשמור את השינויים, אתם מוסיפים אותו ל-Staging Area על ידי השורת קוד הבאה: git add .
ה-Staging Area הוא אזור ביניים שבו אתם 'מסמנים' את השינויים שאתם רוצים ש-Git יעקוב אחריהם ויכין לשמירה. עם זאת, חשוב לזכור שהשינויים שנמצאים ב-Staging Area נשמרים באופן זמני בדיסק המקומי שלכם, בקובץ שנמצא בתוך התיקייה המוסתרת .git (שעלייה נדבר בהמשך).
Repository – שמירת השינויים
Repository, או בקיצור "repo", הוא המאגר המרכזי שבו Git שומר את כל הקוד שלכם. כשאתם מוכנים, אתם לוקחים את כל מה שבחרתם ב-Staging Area ושומרים אותו במאגר באמצעות הפקודה הבאה: git commit -m "Short and clear message"
פקודה זו שומרת את השינויים מאזור ההכנה (Staging Area) למאגר המקומי (Local Repository) יחד עם הודעה שמסבירה מה בדיוק ביצעתם או שיניתם. עכשיו, חשוב לדעת שיש לנו שני סוגים של מאגרי גרסאות (Repositories), אז בואו נחקור אותם.
Local vs. Remote Repositories
Local Repository הוא מאגר גרסאות המאוחסן במחשב האישי שלכם, שבו אתם עובדים על קבצי הפרויקט ומבצעים שינויים באופן מקומי (Local). למעשה, ה-Local Repository מיוצג על ידי התיקייה המוסתרת .git שנמצאת בתיקיית הפרויקט.
התיקייה הזו מוסתרת כברירת מחדל (hidden), מכיוון שהיא מכילה את כל המידע הקריטי ש-Git זקוק לו לניהול היסטוריית הגרסאות, הקומיטים (commits), הגדרות הפרויקט ועוד. ההסתרה נועדה להגן על הקבצים החשובים שבתוכה מפני מחיקות או שינויים לא מכוונים, שעלולים לפגוע בתקינות המאגר המקומי.
כל שינוי שאתם מבצעים משפיע אך ורק על הקבצים במחשב שלכם (המאגר המקומי), מבלי להשפיע על קבצים או מאגרים של אנשים אחרים. זה מאפשר לכם לעבוד בצורה פרטית ובטוחה עד שתבחרו לשתף את השינויים.
Remote Repository הוא מאגר גרסאות המאוחסן על שרת מרוחק, כמו GitHub או GitLab, ומשמש לשיתוף פעולה עם חברי צוות או כגיבוי לפרויקט. שינויים שביצעתם במאגר המקומי (Local Repository) נשלחים (push) למאגר המרוחק, ומהמאגר המרוחק ניתן למשוך (pull) שינויים שבוצעו על ידי אחרים.
📌 כך מתנהל תהליך העבודה עם Git ו-GitHub: העבודה מתחילה במאגר המקומי (Local Repository) שבו מבצעים את השינויים. לשיתוף או גיבוי העבודה, תשלחו (git push) את השינויים למאגר המרוחק (Remote Repository), וחברי הצוות יוכלו למשוך אותם באמצעות (git pull) או (git fetch).
הכוח של Git: כלים ומונחים מרכזיים
עבודה עם ענפים (branches) ב-Git
אחת התכונות החשובות ביותר של Git היא היכולת ליצור "ענפים" (branches) של קוד. ענף הוא למעשה גרסה מבודדת של הפרויקט, שבה מתכנתים יכולים לעבוד בצורה עצמאית על פיצ'רים חדשים, תיקוני באגים או שינויים אחרים, מבלי להפריע לקוד הראשי או לקוד של שאר חברי הצוות.
לדוגמה, יוסי וצביקה עובדים על אותו פרויקט. יוסי צריך לבנות פיצ'ר חדש שקשור למערכת ההתחברות, ולכן הוא יוצר ענף בשם feature/add-login. במקביל, צביקה מתקן באג קריטי במערכת הקיימת ויוצר ענף משלו בשם bugfix/fix-login-error. כעת, כל אחד מהם יכול לעבוד על הקוד שלו בענף נפרד, בלי לדאוג שישפיע על העבודה של השני.
כשיוסי מסיים לפתח את הפיצ'ר שלו וצביקה משלים את תיקון הבאג, שניהם יכולים לשלב (merge) את הענפים שלהם חזרה לתוך הקוד הראשי (main branch). בצורה הזו, העבודה של שניהם נשמרת בצורה מסודרת ומאורגנת, בלי קונפליקטים מיותרים.
ענפים מאפשרים עבודה מקבילה ושומרים על סביבה מבודדת לכל מפתח. הם מבטיחים שהקוד הראשי נשאר נקי עד שהשינויים מוכנים, ומאפשרים שיתוף פעולה נוח ויעיל בין חברי הצוות. בזכותם, ניהול פרויקטים מורכבים הופך לפשוט יותר, עם פחות כאב ראש ויותר סדר.
ברירת המחדל בפרויקטי Git היא שיש ענף מרכזי שנקרא main או master. הענף main מכיל את הקוד הראשי והמוצק של הפרויקט, והוא משמש כבסיס לפיתוח המשך ולשחרורים לסביבת ה-production. כדי ליצור branch, ביצעו את הפקודה הבאה: git checkout -b new_branch_name
Commit Snapshot – או פשוט Commit
כל commit הוא למעשה פרק או נקודת זמן בהיסטוריה של הפרויקט. הוא מאפשר לכם לחזור אחורה בכל רגע, לראות מה השתנה, מי ביצע את השינויים, וכדומה. היכולת הזו מעניקה ביטחון ושקיפות בעבודה, ומאפשרת לעקוב אחרי ההתפתחות של הפרויקט בצורה ברורה ומסודרת.
לפני שניתן לבצע commit, יש להוסיף את הקבצים או את השינויים שאנחנו רוצים לכלול ב-staging area באמצעות הפקודה git add. ה-staging area כמו שאמרנו הוא מעין "אזור ביניים" שבו אנחנו מכינים ומארגנים את השינויים לפני שאנחנו מעבירים אותם רשמית ב-commit ומעדכנים את ה-repository. כדי לעדכן את ה-repository בשינויים נוכל לבצע את הפקודות הבאות:
git add .
git commit -m "Your commit message here"
Repository
אז כמו שאמרנו, Repository ב-Git הוא אוסף של קבצים ותיקיות שעליהם מתבצע ניהול גרסאות על ידי Git. כלומר, Git עוקב אחר השינויים שמתבצעים בקבצים ובתיקיות האלו כדי לאפשר לנו לנהל ולשחזר לגרסאות קודמות אם נצטרך.
כדי ליצור repository חדש ב-Git, אנחנו צריכים לגשת לתיקייה הראשית (root directory) של הפרויקט שלנו. תיקייה זו היא המיקום שבו נמצאים כל הקבצים והספריות של הפרויקט, כמו הקוד, התצורות והמשאבים שאתם עובדים עליהם. חשוב לבצע את הפקודה הזו בתיקייה הנכונה, כי Git יתחיל לעקוב אחרי כל מה שנמצא בתיקייה שבה הוא מופעל.
לאחר שהגענו לתיקייה הראשית, נריץ את הפקודה הזו שיוצרת מאגר (repository) חדש בתיקייה שלכם ומאפשרת ל-Git לעקוב אחרי השינויים שתבצעו בקבצים: git init
במקרים מסוימים, כשאתם משתמשים בכלים או פריימוורקים כמו React, Next.js, או Create React App, ייתכן שהפקודה git init כבר הופעלה אוטומטית כחלק מתהליך יצירת הפרויקט. כדי לוודא ש-Git פעיל בפרויקט, עברו לתיקיית הפרויקט והריצו את הפקודה: git status
אם המאגר קיים, הפקודה git status תציג את המצב הנוכחי של Git בתיקייה – למשל, האם יש קבצים חדשים שעדיין לא מנוהלים, אילו קבצים שונו מאז השמירה האחרונה, והאם קיימים שינויים שממתינים לשמירה (commit). אם המאגר לא קיים, הפקודה תציין זאת, ואז תוכלו ליצור אותו באמצעות הפקודה git init.
Merge
נחזור לדוגמה של יוסי וצביקה: יוסי עבד על פיצ'ר חדש בענף משלו שנקרא feature/add-login, וצביקה תיקן באג בענף נפרד בשם bugfix/fix-login-error. עכשיו, כשהם סיימו לעבוד, הם צריכים לשלב את השינויים שלהם, כך שכל העבודה שלהם תהיה חלק מאותו פרויקט.
בעבר, יוסי וצביקה היו צריכים לשלב את הקוד שלהם בצורה ידנית – קובץ אחרי קובץ. ב-Git, התהליך הזה נקרא Merge (מיזוג) והוא מאפשר לשלב את השינויים שלהם בצורה אוטומטית ומסודרת.
זה בעצם תהליך של שילוב שינויים מענף אחד לאחר. המיזוג מתבצע בדרך כלל כאשר מפתחים סיימו לעבוד על פיצ'ר, תיקון, או שיפור בענף משני, והם רוצים לכלול את השינויים בענף הראשי (main) או בכל ענף אחר שישמש כבסיס להמשך הפיתוח.
לפני ביצוע המיזוג, חשוב לוודא שהענף המקבל (למשל, main) מעודכן עם השינויים האחרונים. זה הכרחי כי ייתכן שבזמן שעבדתם על הקוד שלכם, אחד מחברי הצוות כבר עשה שינויים ב-main ועדכן אותו. אם לא נוודא שה-main אצלנו (ב-local) עדכני, אנחנו עלולים להתקל בקונפליקטים מיותרים בעת המיזוג. לכן, לפני ביצוע ה-merge, נריץ את הפקודה הבאה: git pull origin main
פקודה זו תוודא שה-main המקומי (local) מעודכן עם כל השינויים האחרונים. רק לאחר מכן נוכל לבצע את ה-merge בצורה בטוחה ומסודרת:
git merge branch_to_merge
Git וכלים שיתופיים: GitHub, GitLab ו-Bitbucket
Push
פקודת git push ב-Git משמשת להעלאת שינויים מ-local repository ל-remote repository. כאשר מבצעים commit, השינויים נשמרים רק באופן מקומי במחשב. הפקודה git push דוחפת את השינויים האלו ל-remote repository, שם הם זמינים לשאר חברי הצוות.
לאחר שהשינויים הועלו, חברי הצוות יכולים לעדכן את ה-local repository שלהם על ידי שימוש בפקודת git pull, שמשלבת את השינויים מה-remote עם העבודה המקומית שלהם. לחלופין, ניתן להשתמש ב-git fetch ואחריו git merge לקבלת שליטה מלאה בתהליך השילוב.
git push <remote> <branch>
<remote> – זה השם של המאגר המרוחק שאליו שולחים את השינויים. בדרך כלל קוראים לו origin.
<branch> – זה השם של הענף (branch) שבו עובדים, לדוגמה main או feature/login.
💡 חשוב: לפני שתוכלו להשתמש ב-git push, עליכם להגדיר את ה-remote repository. אם יצרתם את הפרויקט מקומית (כלומר, התחלתם מאפס ולא חיברתם אותו למאגר מרוחק), תוכלו להוסיף חיבור ל-remote repository באמצעות הפקודה:
git remote add origin <remote_repository_URL>
סנכרון קוד עם git pull ו-git fetch
כאשר עובדים בצוות, חשוב לשמור על סנכרון בין המאגר המקומי שלכם (Local Repository) למאגר המרוחק (Remote Repository). לשם כך, קיימות שתי פקודות עיקריות: git pull ו-git fetch.
git fetch
פקודת git fetch משמשת להבאת שינויים מהמאגר המרוחק (Remote Repository) למאגר המקומי (Local Repository), מבלי לעדכן את ה-working directory או למזג את השינויים עם הענף המקומי. יתרון זה מאפשר לכם לבדוק את העדכונים שהתקבלו מהמאגר המרוחק לפני שילובם בקוד המקומי, וכך מעניק לכם שליטה מלאה על תהליך העבודה.
git fetch <remote>
git pull
פקודת git pull בדומה ל-git fetch, גם משמשת למשיכת שינויים מהמאגר המרוחק (Remote Repository) אל המאגר המקומי (Local Repository). אך בניגוד ל-git fetch, שרק מושכת את העדכונים מבלי לבצע שינוי בענף המקומי, git pull גם מושכת את השינויים וגם מבצעת מיזוג (merge) אוטומטי של העדכונים שנמשכו ישירות לענף המקומי שבו אתם עובדים.
git pull origin main
💡 במילים אחרות, git pull משלבת את הפעולות של git fetch ו-git merge בפעולה אחת, מה שחוסך שלב אך עשוי להיות פחות גמיש אם מתעוררות התנגשויות (conflicts).
Pull request / PR
Pull Request הוא מנגנון שמשמש בפלטפורמות כמו GitHub, GitLab, ו-Bitbucket להצעת שינויים מענף אחד לענף אחר ב-repository. הוא מאפשר למפתחים להציג את השינויים שלהם בפני צוות הפיתוח ולבקש אישור למיזוג השינויים לענף המטרה (למשל, main).
במהלך תהליך ה-Pull Request, מתבצעת סקירת קוד (code review) על ידי חברי הצוות או מפתחים אחרים, כדי לוודא שהשינויים עומדים בסטנדרטים הנדרשים ואינם גורמים לבעיות בקוד. לאחר שה-Pull Request מאושר, השינויים ממוזגים לענף המטרה, והפרויקט מתעדכן עם השינויים החדשים.
בניגוד לפקודות Git הרגילות, Pull Request אינו פקודה שניתן לבצע מהשורת הפקודה של Git. הוא פיצ'ר של הפלטפורמה המתארחת של ה-repository, ולכן התהליך נעשה דרך ממשק האינטרנט של הפלטפורמה.
Clone
הוא תהליך ב-Git שבו אתם יוצרים עותק מקומי של repository אחר. העותק המקומי כולל את כל הקבצים, ההיסטוריה והענפים של ה-repository האחר. לאחר שיצרתם clone, אתם יכולים לעבוד על הפרויקט במחשב האישי שלכם, לבצע שינויים ולשמור אותם באמצעות commits.
הפקודה ליצירת clone היא `git clone`, והיא מקבלת כפרמטר את כתובת ה-URL של ה-repository המרוחק: git clone https://github.com/user/repo.git
לאחר שיצרתם clone, אתם יכולים להשתמש בפקודה `git push` כדי להעלות את השינויים שביצעתם ב-repository המקומי שלך ל-repository האחר. זה מאפשר לכם לשתף את העבודה שלכם עם שאר הצוות או עם הקהילה.
בנוסף, אתם יכולים להשתמש בפקודה `git pull` כדי להוריד שינויים מה-repository האחר ולמזג אותם ל-repository המקומי שלכם. זה מאפשר לכם לשמור על הגרסה המקומית שלכם מעודכנת עם השינויים האחרונים מה-repository האחר.
Fork
הוא מונח שמתאר תהליך יצירת עותק של repository מסוים מפלטפורמת אחסון הקוד כמו GitHub, GitLab, או Bitbucket. ה-Fork מאפשר לכם ליצור עותק פרטי של ה-repository המקורי בחשבון שלכם, כך שתוכלו לעשות בו שינויים מבלי להשפיע על ה-repository המקורי.
לאחר שיצרתם Fork של repository, אתם יכולים לעבוד על הקוד באופן עצמאי, ליצור ענפים חדשים, לבצע commits, ולהוסיף שינויים לעותק שלכם. אם אתם רוצים לשתף את השינויים שלכם עם ה-repository המקורי, אתם יכולים ליצור Pull Request. ה-Pull Request מבקש מבעלי ה-repository המקורי לשקול למזג את השינויים שלכם לתוך ה-repository שלהם.
בדרך זו, Fork מאפשר למשתמשים לתרום לפרויקטים קיימים ולשתף רעיונות ותיקונים עם הקהילה, מבלי לקבל גישה ישירה לכתיבה ב-repository המקורי. גם פקודה זו אינה נתמכת ישירות על ידי Git, אלא היא פיצ'ר של פלטפורמות כמו GitHub, GitLab ו-Bitbucket שגם אותה תוכלו לבצע דרך הממשק שלהם.
ההבדל בין Fork ל-Clone
Fork ו-Clone הם שני תהליכים שונים ליצירת עותק של מאגר קוד (repository), אבל יש ביניהם הבדל חשוב במטרה ובדרך שבה הם עובדים.
Fork הוא תהליך שמתבצע בפלטפורמות כמו GitHub, GitLab או Bitbucket, שבו אתם יוצרים עותק של repository מסוים, אבל לא על המחשב שלכם – אלא בתוך החשבון שלכם בפלטפורמה. העותק הזה מנותק מה-repository המקורי, ולכן אתם יכולים לבצע שינויים, לבצע commit וכדומה מבלי החשש שזה ישפיע על ה-repository המקורי.
אחרי שעשיתם את השינויים שאתם רציתם, אתם תוכלו ליצור Pull Request (PR) כדי לבקש מבעלי ה-repository המקורי לשלב את השינויים שלכם במאגר שלהם, ואז הם יכולים לאשר או לסרב. זה שימושי בעיקר כשעובדים על פרויקט קוד פתוח.
Clone, לעומת זאת, הוא תהליך שבו אתם יוצרים עותק של המאגר למחשב האישי שלכם. בניגוד ל-Fork, Clone הוא פקודה מובנית ב-Git. העותק הזה מחובר ל-repository המקורי, כך שתוכלו למשוך שינויים (pull) ולהעלות שינויים (push) ל-remote repository אם יש לכם הרשאות.
אם אין לכם הרשאות, אתם קצת תקועים, ולכן תצטרכו קודם לבצע Fork ל-repository, ורק לאחר מכן Clone ל-Fork שלכם כדי להמשיך לעבוד בצורה מלאה.
קונפליקטים ב-Git: איך הם קורים ואיך מתמודדים איתם
מהו קונפליקט ב-Git?
Conflict ב-Git מתרחש כאשר שני אנשים או יותר מבצעים שינויים בקוד באותם חלקים של קובץ, ולאחר מכן מנסים למזג (merge) את השינויים שלהם. במצב כזה, Git לא יודע איזה שינוי לשמור – את זה שבוצע על ידי המשתמש הראשון, או את זה של המשתמש השני – ולכן הוא עוצר את התהליך ודורש מהמפתחים לפתור את ההתנגשות באופן ידני.
דוגמה לקונפליקט: יוסי וצביקה עובדים יחד
נניח שיוסי עובד על פיצ'ר חדש בענף משלו (feature/add-login), ובמקביל, צביקה מתקן באג בענף הראשי (main). יוסי מוסיף שורת קוד לפונקציה שמציגה הודעה: "התחברות בוצעה בהצלחה", ובמקביל, צביקה משנה את אותה פונקציה כך שתציג הודעה אחרת: "התחברות נכשלה". צביקה משלים את העבודה שלו ומבצע git push ל-main המרוחק.
עכשיו, יוסי מנסה למזג את השינויים שלו אל הענף הראשי (main). בשלב זה, Git מזהה ששני השינויים התרחשו באותו חלק בקובץ, ולכן הוא אינו יודע איזה מהם לבחור. כתוצאה מכך, Git עוצר את תהליך המיזוג ומבקש לפתור את הקונפליקט באופן ידני.
איך יוסי מתמודד עם שינויים שבוצעו ב-main?
כדי להימנע ממצבים של קונפליקטים בשלב המיזוג, יוסי רוצה לוודא שהקוד שלו תואם לשינויים שצביקה כבר ביצע ב-main. לצורך זה, יוסי יכול לפעול באחת משתי דרכים:
האפשרות הראשונה היא להישאר בענף feature/add-login ולמשוך את השינויים מה-main המרוחק באמצעות הפקודה git pull origin main. פעולה זו תשלב את השינויים של צביקה לתוך הענף feature/add-login, כך שיוסי יוכל להמשיך לעבוד על הקוד שלו כשהוא מעודכן עם השינויים האחרונים.
האפשרות השנייה היא לעבור ל-main המקומי, להריץ git pull, ולמשוך את השינויים מה-main המרוחק. במקרה הזה, הענף המקומי main יתעדכן עם כל השינויים שצביקה ביצע, אך השינויים לא ייכנסו אוטומטית לענף feature/add-login. אם יוסי רוצה לשלב את השינויים האלה ב-feature/add-login, הוא יצטרך לעבור חזרה לענף שלו ולבצע מיזוג (merge) עם הענף הראשי המקומי.
למה Git לא מאפשר Push ישיר ל-main בלי Pull קודם?
אם יוסי וצביקה עובדים על ה-main בו זמנית, וצביקה היה הראשון שדחף (push) שינויים ל-main המרוחק, וכעת יוסי מנסה לדחוף את השינויים שלו. Git מזהה שהענף main המקומי של יוסי לא מסונכרן עם הענף main המרוחק, ולכן הוא עוצר את הפעולה ומציג הודעת שגיאה.
ההודעה הזו מבקשת מיוסי להריץ קודם git pull, כדי לעדכן את הענף המקומי שלו בשינויים האחרונים שבוצעו ב-main המרוחק. התהליך הזה חיוני כדי למנוע מצב שבו השינויים של יוסי ידרסו בטעות את השינויים שצביקה כבר דחף.
כדי להתקדם, יוסי חייב להריץ git pull, לעדכן את הענף המקומי שלו, ולפתור קונפליקטים (אם יש). רק לאחר שהענף המקומי תואם לחלוטין לענף המרוחק, יוסי יוכל לבצע push ל-main.
חשוב להדגיש שקונפליקט לא מתרחש רק בגלל שה-main המקומי של יוסי לא מסונכרן עם main המרוחק. קונפליקט מתרחש רק כאשר יש שינויים סותרים באותו חלק של אותו קובץ בשני ענפים שונים (למשל, השינויים של יוסי בענף שלו והשינויים שצביקה כבר דחף ל-main המרוחק).
אם צביקה שינה קובץ אחר וביצע Push, Git לא ייצור קונפליקט, כי השינויים לא מתנגשים – הם לא באותו חלק של הקוד. יוסי יצטרך לבצע git pull כדי לסנכרן את הענף המקומי שלו עם השינויים שביצע צביקה, אבל המיזוג יתבצע בצורה אוטומטית. לעומת זאת, אם צביקה שינה את אותו קובץ, זה תלוי האם השינויים מתנגשים:
אם צביקה שינה שורה 10, ויוסי שינה שורה 50 – לא יהיה קונפליקט.
אם שניהם שינו את שורה 10 – Git יזהה קונפליקט, כי הוא לא יודע איזה שינוי לשמור.
למה זה חשוב?
Git מזהה קונפליקטים רק כאשר יש שינויים מנוגדים בענפים שונים. כלומר, אם צביקה לא היה מבצע push, השינויים שלו לא היו קיימים ב-main המרוחק מבחינת Git, ולכן לא היה מתעורר קונפליקט. זה מדגיש את החשיבות של עבודה מסודרת עם Git, הכוללת משיכת שינויים (pull) עדכנית לפני ביצוע push. בצורה כזו, כל המפתחים מבטיחים שהענף המקומי שלהם תואם למה שקיים ב-remote repository.
סיטואציה נפוצה בצוותי פיתוח
נניח שיש צוות של 5 מפתחים, וכל אחד מהם משך את הקוד מה-stage ויצר לעצמו branch נפרד כדי שיוכלו לעבוד על המשימות שלהם. שלושה מתוך החמישה עשו push ל-stage, והשניים האחרים עדיין לא. המצב הזה עלול ליצור בעייתיות וקונפליקטים בקוד. למה? כי הקוד שהמפתחים שעדיין לא עשו push עובדים עליו מבוסס על גרסה ישנה של ה-stage, ולכן כשהם ינסו לבצע push, עלול להיווצר קונפליקט.
כדי לפתור מצב כזה, יש להנחות את המפתחים לבצע pull לפני שהם מבצעים push, כך שיקבלו את השינויים האחרונים ויפתרו קונפליקטים מקומיים במידת הצורך.