בקצרה
- מתקינים Git עם apt או dnf, ומגדירים שם, אימייל, ענף ברירת מחדל ומשיכה בלי מיזוגים אוטומטיים.
- השרת מתחבר ל‑GitHub או ל‑GitLab עם מפתח SSH ייעודי מסוג ed25519, עדיף כ‑Deploy Key לקריאה בלבד.
- פריסה בטוחה היא משיכה שמסרבת למזג היסטוריות שהתפצלו, או מאגר bare עם hook שמעתיק את הקוד לתיקיית האתר.
- תיקיית .git אסור שתהיה נגישה מהדפדפן, ועדיף שבכלל לא תהיה בתוך תיקיית האתר.
למה Git על השרת
Git הוא מערכת לניהול גרסאות: הוא שומר כל שינוי בקוד, מי עשה אותו ומתי, ומאפשר לחזור אחורה. כשהשרת מושך את הקוד ממאגר Git, במקום שמעלים קבצים ב‑FTP, הפריסה הופכת לצפויה: אותה גרסה שנבדקה אצלכם היא זו שעולה לאוויר, ורואים בדיוק מה השתנה.
הכלל החשוב: עורכים קוד אצלכם או בסביבת בדיקות, דוחפים למאגר, והשרת רק מושך. מי שעורך קבצים ישירות בשרת החי שובר את השיטה, כפי שנראה בפרק התקלות. המדריך מניח שרת לינוקס עם גישת SSH ומשתמש עם הרשאות sudo.
התקנה והגדרה ראשונית
ב‑Ubuntu וב‑Debian:
sudo apt update
sudo apt install -y git
ב‑AlmaLinux, Rocky Linux, RHEL ו‑Fedora:
sudo dnf install -y git
בודקים שההתקנה הצליחה עם git --version. הגרסה שמגיעה מהמאגרים של ההפצה מספיקה לפריסה, ואין צורך לקמפל גרסה חדשה יותר.
Git רושם שם ואימייל בכל Commit (שמירה של שינוי בהיסטוריה). גם אם השרת רק מושך קוד, כדאי להגדיר אותם, יחד עם כמה הגדרות שחוסכות הפתעות. ההגדרות נשמרות לכל משתמש בנפרד, ולכן מריצים אותן בתור המשתמש שיעבוד עם Git:
git config --global user.name "Deploy Server"
git config --global user.email "deploy@example.co.il"
git config --global init.defaultBranch main
git config --global pull.ff only
git config --global core.editor nano
init.defaultBranch main: מאגרים חדשים ייפתחו עם ענף בשםmain, כמו ב‑GitHub וב‑GitLab.pull.ff only: הפקודהgit pullתסרב למזג היסטוריות שהתפצלו, במקום ליצור מיזוג אוטומטי על השרת החי. בשרת, זו בדיוק ההתנהגות הרצויה.core.editor: העורך שייפתח כש‑Git צריך מכם טקסט.nanoפשוט יותר מ‑vimלמי שלא מכיר אותו.
כדי לראות את כל ההגדרות, ומאיזה קובץ כל אחת נלקחה: git config --list --show-origin.
מפתח SSH ל‑GitHub או ל‑GitLab
כדי למשוך ממאגר פרטי, השרת צריך להזדהות. הדרך המומלצת היא מפתח SSH: זוג קבצים, מפתח פרטי שנשאר בשרת ומפתח ציבורי שמוסרים לשירות. יוצרים מפתח מסוג ed25519, הסוג המודרני והמומלץ, בתור המשתמש שיריץ את Git:
ssh-keygen -t ed25519 -C "deploy@my-server" -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub
הפקודה תציע להגדיר סיסמה למפתח. בשרת שמושך קוד אוטומטית משאירים אותה בדרך כלל ריקה, ולכן חשוב שהמפתח יהיה מוגבל ככל האפשר. את השורה שמודפסת, המפתח הציבורי, מוסיפים כ‑Deploy Key של המאגר:
- ב‑GitHub: בעמוד המאגר, Settings, ואז Deploy keys.
- ב‑GitLab: בעמוד הפרויקט, Settings, Repository, ואז Deploy keys.
Deploy Key עדיף על מפתח שמחובר לחשבון האישי שלכם: הוא נותן גישה למאגר אחד בלבד, ולקריאה בלבד אם לא סימנתם אחרת. אם השרת ייפרץ, התוקף לא יקבל גישה לכל המאגרים שלכם. ב‑GitHub אי אפשר להשתמש באותו Deploy Key ביותר ממאגר אחד, ולכן כשיש בשרת כמה אתרים, יוצרים מפתח לכל אחד ומגדירים לו כינוי בקובץ ~/.ssh/config:
Host github-my-site
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_my_site
IdentitiesOnly yes
ואז משתמשים בכינוי במקום github.com, למשל git@github-my-site:my-org/my-site.git. בודקים את החיבור (עם כינוי: ssh -T git@github-my-site):
ssh -T git@github.com
בחיבור הראשון תתבקשו לאשר את טביעת האצבע של השרת המרוחק. משווים אותה לזו ש‑GitHub או GitLab מפרסמים בתיעוד שלהם, ורק אז מאשרים. GitHub עונה בהודעה שמתחילה ב‑Hi ומציינת שאין גישת shell, וזה בדיוק מה שצריך.
שיבוט המאגר לשרת
cd /var/www
git clone git@github.com:my-org/my-site.git my-site
cd my-site
git log --oneline -5
מריצים את זה בתור המשתמש שהקבצים צריכים להיות שייכים לו, ולא עם sudo. מעבר לבעיות הרשאות, sudo git רץ כ‑root ומחפש את המפתחות של root, ולכן ייכשל גם כשהמפתח שלכם מוגדר נכון.
חשוב גם שקבצים רגישים לא ייכנסו למאגר מלכתחילה. רושמים אותם בקובץ .gitignore שבמאגר, ויוצרים אותם בשרת ידנית, שם הם נשארים בין פריסה לפריסה. באתר וורדפרס, למשל:
wp-config.php
.env
wp-content/uploads/
wp-content/cache/
פריסה פשוטה ובטוחה
דרך 1: משיכה בשרת
הדרך הפשוטה: מתחברים לשרת ומושכים את הגרסה החדשה. כדי שזה יהיה בטוח, משתמשים בתסריט קצר שעוצר בכל שגיאה ולא ממזג שום דבר בשרת:
#!/usr/bin/env bash
set -euo pipefail
cd /var/www/my-site
git fetch --prune origin
git merge --ff-only origin/main
echo "Deployed: $(git log -1 --oneline)"
אם מישהו שינה בשרת קבצים שהגרסה החדשה משנה, או שההיסטוריה התפצלה, התסריט ייעצר עם שגיאה במקום לדרוס או לערבב קוד. אחרי המשיכה מוסיפים את מה שהפרויקט צריך, למשל composer install --no-dev. החיסרון של הדרך הזו: תיקיית .git יושבת בתוך תיקיית האתר, וחייבים לחסום אליה גישה.
דרך 2: מאגר bare עם hook
מאגר bare הוא מאגר Git בלי עותק עבודה, רק ההיסטוריה. דוחפים אליו מהמחשב שלכם כמו לכל מאגר מרוחק, ו‑hook (תסריט ש‑Git מריץ אוטומטית אחרי אירוע) מעתיק את הקבצים לתיקיית האתר. היתרון: תיקיית .git נשארת מחוץ לתיקיית האתר, והפריסה היא git push אחד. בשרת:
mkdir -p ~/repos/my-site.git
cd ~/repos/my-site.git
git init --bare
תיקיית האתר (בדוגמה /var/www/my-site) צריכה להיות קיימת ושייכת למשתמש שדרכו דוחפים. עכשיו יוצרים את הקובץ hooks/post-receive בתוך המאגר:
#!/usr/bin/env bash
set -euo pipefail
TARGET=/var/www/my-site
REPO="$HOME/repos/my-site.git"
BRANCH=main
while read -r oldrev newrev ref; do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
git --work-tree="$TARGET" --git-dir="$REPO" checkout -f "$BRANCH"
echo "Deployed $BRANCH to $TARGET"
fi
done
נותנים לו הרשאת הרצה בשרת:
chmod +x ~/repos/my-site.git/hooks/post-receive
ובמחשב שלכם מוסיפים את השרת כיעד, ודוחפים:
git remote add production deploy@server.example.co.il:repos/my-site.git
git push production main
שימו לב: checkout -f דורס כל שינוי שנעשה ישירות בקבצים של המאגר בתיקיית האתר. זה רצוי, כי השרת לא אמור להיות מקום לעריכה. קבצים שלא נמצאים במאגר, כמו תמונות שהועלו לאתר, לא נפגעים.
הגנה על תיקיית .git
כשמשבטים את המאגר ישירות לתיקייה שממנה האתר מוגש, גם תיקיית .git נמצאת שם. אם השרת מגיש אותה, כל אחד יכול להוריד את הקבצים שבה, ובעזרת כלים זמינים לשחזר את כל קוד המקור וההיסטוריה, כולל סיסמאות שנכתבו פעם בקוד ונמחקו מאז. זו אחת הבדיקות הראשונות שסורקים אוטומטיים מריצים על אתרים.
הפתרון הטוב ביותר הוא שהתיקייה לא תהיה שם בכלל: מאגר bare כמו בדרך 2, או מבנה שבו האתר מוגש מתיקיית משנה של המאגר, למשל public. אם זה לא אפשרי, חוסמים את הגישה. ב‑Apache וב‑LiteSpeed, בקובץ .htaccess:
RewriteEngine On
RewriteRule (^|/)\.git(/|$) - [F,L]
ב‑Nginx, בתחילת בלוק ה‑server:
location ~ /\.git {
deny all;
}
ובודקים מבחוץ:
curl -s -o /dev/null -w "%{http_code}\n" https://example.co.il/.git/config
התשובה צריכה להיות 403 או 404. אם קיבלתם 200, הקוד שלכם חשוף: חוסמים מיד, ומחליפים כל סיסמה או מפתח שהופיעו אי פעם במאגר.
תקלות נפוצות ואיך פותרים אותן
Permission denied (publickey): השירות לא מכיר את המפתח שהשרת מציג. בודקים שאתם מריצים בתור המשתמש הנכון ולא עםsudo, שהמפתח הציבורי נוסף למאגר הנכון, ושההרשאות תקינות (chmod 700 ~/.sshו‑chmod 600למפתח הפרטי). פירוט מלא של החיבור מקבלים עםssh -vT git@github.com.Host key verification failed: החיבור הראשון לשירות לא אושר, למשל כי הוא רץ מתוך תסריט. מתחברים פעם אחת ידנית עםssh -Tומאשרים, אחרי בדיקה של טביעת האצבע.detected dubious ownership in repository: התיקייה שייכת למשתמש אחר מזה שמריץ את Git. הפתרון הנכון הוא להריץ בתור הבעלים, למשלsudo -u deploy git pull. רק אם באמת צריך, מסמנים את התיקייה כבטוחה עםgit config --global --add safe.directory /var/www/my-site.Your local changes to the following files would be overwritten by merge: מישהו ערך קבצים בשרת.git statusו‑git diffמראים מה השתנה. שינוי שצריך להישאר מעבירים למאגר בדרך המסודרת, ושינוי מיותר מבטלים עםgit restore.Not possible to fast-forward, aborting: ההיסטוריה בשרת התפצלה מזו שבמאגר, בדרך כלל בגלל Commit שנעשה בשרת או דחיפה בכוח (Force Push). בודקים מה שונה, ורק אם בטוחים שאין בשרת שום דבר לשמור, מריציםgit reset --hard origin/main.refusing to update checked out branch: דחפתם למאגר רגיל שהענף שלו פתוח בעותק עבודה. בשביל זה בדיוק יש מאגר bare.
בשורה התחתונה
Git בשרת הופך פריסה מהעתקת קבצים ידנית לתהליך שאפשר לחזור עליו, ולחזור ממנו: מתקינים, מגדירים מפתח SSH מוגבל, ופורסים עם משיכה זהירה או עם מאגר bare ו‑hook. שני כללים שאסור לוותר עליהם: לא עורכים קוד ישירות בשרת, ולא משאירים את .git חשופה לדפדפן. כדי להריץ את כל זה צריך שרת עם גישת root, כמו שרת VPS מנוהל בישראל.
שאלות נפוצות
עדיף לפרוס עם Git או עם FTP?
עם Git. רואים בדיוק מה השתנה, הפריסה מעדכנת רק את הקבצים שהשתנו, ואפשר לחזור לגרסה קודמת. FTP לא יודע מה השתנה ולא שומר היסטוריה.
איך חוזרים לגרסה קודמת אחרי פריסה שבורה?
הדרך הנקייה היא git revert לשינוי הבעייתי במחשב שלכם, ואז פריסה מחדש. במצב חירום אפשר להריץ בשרת git reset --hard עם המזהה של הגרסה התקינה, שמוצאים עם git log.
האם לשמור את wp-config.php או קובץ .env ב‑Git?
לא. קבצים עם סיסמאות ומפתחות נשארים רק בשרת, ומוסיפים אותם ל‑.gitignore. כך גם דליפה של המאגר לא חושפת את מסד הנתונים.
למה Git מבקש שם משתמש וסיסמה ולא מקבל אותם?
GitHub כבר לא מקבל את סיסמת החשבון בפעולות Git. מתחברים עם מפתח SSH, או עם טוקן גישה אישי במקום הסיסמה.
איזה משתמש צריך להריץ את Git בשרת?
לא root. משתמשים במשתמש של האתר, או במשתמש פריסה ייעודי עם הרשאות לתיקיית האתר בלבד. כך גם המפתח וגם הקבצים נשארים מוגבלים.