גיט() היא מערכת ניהול גרסאות מבוססת קוד פתוח.
מערכת לניהול גרסאות היא מערכת המאפשרת לנו לשמור גרסאות של הקוד שלנו.
למה זה טוב?
- אם הכנסו באג בתוכנה, נוכל לחזור לגרסה התקינה
- אם כמה אנשים רוצים לעבוד כל אחד על פיצ'ר, הם יכולים לעשות זאת בנפרד, כל אחד בגרסה משלו ואז למזג את הגרסאות ביחד לגרסה אחת סופית.
גיט מאפשר לנוL:
- לעקוב אחרי קבצים לאורך זמן
- לעבוד על קבצים במקביל עם מפתחים אחרים
- להשוות בין גרסאות שונות של אותו קובץ
- לחזור חזרה לגרסה ישנה של קבצים במידת הצורך
אבני הבניין של
המאגר -
פרויקט אותו אנו מנהלים באמצעות גיט נקרא (או בקיצור)
לכל מאגר שנרצה לעבוד עליו תהיה לנו תקייה במחשב בה ישמרו הקבצים של מאגר זה ובה נעבוד. כמו כן, בתיקיה ישמרו קבצים נוספים המשמים את גיט כדי לשמור מידע על המאגר אותם ינהל הכלי בשבילנו.
קומיט -
אבן הבניין הבסיסית בעבודה עם גיט נקרא .
כל קומיט היא אוסף של שינויים בקבצים שלנו שאנו שומרים בגרסה חדשה. הרצף של כל הקומיטים עד כה מהווה את המצב הנוכחי של הפרוייקט.
לכל קומיט יש:
- מזהה יחודי
- מצביע לקומיט שהיה לפניו
זה בצעם רשימה מקושרת
מבנה הפקודות של
git <sub-command> [flags] [files/branches]
כאשר היא הפקודה המתאימה לפעולה הספציפית אותה אנו נרצה לבצע והשאר הם ממש כמו ב- שראינו בתרגול הראשון.
מצבי קבצים ב-
כל קובץ במאגר נמצא באחד מארבעה מצבים:
- אם הקובץ לא שונה מאז שביצענו קומיט לשינויים האחרונים הוא יקרא .
- אם ביצענו שינויים בקובץ ועדיין לא הכנסנו אותם לאף קומיט הוא יקרא
- אם ביצענו שינויים בקובץ וסימנו אותם בתור שינויים שנרצה לכלול בקומיט הקרוב הוא יקרא
- אם גיט לא עוקב אחרי הקובץ הוא יקרא .
פקודות לשינוי מצבי קובץ
הפקודה משמשת להוספת קבצים לרשימת השינויים שנרצה לכלול בקומיט הקרוב. (הפקודה מעבירה קבצים למצב )
git add file1.c
הפקודה יוצרת קומיט חדש הכולל את כל השינויים במאגר שהיו במצב .
git commit -m "Fixing a bug in the main function"
הפקודה מספקת לנו מידע על המצב של הקבצים במאגר שלנו. בעזרתה נוכל לראות אילו קבצים במצב ואילו במצב וכו'
git status
הפקודה מראה לנו שאת השינויים במאגר בהשוואה לקומיט האחרון.
כדי לראות את כל השינויים בכל הקבצים במצב נכתוב:
git diff
כדי לראות את השינויים בקבצים ספציפיים במצב , למשל בקובץ :
git diff file1.c
כדי לראות שינויים בקבצים במצב נכתוב:
git diff --staged
הפקודה מראה לנו רשימה של כל הקומיטים האחרונים במאגר
git log
נוכל להסתכל על כמה דגלים שימושיים:
- הדגל
-<n>מגביל את מספר הקומיטים המוצגים - הדגל
--onelineמציג כל קומיט בשורה בודדת ומראה רק את הכותרות - הדגל
--graphמציג את רצף הקומיטים בצורה של גרף - הדגל
-pמציג את כל השינויים הכלולים כל קומיט - הדגל
--statמציג סיכום מתומצת של השינויים עבור כל קומיט - הדגל
--allמציג את כל הקומיטים מכל הענפים.
ניתן לשלב דגלים אחד עם השני. למשל:
git log --all --graph --oneline
עבודה נכונה עם קומיטים
כל קומיט או אטומי - מכיל שינוי לוגי אחד של הפרוייקט. למשל:
- הוספת פיצ'ר קטן אחד
- תיקון של באג בפרוייקט
כדאי לכתוב שמות טובים לקומיט כדי שנדע מה קרה בו ומה השתנה
במידה והפרויקט שלנו כבר במצב בו התוכנית עובדת, בין כל שני קומיטים נרצה לשמור על מצב זה ולא לשבור את התוכנית.
ענפים -
ענף הוא בעצם רשימה מקושרת שיוצאת מקומיט מסויים ומתנתקת משאר הרצף די שנוכל לעשות בה שינויים ואולי גם בסוף לשלב אותה ביחד עם הרצף המקורי.
הפקודה מאפשרת לנו לראות אילו ענפים יש לנו במאגר:
git branch
אם נוסיף בסוף הפקודה שם, היא תיצור ענף חדש בשם זה שיתחיל בקומיט שבו אנו נמצאים:
git branch newBranch
הפקודה מאפשרת לנו לעבור בין ענפים וקומיטים שונים
git checkout branch1
ניתן ליצור ענף ידש וגם לעבור אליו בפקודה יחידה. נעשה זאת ע"י הוספת הדגל -b לפקודה:
git checkout -b branch2
הפקודה ממזגת שני ענפים. כלומר, היא יוצרת קומיט מיוחד המצביע לשני הענפים אותם מיזגנו.
git merge feature_branch
את הפקודה נפעיל מהענף אליו נרצה להביא את השינויים ובה נציין את הענף ממנו נרצה להביא את השינויים.
במידה והענפים מוכלים אחד בשני, הפקודה לא תיצור קומיט חדש, אלא רק תזיז את הענף אליו אנו מביאים את השינויים כך שיצביע על הקומיט העדכני ביותר.
לשינוי זה קוראים .
הפקודה מזיזה ענץ אחד מעל ענץ אחר. כלומר, הפקודה יוצרת עותקים של כל הקומיטים בענף הראשון וגורמת להם להצביע לענף השני.
git rebase master
את הפקודה נפעיל מהענף ממנו נרצה להביא את השינויים ובה נציין את שם הענף אליו נרצה להביא את השינויים.
עבודה נכונה עם ענפים
- נהוג לשמור ענף אחד מרכזי הנקרא או .
בדרך כלל לא נעבוד ישירות על הענף הזה אלא נעדכן אותו רק ע"י מיזוג של ענפים אחרים. - בכל פעם שנרצה לעשות שינוי במערכת:
- נפתח ענף חדש וזמני המיועד לשינויים אותם נרצה להוסיף ונעבור אליו
- נוסיף על הענף החדש קומיטים המכילים את השינויים שלנו
- נמזג את השינויים לתוך באמצעות פקודת או
- אופיונלי: נמחק את הענף הזמני שיצרנו
- בפרוייקטים גדולים נהוג להחזיק ענף קבוע הנקרא המייצג את המצב התכנית העדכני עבור המפתחים בעוד מייצג את המצב העדכני עבור הלקוח.
עבודה מרחוק -
כדי לאפשר עבודה של מספר מפתחים על הפרויקט, כל אחד ממחשב אחר, נארח את המאגר שלנו על שרת גיט(למשל )
כל מפתח יחזיק עותק מקומי של המאגר המאוחסן על השרת ומדי פעם יבוצעו עדכונים בין השרת לעותק המקומי.
כאשר אנחנו עובדים על מאגר הוא יכול להיות פומבי או פרטי. במידה והמאגר מסומן כפומבי כולם יכולים לראות אותו ואת תוכנו.
שימו אין להעלות מאגרים פומביים לתרגילי הבית!
הפקודה יוצרת עותק מקומי של מאגר המאוחסן בשרת גיט מרוחק. בפקודה נציין את ה- בו מאוחסן המאגר.
git clone https://github.com/CS234124/GitSimulation..git
עבודה מרחוק עם ענפים
בנוסף לאלו שקיימים במאגר המקומי, עבור כל אחד מהענפים בשרת המרוחק יהיה לנו עותק מקומי.
במאגר המקומי, שמות הענפים יתחילו בשם השרת המרוחק(בדרך כלל )
בדרך כלל לא נוסיף שינויים ישירות לענפים אלו.
כעת, יכולות להיות לנו 3 גרסאות שונות לכל ענף. למשל, עבור ענף הנקרא :
- הענף מקומי() - הגרסה עליה אנו עובדים ולה אנו מוסיפים שינויים
- הענף מקומי() - גרסה מקומית המקבילה לענף בשרת המרוחק
- הענף בשרת המרוחק - גרסה המשותפת לכל המפתחים בפרוייקט
לעיתים נרצה לסנכרן בין שלושת הגרסאות הללו, למשל כשנרצה להביא שינויים חדשים מהשרת אלינו או כאשר נרצה לעדכן את שאר המפתחים בפרוייקט בשינויים שביצענו מקומית.
הפקודה מביאה את כל השינויים מהשרת המרוחק אלינו ומעדכנת את כל ה- הנמצאים במאגר המקומי
git fetch
שימו - הפקודה לא משנה את הענפים המקומיים עליהם אנו עובדים.
הפקודה גם מעדכנת את ה- וגם מבצעת לענף המקומי עליו אנו נמצאים כך שיהיה מסונכרן עם זה שבשרת המרוחק.
git pull
כלומר, אפשר לחושב על הפקודה כאילו היא מבצעת שני דברים:
git fetch
git merge
כמובן עם הדגלים והקבצים המתאימים.
במידה ויש שינויים חדשים בענף המקומי וגם בזה שנמצא על השרת, לא יהיה ניתן לבצע פקודת .
במקרה זה נוכל להוסיף את הדגל --rebase וכעת הפקודה תבצע ואז של הענף המקומי מעל זה שהבאנו מהשרת המרוחק
git pull --rebase
הפקודה מעדכנת את השרת המרוחק בשינויים שביצענו בענף המקומי.
git push
שימו - פקודת ה- פועלת רק על הענף בו אנו נמצאים בעת ההפעלה
שימו - במידה וקיימים שינויים גם מקומית וגם בשרת המרוחק לא נוכל לבצע ותחילה נדרש לבצע ורק לאחר מכן לבצע .
רצף עבודה במאגר מרוחק
בכל פעם שנרצה לבצע שינויים במאגר שלנו ולעדכן את מצב הפרוייקט:
- נבצע לענף הבסיס ממנו אנו רוצים להתחיל את העבודה
- ניצור ענף חדש מקומי ובו נוסיף את כל השינויים שלנו
- מדי פעם, נעדכן את העף המקומי:
- נבצע לענף הבסיסי.
- נבצע לענף עליו אנו עובדים, מעל הבסיס.
- נמזג את הענף עליו אנו עובדים אל הבסיס באמצעות או .
- נבצע לענף הבסיס כך שיעודכן בשרת המרוחק.
שימו - למעשה לא הוספנו אף ענף לשרת המרוחק.
קונפליקטים
כאשר ממזגים שינויים באמצעות הפקודה יכולים להיווצר קונפליקטים בין הענפים.
$ git log --all --graph --oneline
* ed2bd95 (branch1) Changed print to "Goodbye"
| * 5586254 (HEAD -> master) Added "World" to print
|/
* 5e43971 Initial commit
$ git merge branch1
Auto-merging file1.c
CONFLICT (content): Merge conflict in file1.c
Automatic merge failed; fix conflicts and then commit the result.
כאשר אנו נתקלים בקונפליקטים תחילה נברר באילו קבצים הם מתרחשים באמצעות הפקודה
git status
פתרון קונפליקט
גיט מסמן לנו את המקומות בהם יש קונפליקטים בקובץ ותפרט על התוכן שיוצר את הקונפליקט ע"י הוספת שורות העוטפות את השינויים הבעייתיים.
נוכל לפתור את הקונפליקט ע"י מחיקת השורות המיותרות והשארת השינויים אותם נרצה לשמור.
#include <stdio.h>
int main()
{
<<<<<<< HEAD
printf("Hello, World!\n");
=======
printf("Goodbye\n");
>>>>>>> branch1
}
נוכל בנוסף להשתמש בסביבת הפיתוח שלנו שבה לרוב יהיה יותר חביב לפתרון הקונפליקטים.
כאשר אנו מסיימים לפתור את כל הקונפליקטים נסמן לגיט שסיימנו ע"י הוספת הקבצים הרלוונטיים באמצעות הפקודה .
לאחר מכן נבצע פקודת וייוצר עבורינו המכיל את השינויים הנדרשים לפתרון קונפליקטים.
$ git add file1.c
$ git commit
[master aabc51a] Merge branch 'branch1'
$ git log --all --graph --oneline
* aabc51a (HEAD -> master) Merge branch 'branch1'
|\
| * ed2bd95 (branch1) Changed print to "Goodbye"
* | 5586254 Added "World" to print
|/
* 5e43971 Initial commit
ב-
גם כאשר נבצע של ענף אחד מעל השני, יכולים להיווצר קונפליקטים. בשונה מ-, כאן יכולים להיווצר קונפליקטים בכל אחד מה- שאנחנו "מזיזים" בנפרד.
במקרה כזה גיט יעצור את ה- בכל ויתן לנו הזדמנות לפתור אותם בדומה ל- ולאחר מכן נוכל להמשיך עם פעולת ה-.
פתירת קונפליקטים ב-
- גיט תעצור את ה- כאשר היא מוצאת קונפליקט
- נפתור את הקונפליקט ע"י עריכת הקבצים הרלוונטיים
- "נסמן" לגיט שפתרנו את הקונפליקט ע"י פקודת
- נמשיך את ה- באמצעות הפקודה:
git rebase --continue.
הקובץ
זהו קובץ טקסט שאינו חובה ליצור אשר מאפשר לנו לפרט מאילו קבצים בתיקיה גיט צריך להתעלם.
כל שורה בקובץ זה היא שם של קובץ או תיקיה ממנה נרצה שפקודות הגיט שלנו יתעלמו.