במאמר הקודם למדנו על פקודות בסיסיות ב-SVN שלוקחות את הידע התיאורטי שלמדנו במאמר הראשון במדריך ומתרגמות אותו לידע מעשי. למדנו על checkout שלוקח את הקוד ממאגר התוכנה שלנו, הלא הוא ה-repository ומושך אותו אל המחשב המקומי שלנו. למדנו גם איך עושים update כדי למשוך שינויים מהמאגר המרוחק אל המחשב שלנו. איך עושים diff כדי לראות את השינויים ואיך עושים checkin על מנת לדחוף את השינויים אל מאגר התוכנה המרוחק. בסופו של דבר למדנו גם איך יוצרים ענפים שונים. במאמר הזה אנו נלמד על המיזוג ועל קונפליקטים.
מה קורה אם יצאתי מה-trunk אל ענף בשם develop. שיניתי שם קובץ בשם file1.php. בזמן שאני עובד בשצף קצף על הענף שלי, מתכנת אחר שינה את אותו קובץ ב-trunk? ב-SVN תהיה לי בעיה ברגע שאמזג את הענף שלי אל ה-trunk. הקובץ שאותו אני מנסה למזג כבר שונה על ידי מתכנת אחר!
הדיאגרמה הזו תבהיר את הבעיה:
SVN מספיק חכם כדי לדעת שהקובץ file1.php השתנה ב-trunk. המצב שבו אני מנסה לשנות קובץ שהשתנה מאז הפעם האחרונה שהוצאתי את הענף נקרא conflict. בדרך כלל אם השינויים נעשו במקומות שונים בקובץ – למשל המתכנת שינה את שורה 1-5 ואני שיניתי את שורה 20-30, לא יהיה קונפליקט. SVN ידע למזג את השינויים בקובץ לקובץ מאוחד שיכיל גם את השינויים שלי וגם את השינויים של המתכנת השני. אבל אם שנינו שינינו את שורה 1-5, יש לנו מצב של קונפליקט.
במצב הזה, נדרשת התערבות אנושית של מפתח על מנת לפשר בקונפליקט. האם השינוי שלי נחשב יותר? האם השינוי של המתכנת השני נחשב יותר? האם אפשר לכתוב את הקובץ מחדש על מנת שיכיל את שני השינויים? יישוב הקונפליקט נקרא באנגלית resolving conflicts וכאשר עובדים בצוות, זה תמיד קורה. הדבר הכי חשוב הוא להזהר ולא למחוק את השינויים של המתכנת השני שאחרי זה יבוא להיפרע מכם.
איך זה נראה בפועל? ברגע שאני אנסה למזג את השינויים בענף של ה-trunk, יהיה לי קונפליקט, אצטרך לשנות את הקוד ואז לדחוף מחדש את הקוד למנהל הגרסאות.
נניח שיש לי פרויקט שנראה כך:
. ├── branches │ └── develop │ └── index.php └── trunk └── index.php 3 directories, 2 files
מישהו שינה ב-trunk את ה-Index.php. אני שיניתי ב-develop את ה-index.php. אני רוצה להחזיר את ה-develop ל-branch. ראשית אני צריך לוודא שכל השינויים המרוחקים נשאבים לענף שלי. אני מבצע פקודת merge באופן הזה:
$ svn merge http://localhost/svn/testrepo/trunk/
שימו לב שבמקרה הזה ה-URL של ה-repo הוא של המאגר שלי. אם תעבדו מול מאגר של SVN, יהיה לכם URL שונה מן הסתם.
ברגע שאני מקליד את הפקודה ומשגר אותה, מייד ה-SVN מתריע בפני שיש לי קונפליקט ושואל אותי מה לעשות.
אני יכול להציג את השוני (diff) בין הקבצים, להכנס ישר למוד של עריכה, למחוק את השינויים שלי ולקבל את הגרסה החיצונית (שנקראת הגרסה שלהם), לקבל רק את הגרסה שלי. אני אבקש להציג את השינוי קודם כל. זה מה שאקבל:
--- index.php.working - MINE +++ index.php - MERGED @@ -1,6 +1,10 @@ < ?php //Printing Hello World! +<<<<<<< .working print "Hello World???"; +======= +print "Hello World!!!!"; +>>>>>>> .merge-right.r8
אתם יכולים לנחש מה מהות הקונפליקט? במקרה הזה יש לנו את הגרסה שלי יש, זו שהיא working. הגרסה הלוקלית. מה כתוב בה? hello world עם סימני שאלה. בגרסה המרוחקת, של ה-trunk, יש את ה-hello world עם סימני קריאה.
אחרי זה, אני עדיין צריך להחליט מה לעשות. במקרה הזה אני אבחר להכנס לתהליך של מיזוג או merge. במיזוג אני צריך לקבל החלטות קשות נוספות שנוגעות לקוד עצמו. האם להציב את השינויים שלהם לפני? להציב את השינויים שלי לפני השינויים שלהם?
Merging 'index.php'. Conflicting section found during merge: (1) their version (at line 4) |(2) your version (at line 4) -----------------------------------------------------------------+----------------------------------------------------------------- print "Hello World!!!!"; |print "Hello World???"; -----------------------------------------------------------------+----------------------------------------------------------------- Select: (1) use their version, (2) use your version, (12) their version first, then yours, (21) your version first, then theirs, (e1) edit their version and use the result, (e2) edit your version and use the result, (eb) edit both versions and use the result, (p) postpone this conflicting section leaving conflict markers, (a) abort file merge and return to main menu:
מכאן זה תלוי בקוד. בכל מקרה אחרי ההחלטה, אני שוב נשאל אם יש עוד משהו שאני צריך לעשות. אם ערכתי ותיקנתי, אני יכול לציין שהקונפליקט נפתר – resolved:
Merge of 'index.php' completed. Select: (p) postpone, (df) show diff, (e) edit file, (m) merge, (r) mark resolved, (mc) my side of conflict, (tc) their side of conflict, (s) show all options: r
אחרי שהקונפליקט נפתר, עלי לבצע check in כדי להכניס את פתרון הקונפליקט לענף שלי. עכשיו, אני יכול לבצע את המיזוג. אני ניגש אל ה-trunk, מבצע update ליתר בטחון ואז מושך את ענף ה-develop אלי באמצעות פקודת:
svn merge --reintegrate http://localhost/svn/testrepo/branches/develop
למדנו כאן כמה מונחים חשובים:
קונפליקט – מה שקורה כאשר יש שינוי באותו מקום באותו קובץ שנעשה בשני ענפים שונים בנקודות זמן שונות. מי שצריך לפתור את הקונפליקט הוא מפתח אנושי.
פתרון הקונפליקט (resolve) הוא מה שהמפתח האנושי מחליט לעשות עם הקוד. יש מספר רב של דרכים לפתור את הקונפליקט. אבל אחרי שהקונפליקט נפתר, המפתח חייב להודיע ל-SVN שיש פתרון באמצעות mark resolved.
ראינו גם ש-diff הוא תהליך חשוב מאוד בתהליך הקונפליקטים.
במאמר הבא נדבר על כלים נוספים של SVN שיסייעו לנו לראות את הכוח האמיתי של מערכת ניהול הגרסאות.