טיפ גיט: על הקשר בין בראנצ'ים לקומיטים

04/05/2018
git

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

השבוע דיברתי עם אליעזר במפגש תרגול git ועברנו על בראנצ'ים ועל הפקודה git branch -f שלדעתי עוזרת לעשות קצת סדר בקשר בין בראנצ'ים לקומיטים.

הבלבול מתחיל כבר כשפותחים פרויקט חדש:

$ git init
$ git log
fatal: your current branch 'master' does not have any commits yet

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

$ echo 1 > demo.txt
$ git add .
$ git commit -m 'commit 1'
$ echo 2 > demo.txt
$ git commit -a -m 'commit 2'
$ git log
localhost:project ynonperek$ git log
commit cd877d58a9885372b9b6ff93c77dabcff8b58052 (HEAD -> master)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:51 2018 +0300

    commit 2

commit 85f0794fe33b4718dce3f02b480a119261d64115
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:40 2018 +0300

    commit 1

נראה אחלה עד שחוזרים אחורה:

$ git checkout 85f0794fe33b4718dce3f02b480a119261d64115

$ git log
commit 85f0794fe33b4718dce3f02b480a119261d64115 (HEAD)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:40 2018 +0300

    commit 1

$ git branch
* (HEAD detached at 85f0794)
  master

אם חשבנו על בראנצ' כספריה ועל קומיט בתור מצב עניינים בספריה ברגע נתון - מה עכשיו קרה כאן? ברור שאנחנו לא בבראנצ' master יותר, אז מה פתאום קומיט עזב את הבראנצ' שלו? ואיך חוזרים?

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

בדוגמא שלנו המדבקה HEAD היתה מחוברת לקומיט cd877. אחרי ביצוע checkout הזזנו את המדבקה HEAD לקומיט הישן יותר. המדבקה master אגב עדיין נשארה מחוברת לקומיט החדש. הנה כמה פקודות שעכשיו נראות הרבה יותר הגיוניות (ממשיכים מאותו מצב):

$ git checkout master
$ git branch
* master
$ git log
commit cd877d58a9885372b9b6ff93c77dabcff8b58052 (HEAD -> master)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:51 2018 +0300

    commit 2

commit 85f0794fe33b4718dce3f02b480a119261d64115
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:40 2018 +0300

    commit 1

ואם קומיט לא חי בתוך בראנצ' אפשר להתחיל לעשות דברים מעניינים - כמו לעקוף את master ולבצע קומיט ישירות מתוך קומיט אחר:

$ git checkout cd877
$ echo 3 > demo.txt
$ git commit -a -m 'commit 3'
$ git log
commit ff12c93ea35fce6e1583d877458f848da796a26f (HEAD)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:37:11 2018 +0300

    commit 3

commit cd877d58a9885372b9b6ff93c77dabcff8b58052 (master)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:51 2018 +0300

    commit 2

commit 85f0794fe33b4718dce3f02b480a119261d64115
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:40 2018 +0300

    commit 1

פעולת git checkout הזיזה את HEAD ועכשיו כל הפעולות שלנו מתבצעות על קומיט cd877 ולא "מושכות" את master קדימה. מה שיפה שמאחר ואנחנו חושבים על בראנצ'ים כמו מדבקות קל מאוד להזיז את master לקומיט אחר. הפקודות הבאות מתקנות את המצב:

$ git branch -f master HEAD
$ git checkout master
$ git log
commit ff12c93ea35fce6e1583d877458f848da796a26f (HEAD -> master)
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:37:11 2018 +0300

    commit 3

commit cd877d58a9885372b9b6ff93c77dabcff8b58052
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:51 2018 +0300

    commit 2

commit 85f0794fe33b4718dce3f02b480a119261d64115
Author: ynonp <ynonperek@gmail.com>
Date:   Thu May 3 08:23:40 2018 +0300

    commit 1

הפקודה git branch -f מזיזה ברנאצ' לקומיט אחר. היא יכולה להתקיים כי קומיטים לא חיים בתוך בראנצ'ים אלא מדובר במבנה הרבה יותר גמיש ואין בעיה להזיז את הבראנצ' לקומיטים אחרים ובחזרה.

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