Git לפרילנסר וורדפרס: מ-zip ל-workflow אמיתי
אם אתם בונים אתרי וורדפרס כפרילנסרים, יכול להיות שאתם מנהלים גרסאות בעזרת תיקייה בשם site-final, אחר כך site-final2, ולפני שינוי גדול מכווצים הכול ל-ZIP ושומרים בצד. זה מספק עותק, אבל לא היסטוריה שקל לקרוא, להשוות ולשחזר.
המדריך הזה הוא על המעבר מ-ZIP ו-FTP ל-workflow מבוסס Git. הוא מסביר למה כל כלי קיים ומתי משתמשים בו, לא רק איך. אני עובד כך יום-יום, וזו אחת ההשקעות שהחזירו את עצמן הכי מהר. בסוף המדריך יש גם שלוש טעויות שכדאי להכיר מראש.
זה לא מדריך למתחילים מוחלטים. אני מניח שאתם מכירים שורת פקודה ברמה בסיסית ובניתם כבר אתר או שניים. אם זה אתם, בואו נתחיל.
מה Git בעצם עושה
Git הוא מערכת לניהול גרסאות. הוא שומר תצלומי-מצב (snapshots) של הפרויקט לאורך זמן. כל תצלום נקרא commit, ושמור עם הודעה, מחבר ותאריך. אתם יכולים לקפוץ לכל מצב שהפרויקט היה בו אי-פעם.
זה נותן לכם ארבעה דברים שה-zip לא נותן:
- רשת ביטחון. שברתם משהו? חוזרים אחורה ל-commit תקין.
- יומן עבודה. מה בדיוק שיניתם, לאיזה לקוח, ומתי.
- ניסויים בלי פחד. ענפים (branches), שנגיע אליהם עוד מעט.
- בסיס לאוטומציית deploy. repository מרוחק מאפשר לחבר בהמשך CI או תהליך פריסה, במקום להעביר קבצים ידנית. GitHub לבדו אינו מעלה את האתר לשרת.
אם בכוונתכם לעבוד עם Bedrock בהמשך, Git הוא מקור הקוד שעליו תהליך ה-deploy נשען. גם בלי Bedrock, ההיסטוריה והיכולת להשוות שינויים מועילות בכל פרויקט.
להתקין את Git
לפני הכול צריך שיהיה לכם Git על המחשב. אפשר להיעזר גם במדריך הרשמי של Git (נפתח בלשונית חדשה) לצד ההסברים כאן.
- macOS: הדרך הנקייה היא דרך Homebrew:
brew install gitאם אין לכם Homebrew, הקלדת git --version בטרמינל תציע להתקין את Xcode Command Line Tools, שכוללים גרסה של Git. שתי הדרכים תקינות; אני מעדיף Homebrew כי קל יותר לעדכן.
- Windows: אני ממליץ לעבוד בתוך WSL2 (סביבת לינוקס בתוך Windows), כי שם הסביבה מתנהגת כמו השרת שאליו תעלו בסוף. בתוך טרמינל ה-WSL2:
sudo apt install gitאם אתם מעדיפים לעבוד ישירות ב-Windows בלי WSL2, אפשר להתקין את Git for Windows (נפתח בלשונית חדשה), או להריץ winget install Git.Git. לכל אורך המדריך הזה אני מניח שאתם עובדים ב-WSL2.
אחרי ההתקנה, Git צריך לדעת מי אתם (החתימה על כל commit):
git config --global user.name "Your Name"git config --global init.defaultBranch mainמודל החשיבה היחיד שחשוב
זה החלק שאם תבינו אותו, כל השאר מסתדר. לקובץ ב-Git יש שלושה אזורים:
Working Directory → Staging Area → Repository (.git) (הקבצים האמיתיים) (מה ש-git add בחר) (תצלומים שנשמרו) עורכים git add git commitאתם עורכים קבצים ב-Working Directory. בוחרים אילו שינויים ייכנסו לתצלום הבא עם git add, זה מעביר אותם ל-Staging Area. ואז git commit מקפיא את מה שב-staging לתצלום קבוע ב-Repository (תיקיית .git הנסתרת).
שלב ה-staging מרגיש מוזר בהתחלה, וזו בדיוק הנקודה: אתם מחליטים מה נכנס לכל commit, במקום שהכול יישמר בערימה אחת. זה מה שהופך את ההיסטוריה לקריאה אחר כך.
לפני ה-commit הראשון צריך להפוך את התיקייה ל-repository וליצור .gitignore. בפרויקט וורדפרס בסיסי, התחילו למשל כך והתאימו את הנתיבים למבנה שלכם:
git init.env/vendor//node_modules//wp-content/uploads//web/app/uploads/*.sql*.sql.gz.DS_Store*.logשמרו את הרשימה בקובץ בשם .gitignore. רק אחר כך מתחילים לעקוב אחרי הקבצים. לפני ה-commit בודקים שוב את הרשימה כדי לוודא שלא נכנסו סודות, dumps או מדיה:
הזרימה היומיומית נראית ככה:
git status # מה השתנה ומה ב-staging, הפקודה שתריצו הכי הרבהgit add . # מעביר את כל השינויים ל-staginggit status # בדיקה אחרונה של הקבצים שנבחרוgit diff --staged # התוכן המדויק שייכנס ל-commitgit commit -m "Add footer template"git log --oneline # ההיסטוריה, שורה ל-commit
עוד שתי פקודות שתשתמשו בהן כל הזמן: git diff מראה מה השתנה ב-Working Directory ועדיין לא ב-staging, שורה-שורה, זו התשובה ל”מה אני עומד לעשות לו commit”. אחרי git add, השינוי כבר ב-staging, אז git diff רגיל יראה ריק; כדי לראות מה עומד להיכנס משתמשים ב-git diff --staged.
ענפים זולים, לעבוד בלי לפחד
זה החלק ששינה לי הכי הרבה את העבודה. branch הוא בסך הכול מצביע שזז ומראה על commit. יצירת branch לא מעתיקה קבצים; היא מיידית וכמעט בחינם.
למה זה משנה לפרילנסר: אתר הלקוח חי ועובד, ואתם רוצים לבנות פיצ’ר חדש. אתם לא רוצים קוד חצי גמור על ה-branch הראשי (main). לכן פותחים branch ועובדים בו עד שהשינוי מוכן לבדיקה. כך קל יותר לשמור את main במצב שנבדק וניתן לפריסה.
git switch -c feature-footer # יוצר branch ועובר אליו (-c = create)# ... עורכים, git add, git commit ...git switch main # חוזרים ל-maingit switch -c היא הפקודה המודרנית. ברחבי האינטרנט תיתקלו גם ב-git checkout -b, אותו דבר בדיוק.
שימו לב לדבר אחד שמבלבל בהתחלה: כשאתם עוברים בין branches, קבצים “נעלמים” ומופיעים. זה מכוון. מעבר branch משכתב את ה-Working Directory כך שיתאים לתצלום שה-branch מצביע עליו. הקבצים לא אבדו, הם שמורים בתוך ה-commits של ה-branch השני, ויחזרו ברגע שתחזרו אליו.
מיזוג, ושני סוגי merge
כשהפיצ’ר מוכן, ממזגים אותו חזרה ל-main:
git switch maingit merge feature-footerיש כאן שני תרחישים, וכדאי להכיר את שניהם כי הפלט נראה שונה:
- Fast-forward. אם
mainלא זז מאז שפתחתם את ה-branch, אין מה לשלב, Git פשוט מחליק את המצביעmainקדימה כדי להדביק את ה-branch. בלי merge commit חדש. - Merge commit. אם גם
mainוגם ה-branch קיבלו commits חדשים, אי אפשר להחליק. Git יוצר merge commit שמשלב את שתי ההיסטוריות. זה ה-commit היחיד עם שני הורים, וזו הדרך של Git לתעד “כאן שני קווי עבודה התאחדו”.
מיזוג של שינויים נפרדים עובר בדרך כלל אוטומטית. כששני branches כוללים שינויים חופפים ש-Git אינו יודע לשלב, מקבלים merge conflict. זו אינה תקלה ב-Git, אלא מקום שבו צריך לבחור ידנית את התוצאה הרצויה. הקובץ יכיל סמנים:
<<<<<<< HEAD# הגרסה שלי=======# הגרסה הנכנסת>>>>>>> feature-titleהפתרון פשוט גם אם הוא נראה מאיים: עורכים את הקובץ לטקסט הסופי הרצוי, מוחקים את כל שלושת הסמנים (<<<<<<<, =======, >>>>>>>), ואז:
git add README.md # אומר ל-Git "פתרתי את הקונפליקט"git commit -m "Merge feature-title"הסמנים נראים מאיימים בפעם הראשונה, אבל התהליך קבוע: עורכים את התוצאה, מסירים את הסמנים, מוסיפים את הקובץ ל-staging ומשלימים את ה-commit.
כשמשהו משתבש, לתקן טעויות
אין ב-Git כפתור “undo” אחד. הפקודה תלויה באיפה הטעות נמצאת כרגע. הנה המפה:
- שינוי לא-שמור שאתם רוצים לזרוק:
git restore <file>מחזיר את הקובץ למצב ה-commit האחרון. זו פקודה הרסנית: השינוי הלא-שמור נמחק. רק כשאתם בטוחים שהוא זבל. - הכנסתם קובץ ל-staging בטעות:
git restore --staged <file>מוציא אותו מ-staging, אבל התוכן נשאר. - ה-commit האחרון שגוי (הודעה לא נכונה, או שכחתם קובץ),
git commit --amend. זה מחליף את ה-commit האחרון ב-commit חדש. - commit ישן שאתם רוצים לבטל: כאן יש שתי פקודות, וההבדל ביניהן קריטי. נחזור אליו בעוד רגע ב”מלכודות”.
ועוד פקודה שימושית היא git stash. לפעמים Git לא יאפשר לעבור branch עם שינויים פתוחים, וגם כשהוא מאפשר לא תמיד תרצו לסחוב אותם למשימה אחרת. במקרה כזה אפשר לשמור את השינויים זמנית:
git stash push -u -m "wip" # כולל גם קבצים חדשים שעדיין אינם במעקבgit switch main # מטפלים בדחוףgit switch feature-xgit stash pop # מחזירים את העבודה למקוםלהתחבר ל-GitHub
עד עכשיו הכול היה מקומי. GitHub הוא שירות אירוח ל-repos של Git: גיבוי בענן, גישה מכל מקום, ובסיס לדפלוי. Git הוא הכלי לניהול הגרסאות; GitHub הוא שירות שמאחסן repos של Git בענן.
אם GitHub CLI כבר מותקן ומחובר לחשבון, אפשר ליצור repository פרטי, להגדיר remote ולדחוף את main בפקודה אחת:
gh repo create my-project --private --source=. --remote=origin --pushremote הוא שם שמצביע על עותק של ה-repository שמתארח במקום אחר. המוסכמה היא לקרוא לראשי origin. הפקודה שלמעלה מגדירה את הקשר בין main המקומי ל-origin/main, כך שבהמשך אפשר להריץ git push ו-git pull בלי ארגומנטים.
עכשיו נקודה שמבלבלת המון אנשים, ושווה להבין לעומק. ה-repo המקומי שלכם שומר מצביע בשם origin/main, תמונת מצב של “איפה main של GitHub היה בפעם האחרונה שבדקתי”. המצביע הזה תקוע ולא מתעדכן לבד, גם אם מישהו דחף שינויים ל-GitHub. הוא זז רק כשאתם פונים לשרת. מכאן ההבדל בין שתי פקודות:
git fetch # מוריד מידע ומעדכן את origin/main, בלי לשנות את הקבציםgit pull --ff-only # מעדכן רק אם אפשר להתקדם בלי ליצור merge commitfetch מאפשר לבדוק מה השתנה ב-remote בלי לשנות את ה-branch המקומי. pull מבצע fetch ואז משלב את ה-branch המרוחק לפי הגדרת Git, באמצעות fast-forward, merge או rebase. אני מעדיף להתחיל ב-git fetch כשלא ברור מה השתנה בצד השני. אפשר להעמיק בתיעוד הרשמי של git pull (נפתח בלשונית חדשה).
ולהתחיל עבודה על פרויקט קיים (או על מחשב חדש) זה פשוט git clone <url>, זה מוריד את כל ההיסטוריה ומגדיר את origin אוטומטית.
Pull Request, גם כשאתם לבד
Pull Request (PR) הוא בקשה למזג branch אחד לתוך אחר, עטופה בעמוד GitHub שבו רואים את ה-diff, מריצים בדיקות אוטומטיות, ולוחצים merge. זה merge מנוהל עם שלב סקירה.
“רגע, אני עובד לבד, למה לי PR?” גם בעבודת סולו, PR נותן מקום אחד לסקור את ה-diff ולראות אם הבדיקות עברו לפני שהשינוי נכנס ל-main. אפשר גם להגדיר branch protection (נפתח בלשונית חדשה) שחוסם push ישיר או דורש בדיקות, בהתאם לסוג ה-repository ולתוכנית GitHub.
git switch -c feature-aboutgit add . && git commit -m "Add about page"git push -u origin feature-about # דוחפים את ה-BRANCH, לא את maingh pr create --base main --head feature-about --title "Add about page"gh pr merge --merge --delete-branch # ממזג ומנקה את ה-branch בשני הצדדים
שלוש טעויות שכדאי להכיר מראש
אלה טעויות נפוצות במעבר לעבודה עם Git, ולכל אחת מהן יש דרך ברורה להימנע מנזק.
1. ה-.env שכבר נכנס ל-commit
בפרויקט וורדפרס יש דברים שאסור שייכנסו ל-repo: סודות (סיסמת DB, מפתחות API ב-.env), תלויות שאפשר להוריד מחדש (vendor/, node_modules/), ומדיה שהועלתה. בשביל זה יש את .gitignore, קובץ טקסט עם רשימת patterns ש-Git יתעלם מהם:
.env/vendor//node_modules//wp-content/uploads//web/app/uploads/*.sql*.sql.gz.DS_Store*.logהמלכודת: ל-.gitignore אין השפעה רטרואקטיבית. הוא מתעלם רק מקבצים ש-Git עוד לא עוקב אחריהם. אם כבר עשיתם commit ל-.env, הפעולה הראשונה היא לבטל או להחליף את הסודות שבתוכו. אחר כך מפסיקים לעקוב אחרי הקובץ:
git rm --cached .env # מפסיק לעקוב, בלי למחוק את הקובץ עצמוgit commit -m "Stop tracking .env"הפקודה הזו אינה מסירה את הסוד מהיסטוריית Git. אם ה-commit כבר נדחף ל-GitHub, עקבו אחרי הנחיות GitHub להסרת מידע רגיש (נפתח בלשונית חדשה). ניקוי היסטוריה דורש תיאום עם מי שכבר משך את ה-repository, ולכן החלפת הסוד היא תמיד הצעד הראשון.
הכלל שיחסוך לכם את כל הסיפור: כתבו את .gitignore לפני ה-git add הגדול הראשון.
2. case-sensitivity שעובד אצלכם ושובר בייצור
זו תקלה שמופיעה לעיתים רק בשרת. מערכות הקבצים הנפוצות ב-macOS וב-Windows אינן רגישות לאותיות כברירת מחדל, בעוד Linux בדרך כלל כן. לכן Header.php ו-header.php עשויים להיחשב אותו קובץ במחשב וקבצים שונים בשרת.
התרחיש הקלאסי: רשמתם בקוד require 'Header.php' אבל הקובץ נשמר כ-header.php. כדי לבצע שינוי שם שמחליף רק אותיות בצורה ש-Git יתעד בוודאות, השתמשו בשם ביניים:
git mv header.php header.tmpgit mv header.tmp Header.php3. reset מול revert, מתי כל אחד
שתי הפקודות מבטלות commit, אבל בדרך הפוכה לגמרי, וההבדל קריטי:
git resetמזיז את מצביע ה-branch אחורה ובכך משכתב היסטוריה. הוא שימושי לטעויות מקומיות שעוד לא דחפתם.git reset --hardמוחק גם שינויים לא שמורים, ולכן אל תריצו אותו בלי לבדוק היטב מה יימחק.git revertלא מוחק כלום. הוא יוצר commit חדש שהוא ההיפוך של היעד, מבטל את השינוי ומשאיר את ההיסטוריה שלמה.
git reset --mixed HEAD~1 # מקומי: מבטל commit, השינויים חוזרים ל-working dirgit revert HEAD --no-edit # משותף: יוצר commit חדש שמבטל את הקודםהכלל במשפט אחד: reset לטעויות מקומיות שעוד לא דחפתם; revert לכל מה שכבר נמצא ב-GitHub. אם תשכתבו היסטוריה שכבר שיתפתם, תשברו אותה לכל מי שמשך אותה, כולל לשרת. זה כל המשחק.
איך זה נראה ביום-יום
אחרי שכל החלקים במקום, הלולאה שאני עובד בה על כל פרויקט נראית ככה:
1. git switch -c feature/x # branch לכל משימה2. work, commit in small steps # כל commit = שינוי לוגי אחד3. git push -u origin feature/x # דוחפים את ה-branch4. open a PR # סוקרים את ה-diff5. merge PR into main # main נשאר במצב שנבדקהזרימה הזו מוסיפה כמה צעדים, אבל היא נותנת יומן עבודה, דרך מסודרת לסקור שינויים ובסיס לתהליך deploy. המטרה היא לשמור את main במצב שנבדק, בזמן שעבודה שעדיין לא הושלמה נשארת ב-branches.
מכאן אפשר לחבר את Git לסביבת פיתוח מקומית עם DDEV ולנהל את WordPress והתוספים כתלויות באמצעות Bedrock.
תגובות
טוען תגובות…